继承多个接口仅承诺实现所有纯虚函数,不提供任何代码复用;而多继承具体实现类易引发二义性与数据冗余,应优先用组合或虚继承解决菱形问题。

继承多个接口:只承诺能力,不拿代码
当你用 class Derived : public InterfaceA, public InterfaceB 这种方式继承多个接口(即含纯虚函数的抽象基类),编译器只检查 Derived 是否实现了所有纯虚函数。它不关心你有没有数据成员、有没有默认构造逻辑、有没有现成的 save() 或 validate() 实现——那些都得你自己写。
常见错误现象:error: cannot declare variable 'x' to be of abstract type 'Derived',说明漏实现某个接口里的纯虚函数;或者误以为继承了 InterfaceA::log() 就能直接调用,结果发现它根本没定义体。
使用场景:需要一个类同时满足“可序列化”+“可校验”+“可撤销”,但每种能力的具体实现逻辑差异很大(比如 JSON 序列化 vs 二进制序列化),这时多个接口比单个大基类更清晰。
- 所有被继承的基类必须是抽象类(至少含一个
=0) - 派生类必须实现全部接口中未实现的纯虚函数,哪怕重复声明也要写实
- 虚析构函数建议每个接口都加,否则
delete通过接口指针释放对象时可能析构不全
继承多个实现:代码复用变复杂,二义性立刻出现
C++ 不允许 class Derived : public BaseA, public BaseB 同时继承两个带非纯虚函数或数据成员的类——不是语法禁止,而是几乎必然触发二义性(ambiguous)或菱形继承问题。即使两个基类没有同名函数,只要它们各自有 std::string name_ 或 init(),Derived 对象就会有两个副本,调用时编译器无法决定选哪个。
典型错误信息:error: request for member 'name_' is ambiguous,或链接时报 undefined reference to 'BaseA::init()'(因为 BaseB 也定义了同名函数但没实现)。
参数差异:public 继承本身不解决冲突;protected/private 继承只是隐藏访问,不消除成员重复存在。
- 真正可行的“多实现复用”路径是组合(
private成员 + 委托调用),不是多继承 - 若真需共享实现,应把公共逻辑抽到独立工具类或策略类中,由各基类按需持有
- 虚继承仅用于解决菱形继承中的重复子对象问题,不能绕过“多实现继承”的设计缺陷
为什么不能混着来:接口和实现混在同一个基类里会模糊意图
一个类既声明纯虚函数又提供具体函数(比如 virtual void draw() = 0; 和 void print_info() { std::cout ),它就既是接口又是实现载体。这本身合法,但一旦被多继承,问题就藏不住了:其他基类也可能有 <code>print_info(),而你没法只继承它的接口部分。
性能影响:虚函数表会变大,每次通过不同接口指针调用同一对象,vptr 查找路径不同,但现代 CPU 分支预测通常能掩盖这点开销。
兼容性影响:如果后续要把某个已存在的基类改造成纯接口(比如去掉所有非虚函数),所有继承它的类都要重新编译,ABI 可能断裂。
- 接口类应严格无数据成员、无非虚函数、无默认构造逻辑
- 实现类应避免被多继承;如需复用,优先用模板、策略模式或依赖注入
- 别指望编译器帮你分辨“这个函数我只想继承接口,那个我想继承实现”——C++ 没这种粒度控制
实际编码时最容易忽略的一点
接口多继承时,如果两个接口都声明了同名但签名不同的纯虚函数(比如 virtual void load(const std::string&) = 0; 和 virtual void load(std::istream&) = 0;),派生类必须显式实现**全部重载版本**,哪怕只打算用其中一个。编译器不会自动合成“未实现的重载”为删除函数,而是直接报错说类仍是抽象的。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











