问题描述
据我所知,创建的 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
确实写了到它现在正在寻找的区域 x
、y
和 z
,所以它会找到它们.问题是它写在它应该写的地方之外。
简短回答:在基类和子类之间进行转换时,如果可以,请使用 dynamic_cast
。如果不允许,它将返回 nullptr
。