C++虚继承中最基类是否需默认构造?VMEbus库编译问题咨询
问题分析与解决方案
这个问题的核心是GCC 4.8.5的C++标准实现缺陷,和你的类设计思路无关。先给你理清楚来龙去脉,再提供符合你美学要求的解决方案:
为什么旧GCC报错,新GCC正常?
按照C++标准,虚基类的构造函数是由最终的最派生类负责调用的——比如你的代码里,V1785N作为最派生类,它的构造函数直接初始化VBoard,中间的虚派生类(VBoardAcquisitor)不需要也不应该负责初始化虚基类,因为它的调用会被最派生类覆盖。
但GCC 4.8.5属于比较老旧的版本,它存在一个bug:即使某个虚派生类是抽象类(包含纯虚函数,无法被直接实例化),它仍然要求这个类的构造函数能初始化虚基类,否则就抛出编译错误。而GCC 7.5.0已经修复了这个bug,正确遵循了C++标准,所以你的原始代码在新GCC上能正常编译。
符合你美学要求的解决方案
除了你提到的两个临时办法,还有更优雅的方案,不需要用“任意默认地址”或者给VBoard加默认构造:
方案1:给VBoardAcquisitor添加带参构造函数
修改VBoardAcquisitor的构造函数,让它接受地址参数并传递给VBoard的构造。虽然这个调用会被最派生类的构造覆盖,但它满足了旧GCC的编译要求,而且传递的是实际的模块地址,不是随便填的无效值:
#include <iostream> class VBoard { int address; public : VBoard( int address ) : address( address ) { } virtual ~VBoard() { }; virtual void Init() = 0; }; class VBoardAcquisitor : virtual public VBoard { int *buffer; public : // 修改为带参构造,传递地址给VBoard VBoardAcquisitor(int address) : VBoard(address), buffer( nullptr ) { } virtual ~VBoardAcquisitor() { }; virtual void AllocBuff() = 0; }; class V1785N : public VBoardAcquisitor { public : // 同时初始化VBoard和VBoardAcquisitor,传递同一个有效地址 V1785N( int address ) : VBoard( address ), VBoardAcquisitor(address) { } ~V1785N() { } void Init() override { std::cout << "Init\n"; } void AllocBuff() override { std::cout << "AllocBuff\n"; } }; int main() { V1785N adc( 0x40000000 ); return 0; }
方案2:将VBoardAcquisitor的构造设为protected(更优)
如果你不想让VBoardAcquisitor的构造函数对外暴露(毕竟它是抽象类,不应该被外部实例化),可以把它的构造函数设为protected,这样只有派生类能调用它,更符合封装性:
class VBoardAcquisitor : virtual public VBoard { int *buffer; protected : // 保护构造函数,仅派生类可访问 VBoardAcquisitor(int address) : VBoard(address), buffer( nullptr ) { } public : virtual ~VBoardAcquisitor() { }; virtual void AllocBuff() = 0; };
这两个方案的优势:
- 没有使用无意义的“默认地址”,传递的都是实际的模块地址,符合你的美学要求;
- 满足旧GCC的编译规则;
- 完全符合C++标准的设计逻辑,虚基类的最终控制权仍然在最派生类手中。
内容的提问来源于stack exchange,提问作者LRDPRDX
相关产品推荐
相关产品推荐

