当前位置: 首页 > news >正文

C++虚基类详解

多继承(Multiple Inheritance)

是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。尽管概念上非常简单,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名冲突就是不可回避的一个。

注意: 多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承。

A
B
C
D

基类 A 派生出类 B 和类 C,子类 D 继承自类 B 和类 C。
这个时候类 A 中的成员变量和成员函数继承到类 D 中变成了两份,一份来自 A–>B–>D 这条路径,另一份来自 A–>C–>D 这条路径。

这样的问题在于,在D类中使用或者访问基类A中的成员函数、成员变量时会产生歧义。编译器不知道它是来着A–>B–>D,还是来自A–>C–>D这条路径。

//间接基类A
class A{
protected:int m_a;
};//直接基类B
class B: public A{
protected:int m_b;
};//直接基类C
class C: public A{
protected:int m_c;
};//派生类D
class D: public B, public C{
public:void seta(int a){ m_a = a; }  //命名冲突void setb(int b){ m_b = b; }  //正确void setc(int c){ m_c = c; }  //正确void setd(int d){ m_d = d; }  //正确
private:int m_d;
};int main(){D d;return 0;
}

这段代码实现了上图所示的菱形继承,D类中,成员函数代码试图直接访问成员变量 m_a,结果发生了错误,因为类 B 和类 C 中都有成员变量 m_a(从 A 类继承而来),编译器不知道选用哪一个,所以产生了歧义。

为了消除歧义,可以在访问的时候给他标明是来自哪一个类的,比如以下代码

void seta(int a){ C::m_a = a; }  //表明这个成员函数是通过 A-->C-->D 这条路径//或者这样
void seta(int a){ B::m_a = a; }  //表明这个成员函数是通过 A-->B-->D 这条路径

虚继承(Virtual Inheritance)

为了解决多继承时的命名冲突和冗余数据问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员。
在继承方式前面加上 virtual 关键字就是虚继承,比如以下代码中展示的

//间接基类A
class A{
protected:int m_a;
};//直接基类B
class B: virtual public A{  //虚继承
protected:int m_b;
};//直接基类C
class C: virtual public A{  //虚继承
protected:int m_c;
};//派生类D
class D: public B, public C{
public:void seta(int a){ m_a = a; }  //正确void setb(int b){ m_b = b; }  //正确void setc(int c){ m_c = c; }  //正确void setd(int d){ m_d = d; }  //正确
private:int m_d;
};int main(){D d;return 0;
}

这段代码使用虚继承重新实现了上图所示的菱形继承,这样在派生类 D 中就只保留了一份成员变量 m_a,直接访问就不会再有歧义了。

虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),本例中的 A 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。

虚继承
虚继承
A
B
C
D

必须在虚派生的真实需求出现前就已经完成虚派生的操作
虚派生只影响从指定了虚基类的派生类中进一步派生出来的类,它不会影响派生类本身
理由: 在上图中,当定义 D 类时才出现了对虚派生的需求,但是如果 B 类和 C 类不是从 A 类虚派生得到的,那么 D 类还是会保留 A 类的两份成员。

建议:能用单一继承解决的问题就不要使用多继承

http://www.lryc.cn/news/230157.html

相关文章:

  • Mac M2/M3 芯片环境配置以及常用软件安装-前端
  • Karmada更高效地实现故障转移
  • 前端AJAX入门到实战,学习前端框架前必会的(ajax+node.js+webpack+git)(四)
  • ​TechSmith Camtasia 2024破解版功能介绍及使用教程
  • 【无线网络技术】——无线传输技术基础(学习笔记)
  • 【Liunx】部署WEB服务:Apache
  • 数字媒体技术基础之:常见图片文件格式
  • 2023-2024-2 高级语言程序设计-二维数组
  • 【uniapp】确认弹出框,选择确定和取消
  • 阿里云容器镜像服务的运维总结
  • 修炼k8s+flink+hdfs+dlink(七:flinkcdc)
  • 排查问题流程
  • 【nlp】2.2 传统RNN模型
  • C/C++---------------LeetCode第49.字母异位词分组
  • spark调优案例分享
  • 阿里达摩院开源DAMO-YOLO
  • 【异常检测小集】
  • Mybatis-Plus的IPage和Page
  • jupyter lab常用插件集合
  • centos 6.10 安装 boost 1.78.0
  • Vue 3.0 + vite + axios+PHP跨域问题的解决办法
  • 软件外包开发的开发文档
  • 如何清理C盘文件
  • 从测试的角度看待南航机票bug事件
  • 通过 dump 虚拟机线程方法栈和堆内存来分析 Android 卡顿和 OOM 问题
  • layui 框架的upload上传文件的data参数传到后端的方法
  • Java虚拟机的垃圾回收机制
  • 时间序列基础->数据标签、数据分割器、数据加载器的定义和讲解(零基础入门时间序列)
  • 【图论】最小生成树(python和cpp)
  • 【亚马逊云科技】使用Amazon Lightsail快速建站