基类型未转换为派生类型但可以调用其函数和变量
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
已经写 到它现在正在寻找的区域 x
、y
和 z
从现在开始,所以它找到了它们。问题是它写在了它应该写的地方之外。
简答:如果可以,在基础和子 class 之间转换时使用 dynamic_cast
。如果不允许,它将 return nullptr
。
据我所知,创建的 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
已经写 到它现在正在寻找的区域 x
、y
和 z
从现在开始,所以它找到了它们。问题是它写在了它应该写的地方之外。
简答:如果可以,在基础和子 class 之间转换时使用 dynamic_cast
。如果不允许,它将 return nullptr
。