微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

基类型未转换为派生类型但可以调用其函数和变量

如何解决基类型未转换为派生类型但可以调用其函数和变量

据我所知,创建的 base 类型不能在 OOP 上转换为 衍生 类型。但我遇到过这样的事情,我没想到会工作。这是示例类:

class Base {
public: 
    virtual void Call1() { std::cout << "Base call 1" << std::endl; }
    virtual void Call2() { std::cout << "Base call 2" << std::endl; }
};

class Derived : public Base{
public:
    void Call1() { std::cout << "Derived call 1" << std::endl; }
    void Call2() { std::cout << "Derived call 2" << std::endl; }

    void SetAll() { x = 15; y = 16; z = 16; } // Just random numbers
    
    int GetX() { return x; }
    int GetY() { return y; }       
    int GetZ() { return z; }
private:
    int x;
    int y;
    int z;
};

在主程序中:

Base* b1 = new Base;
Derived* d1 = (Derived*)(b1); // This shouldn't be okay as far as i kNow

// How is this part working?
d1->SetAll();
std::cout << d1->GetX() << std::endl;
std::cout << d1->GetY() << std::endl;
std::cout << d1->GetZ() << std::endl;

输出如下:

15
16
16

即使派生类型的大小更大,由于某种原因,即使我没有分配足够的内存,我也可以调用它的函数并操作它的派生类型的变量 strong> 因为 Base 对象的大小较小。那么这段代码是如何工作的?

解决方法

这里发生的事情大部分都在评论中,但有一点我还没有看到:SetAll() 方法如何设置内存,以及在哪里设置它.作为记录,我在下面描述的是可能正在发生的事情,因为您在这里非常喜欢“未定义的行为”。

Derived* d1 = (Derived*)(b1); // This shouldn't be okay as far as i know

正如你所说,这不应该,也不行d1 认为它指向一个 Derived 类,但实际上不是。但一切都会表现。正如其他人所说,您实际上已经在指针上执行了 reinterpret_cast

d1->SetAll();

这里发生的是 SetAll() 方法将设置 内存位置,如果 d1 指向 Derived 的 em>实际 实例。所以你覆盖了你没有分配的内存,但它经常仍然存在。因此,如果 b1 是 0x00004000(或其他),那么 d1->x 是 0x0004004,或 4 个字节(或 8 个,或编译器依赖于它如何布置基类和派生类)超出“开始” Base 类的。如果 d1 实际上指向一个 Derived 就好了,但它指向一个 Base 这意味着它不是指向它“拥有”的内存区域。因此未定义的行为。

std::cout << d1->GetX() << std::endl;
std::cout << d1->GetY() << std::endl;
std::cout << d1->GetZ() << std::endl;

好吧,d1 确实写了到它现在正在寻找的区域 xyz,所以它会找到它们.问题是它写在它应该写的地方之外。

简短回答:在基类和子类之间进行转换时,如果可以,请使用 dynamic_cast。如果不允许,它将返回 nullptr

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。