Bootstrap

C++ 二义性

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

关键字实现共享基类实例

初始化列表

:组合对象若无默认构造函数,必须通过初始化列表初始化

  1. 多态限制:无虚函数时,基类指针/引用只能访问基类成员(示例中 name 字段未体现多态)

代码运行结果分析

二义性解决后输出

from mother method
from father method
100
0

赋值兼容性输出

test    // p1.name (基类对象)
kkk     // pp.name (派生类对象赋值给基类引用)
kkk     // pt->name (派生类对象地址赋给基类指针)

相关实践建议可参考:[组合优于继承设计原则]

,[多继承二义性解决方案]

;