为了回答当你运行这段代码时会发生什么/为什么,我通过编译它
g++ -ggdb main。Cc,并逐步使用gdb。
main.cc:
class A {
public:
A() {
fn();
}
virtual void fn() { _n=1; }
int getn() { return _n; }
protected:
int _n;
};
class B: public A {
public:
B() {
// fn();
}
void fn() override {
_n = 2;
}
};
int main() {
B b;
}
在main处设置断点,然后进入B(),打印this ptr,进入a()(基构造函数):
(gdb) step
B::B (this=0x7fffffffde80) at main2.cc:16
16 B() {
(gdb) p this
$27 = (B * const) 0x7fffffffde80
(gdb) p *this
$28 = {<A> = {_vptr.A = 0x7fffffffdf80, _n = 0}, <No data fields>}
(gdb) s
A::A (this=0x7fffffffde80) at main2.cc:3
3 A() {
(gdb) p this
$29 = (A * const) 0x7fffffffde80
显示它最初指向派生的B对象B,该对象B被构造在0x7fffffffde80的堆栈上。下一步是进入以A()为基数的ctor,这变成了A * const到相同的地址,这是有意义的,因为以A为基数的对象正好在B对象的开头。但它仍然没有被构建:
(gdb) p *this
$30 = {_vptr.A = 0x7fffffffdf80, _n = 0}
还有一步:
(gdb) s
4 fn();
(gdb) p *this
$31 = {_vptr.A = 0x402038 <vtable for A+16>, _n = 0}
_n已经初始化,它的虚函数表指针包含虚void A::fn()的地址:
(gdb) p fn
$32 = {void (A * const)} 0x40114a <A::fn()>
(gdb) x/1a 0x402038
0x402038 <_ZTV1A+16>: 0x40114a <_ZN1A2fnEv>
因此,下一步通过this->fn()执行A::fn()是完全有意义的,前提是活动this和_vptr.A。再一步,我们回到B() ctor:
(gdb) s
B::B (this=0x7fffffffde80) at main2.cc:18
18 }
(gdb) p this
$34 = (B * const) 0x7fffffffde80
(gdb) p *this
$35 = {<A> = {_vptr.A = 0x402020 <vtable for B+16>, _n = 1}, <No data fields>}
碱基A已经构造好了。注意,存储在虚函数表指针中的地址已经更改为派生类B的虚表,因此调用fn()将通过this->fn()选择派生类重写B::fn(),给定活动this和_vptr。A(在B()中调用B::fn()来查看这个。)再次检查存储在_vptr中的1个地址。A显示它现在指向派生类重写:
(gdb) p fn
$36 = {void (B * const)} 0x401188 <B::fn()>
(gdb) x/1a 0x402020
0x402020 <_ZTV1B+16>: 0x401188 <_ZN1B2fnEv>
By looking at this example, and by looking at one with a 3 level inheritance, it appears that as the compiler descends to construct the base sub-objects, the type of this* and the corresponding address in _vptr.A change to reflect the current sub-object being constructed, - so it gets left pointing to the most derived type's. So we would expect virtual functions called from within ctors to choose the function for that level, i.e., same result as if they were non-virtual.. Likewise for dtors but in reverse. And this becomes a ptr to member while members are being constructed so they also properly call any virtual functions that are defined for them.