MST

星途 面试题库

面试题:C++虚函数继承中的多态与内存布局

已知有类继承体系,`class A { virtual void f() { cout << "A::f" << endl; } }; class B : public A { virtual void f() { cout << "B::f" << endl; } }; class C : public B { virtual void f() { cout << "C::f" << endl; } };`。请分析在创建`C`类对象时,其内存布局情况,特别是虚函数表的结构,以及在不同类型指针(`A*`、`B*`、`C*`)指向`C`类对象时,调用`f`函数的过程和原理。
25.9万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

1. C类对象内存布局及虚函数表结构

  1. 内存布局
    • 当创建C类对象时,由于C继承自BB继承自AC对象的内存布局会包含A类子对象、B类子对象和C类自身的数据成员(如果有)。
    • 首先是A类部分的数据成员,接着是B类部分的数据成员(如果有新增的),最后是C类自身的数据成员(如果有新增的)。
    • 对于虚函数相关,在对象内存布局的开头会有一个虚指针(vptr),它指向虚函数表(vtable)。
  2. 虚函数表结构
    • 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函数的过程和原理

  1. A*指针指向C类对象
    • A* ptr = new C();时,ptr虽然是A*类型,但实际指向C类对象。
    • 调用ptr->f();时,首先通过ptr找到C类对象内存布局开头的虚指针,该虚指针指向C类的虚函数表。
    • 在虚函数表中找到f函数的入口地址(此时是C::f的地址),然后调用C::f函数。这就是多态的实现原理,通过虚函数表和虚指针,根据对象的实际类型(而不是指针类型)来调用合适的虚函数。
  2. B*指针指向C类对象
    • B* ptr = new C();时,同样ptr指向C类对象。
    • 调用ptr->f();时,过程与A*指针类似。通过ptr找到C类对象内存布局开头的虚指针,虚指针指向C类的虚函数表,在虚函数表中找到f函数的入口地址(C::f的地址),进而调用C::f函数。虽然ptrB*类型,但由于多态机制,依然会调用到C类实际重写的f函数。
  3. C*指针指向C类对象
    • C* ptr = new C();时,调用ptr->f();
    • 还是通过ptr找到C类对象内存布局开头的虚指针,虚指针指向C类的虚函数表,在虚函数表中找到f函数的入口地址(C::f的地址),从而调用C::f函数。在这种情况下,指针类型和对象实际类型一致,多态机制同样保证了正确调用到C类重写的f函数。

综上所述,不同类型指针指向C类对象时,通过虚指针和虚函数表机制,都能正确调用到C类重写的f函数,实现了多态行为。