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

Base type is not converting to derived type yet can call its functions and variables

据我所知,创建的 base 类型无法在 OOP[= 上转换为 derived 类型27=]。但是我遇到过这样的事情,我没想到会工作。这是示例 类:

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

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

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

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

正如您所说,这不应该,也不行d1 认为 它指向 Derived class,但实际上不是。但一切都会 表现得 就像它本来的样子。正如其他人所说,您实际上已经在指针上完成了 reinterpret_cast

d1->SetAll();

这里发生的是 SetAll() 方法将设置 内存位置 其中 x、y 和 z 将是 如果 d1 指向 Derived 实际 实例。所以你正在覆盖你没有分配的内存,但它经常仍然存在。因此,如果 b1 是 0x00004000(或其他),则 d1->x 是 0x0004004,或 4 个字节(或 8 个字节,或取决于编译器如何放置基础和派生的 classes) Base class 的“开始”之外。如果 d1 实际上指向 Derived 就没问题,但它指向 Base 这意味着它没有指向它“拥有”的内存区域。因此未定义的行为。

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

好吧 d1 已经写 到它现在正在寻找的区域 xyz 从现在开始,所以它找到了它们。问题是它写在了它应该写的地方之外。

简答:如果可以,在基础和子 class 之间转换时使用 dynamic_cast。如果不允许,它将 return nullptr