多继承场景下为何不为每个类仅分配一个虚指针?
为什么多继承的派生类需要多个虚指针?
先看你给出的C++代码:
#include <iostream> class A{ public: void* ptr; virtual void printNonOverride() { std::cout << "no override" << std::endl; } virtual void print() { std::cout << "hello world!" << std::endl; } }; class C { public: double c; virtual void cFunc() { std::cout << "C"; } }; class B : public A , public C { }; int main() { A a{}; std::cout << sizeof a << std::endl; C c{}; std::cout << sizeof c << std::endl; B b{}; std::cout << sizeof b << std::endl; }
运行输出:
16 16 32
核心原因在于多继承下基类子对象的独立性和类型转换的正确性:
基类内存布局的兼容性要求
当你把B对象转换成C*时,指针会自动调整到B对象中C子对象的起始位置。如果C子对象没有自己的虚指针,通过这个C*调用cFunc()时,会错误地使用A子对象的虚指针——但A的虚表里根本没有cFunc的条目,这会直接导致函数调用错误甚至程序崩溃。编译器必须保证每个带虚函数的基类子对象,都能像独立的基类对象一样正常工作,保留自身的虚指针是最可靠的实现方式。虚函数调用的索引规则
每个类的虚表条目顺序是固定的:比如A的虚表顺序是printNonOverride、print,C的虚表顺序是cFunc。如果强行合并成单个虚表,无法同时满足A*和C*调用虚函数的索引正确性——两种基类指针期望的虚表条目位置完全不同,合并后必然导致调用混乱。正确性优先于内存优化
虽然多一个虚指针会增加内存占用,但相比程序的正确性、基类兼容性以及编译器实现的复杂度,这一点点开销完全可以接受。编译器的首要目标是保证C++语义的正确实现,而非极致的内存压缩。
内容的提问来源于stack exchange,提问作者Wang Hao
相关产品推荐
相关产品推荐

