1. 多继承的二义性
当派生类通过多继承拥有多个父类时,若父类存在同名成员函数或变量,直接调用会产生二义性。
示例代码:
cpp
son s1;
s1.test(); // 编译错误:无法确定调用 mother::test() 还是 father::test()
解决方法:
作用域解析运算符
:明确指定父类作用域
cpp
s1.mother::test(); // 调用 mother 的方法 [1,5](@ref)
s1.father::test(); // 调用 father 的方法
派生类重写方法
(代码中被注释部分):通过覆盖父类方法消除歧义
2. 成员隐藏规则
当派生类与父类存在同名成员变量时,优先访问派生类成员。若需访问父类成员,需显式指定作用域:
cpp
cout << s1.sid << endl; // 输出派生类 sid (100)
cout << s1.father::sid << endl; // 输出父类 sid (0) [1](@ref)
二、继承的赋值兼容性
1. 对象赋值规则
派生类对象可赋值给基类对象
(向上转型):
cpp
per p1;
p1 = s1; // 合法(发生对象切片,仅保留基类部分)
基类对象不可赋值给派生类对象
:
cpp
// s1 = p1; // 非法
2. 指针与引用的多态性
基类指针可指向派生类对象
:
cpp
per* pt = &s1; // 合法
基类引用可绑定派生类对象
:
cpp
per& pp = s1; // 合法
注意:若无虚函数,通过基类指针/引用只能访问基类成员(如示例中的 name
字段)
。
三、组合与继承的选择
1. 组合的实现
示例:stu
类通过组合 date
类实现功能复用:
cpp
class stu {
date bir; // 组合关系
stu(...) : bir(y, m) {} // 通过初始化列表构造组合对象 [8](@ref)
};
优势:
-
符合
has-a
关系,降低类间耦合
-
避免继承层次过深(建议不超过3层)
2. 组合 vs 继承
继承 (is-a) | 组合 (has-a) | |
---|---|---|
耦合度 | 高(暴露父类实现细节) | 低(仅依赖接口)6****7 |
灵活性 | 静态(编译时确定) | 动态(运行时可替换对象) |
复用方式 | 白箱复用(可见内部细节) | 黑箱复用(隐藏实现)8 |
设计原则:优先使用组合,仅在需要多态或严格 is-a 关系时使用继承
。
四、关键知识点总结
虚继承
:解决菱形继承问题(如多个父类继承同一基类),通过
virtual
关键字实现共享基类实例
初始化列表
:组合对象若无默认构造函数,必须通过初始化列表初始化
- 多态限制:无虚函数时,基类指针/引用只能访问基类成员(示例中
name
字段未体现多态)
代码运行结果分析
二义性解决后输出
:
from mother method
from father method
100
0
赋值兼容性输出
:
test // p1.name (基类对象)
kkk // pp.name (派生类对象赋值给基类引用)
kkk // pt->name (派生类对象地址赋给基类指针)
相关实践建议可参考:[组合优于继承设计原则]
,[多继承二义性解决方案]