了解从多个类派生时的虚函数



我已经开始了解C++中虚函数的工作,并遇到了以下代码。以下是我对虚函数的理解:

  1. 每个定义了虚函数的类都会为其创建一个vtable
  2. 创建类的
  3. 实例时,将创建指向该类的vtablevptr

根据我的理解,我正在尝试分析以下代码的输出,我无法破译代码如何打印"C12g"。

class I1 {
public: 
virtual void f(){cout << "I1" << endl;}
};
class I2 {
public: 
virtual void g(){cout << "I2" << endl;}
};
class C12 : public I1, public I2 {
public:
virtual void f(){cout << "C12f" << endl;}
virtual void g(){cout << "C12g" << endl;}
};
int main(int argc, char *argv[]) {
I2 *o = new C12();
((I1*)o)->f();
}

我认为,由于C12对象被分配给类型I2,对象o只能访问其方法g()C12(因为 g 被覆盖(。现在,由于o被类型转换为I1,我认为f()C12会被调用。

实际输出: C12g

我想知道以下事情:

  1. C12 的内存布局以及 I1、I2 指向的内容
  2. C12g 如何打印为输出。
  3. 当一个对象在两个不相关的接口之间进行类型转换时会发生什么?

在这里,您必须首先了解创建的实际对象*o属于C12类型 - 因为这就是您用new C12()构造的对象。

接下来,对于虚函数,将调用实际对象的成员,无论您将指针投射到什么"类型"。因此,当您在I2 *o = new C12()中投射到I2指针时,例如,如果您调用 o->g((,则对底层对象无关紧要,因为对象将"知道"调用其重写的函数。

但是,当您将指针投射到"不相关"I1*时,您会陷入奇怪的境地。请记住,类I1I2本质上具有相同的内存布局,那么在一个类中调用f()将定向到与在另一个中调用g()相同的"偏移量"。但是,由于o实际上是指向I2的指针,因此调用最终得到的对量表条目是 I2 中g的偏移量 - 它被C12覆盖。

同样值得注意的是,您已经使用了 C 样式的转换从I2*I1*(但您也可以使用reinterpret_cast(。这很重要,因为这两个都对指针或指向的对象/内存完全没有作用

可能听起来有点乱码,但我希望它能提供一些见解!

这是一个可能的内存布局/场景 - 但它将是特定于实现的,并且在 C 样式强制转换后使用类指针很可能构成未定义的行为!

可能的内存映射(简化,假设所有组件为 4 字节(:

class I1:
0x0000: (non-virtual data for class I1)
0x0004: v-table entry for function "f"
class I2:
0x0000: (non-virtual data for class I2)
0x0004: v-table entry for function "g"
class C12:
0x0000: (non-virtual data for class I1)
0x0004: v-table entry for function "f"
0x0008: (non-virtual data for class I2)
0x000C: v-table entry for function "g"
0x0010: (class-specific stuff for C12)

现在,当您在I2 *o = new C12();中从C12*转换为I2*时,编译器会理解两个类之间的关系,因此o将指向 C12 中的0x0008偏移量(派生类已被正确"切片"(。但是I2*I1*的 C 样式转换不会改变任何东西,所以编译器"认为"它指向一个I1但它仍然指向一个实际的I2C12切片 - 这"看起来"就像一个真正的I1类。

家庭 作业

您可能会发现有趣的(并且可能与我描述的内存布局一致,也可能不同意(是在main()末尾添加以下代码:

C12* properC12 = new C12();// Points to the 'origin' of the class
I1* properI1 = properC12; // Should (?) have same value as above?
I2* properI2 = properC12; // Should (?) have an offset to 'slice'
I1* dodgyI1 = (I1*)properC12; // Will (?) have same value as properI2!
cout << std::hex << properC12 << endl;
cout << std::hex << properI1 << endl;
cout << std::hex << properI2 << endl;
cout << std::hex << dodgyI1 << endl;

请 - 任何尝试的人 - 让我们知道值是什么,以及您正在使用的平台/编译器。 在Visual Studio 2019中,针对x64平台进行编译,我得到以下指针值:

000002688A9726E0
000002688A9726E0
000002688A9726E8
000002688A9726E0

。这(有点(与我描述的内存布局一致(除了在其他地方放置v表,而不是"块内"(。

相关内容

  • 没有找到相关文章

最新更新