我已经开始理解C++
中的虚函数的工作,并且遇到了以下代码。这是我对虚函数的理解:
vptr
,它指向该类的vtable。基于我的理解,我正在尝试分析以下代码的输出,而我无法解释代码如何显示“ 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
我想了解以下内容:
您首先必须了解的是创建的实际对象,*o
的类型为C12
-因为这是您使用new C12()
构造的。
接下来,使用虚函数,然后将调用实际对象的成员,无论您将指针转换为哪种“类型”。因此,当您在I2
中强制转换为I2 *o = new C12()
指针时,例如,如果您随后调用o-> g(),则对基础对象无关紧要,因为该对象会“知道”调用其覆盖的功能。
但是,当您将指针投射到“无关” I1*
时,您会陷入奇怪的境地。请记住,类I1
和I2
本质上具有相同的内存布局,然后在其中一个中调用f()
将指向与在另一个中调用g()
相同的“偏移”。但是,由于o
实际上是指向I2
的指针,因此调用最终到达的v表条目是I2中g
的偏移量-被C12
覆盖。
同样值得注意的是,您使用了C样式的强制转换来将I2*
转换为I1*
(但您也可以使用reinterpret_cast
)。这很重要,因为这两个绝对不做任何事情
可能听起来有点乱,但我希望它能提供一些见识!
这里是一个可能
内存布局/场景-但这将是特定于实现的,并且在C样式强制转换之后使用类指针很可能构成未定义的行为!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)
现在,当您在C12*
中进行从I2*
到I2 *o = new C12();
的转换时,编译器理解了这两个类之间的关系,因此o
将指向C12中的0x0008
偏移量(派生类已正确“切片”)。 但是从I2*
转换为I1*
的C样式没有任何改变,因此编译器“认为”它指向I1
,但仍指向实际的I2
] C12
的一部分-这看起来像真正的I1
类。