面试题答案
一键面试1. C
类对象内存布局及虚函数表结构
- 内存布局:
- 当创建
C
类对象时,由于C
继承自B
,B
继承自A
,C
对象的内存布局会包含A
类子对象、B
类子对象和C
类自身的数据成员(如果有)。 - 首先是
A
类部分的数据成员,接着是B
类部分的数据成员(如果有新增的),最后是C
类自身的数据成员(如果有新增的)。 - 对于虚函数相关,在对象内存布局的开头会有一个虚指针(vptr),它指向虚函数表(vtable)。
- 当创建
- 虚函数表结构:
A
类的虚函数表中,f
函数的入口地址是A::f
的地址。B
类继承A
类,重写了f
函数,B
类的虚函数表中,f
函数的入口地址被替换为B::f
的地址。C
类继承B
类,重写了f
函数,C
类的虚函数表中,f
函数的入口地址被替换为C::f
的地址。C
类对象中的虚指针指向的虚函数表就是这种结构,即虚函数表中f
函数的入口是C::f
的地址。
2. 不同类型指针指向C
类对象时调用f
函数的过程和原理
A*
指针指向C
类对象:- 当
A* ptr = new C();
时,ptr
虽然是A*
类型,但实际指向C
类对象。 - 调用
ptr->f();
时,首先通过ptr
找到C
类对象内存布局开头的虚指针,该虚指针指向C
类的虚函数表。 - 在虚函数表中找到
f
函数的入口地址(此时是C::f
的地址),然后调用C::f
函数。这就是多态的实现原理,通过虚函数表和虚指针,根据对象的实际类型(而不是指针类型)来调用合适的虚函数。
- 当
B*
指针指向C
类对象:- 当
B* ptr = new C();
时,同样ptr
指向C
类对象。 - 调用
ptr->f();
时,过程与A*
指针类似。通过ptr
找到C
类对象内存布局开头的虚指针,虚指针指向C
类的虚函数表,在虚函数表中找到f
函数的入口地址(C::f
的地址),进而调用C::f
函数。虽然ptr
是B*
类型,但由于多态机制,依然会调用到C
类实际重写的f
函数。
- 当
C*
指针指向C
类对象:- 当
C* ptr = new C();
时,调用ptr->f();
。 - 还是通过
ptr
找到C
类对象内存布局开头的虚指针,虚指针指向C
类的虚函数表,在虚函数表中找到f
函数的入口地址(C::f
的地址),从而调用C::f
函数。在这种情况下,指针类型和对象实际类型一致,多态机制同样保证了正确调用到C
类重写的f
函数。
- 当
综上所述,不同类型指针指向C
类对象时,通过虚指针和虚函数表机制,都能正确调用到C
类重写的f
函数,实现了多态行为。