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

什么时候需要使用虚继承,什么是菱形继承

在阅读ROS的源码的时候,遇到了这样的一个地方:

/** \brief %Controller with a specific hardware interface** \tparam T The hardware interface type used by this controller. This enforces* semantic compatibility between the controller and the hardware it's meant to* control.*/
template <class T>
class Controller: public virtual ControllerBase
{
public:

这个里面继承类有一个virtual关键字,这个virtual关键字的作用是什么呢,什么时候会需要这样的一个virtual关键字呢,本篇文章将回答这些问题:


背景

在 ROS 控制框架(尤其是 ros_control)中,控制器类通常会继承多个基类,这些基类可能最终都继承自同一个公共基类(例如 ControllerBase)。这就会遇到菱形继承(diamond inheritance) 的问题。

普通继承下的问题

假设有以下继承关系:

ControllerBase^         ^|         |
ControllerA  ControllerB\     /\   /MyController

如果 ControllerAControllerB 都是普通继承 ControllerBase

class ControllerA : public ControllerBase { ... };
class ControllerB : public ControllerBase { ... };class MyController : public ControllerA, public ControllerB { ... };

那么 MyController 内部会有两份 ControllerBase 子对象,这会带来:

  • 二义性访问:MyController 中访问 ControllerBase 成员时编译器会报错(不知道选哪一份)。
  • 多份数据冗余:ControllerBase 的状态被复制两份,很可能导致逻辑混乱。

虚拟继承(virtual)的作用

虚拟继承的目的是在多重继承场景下,让所有派生类共享一份公共基类实例,避免菱形继承中的重复子对象。

如果这样改:

class ControllerA : public virtual ControllerBase { ... };
class ControllerB : public virtual ControllerBase { ... };class MyController : public ControllerA, public ControllerB { ... };

那么不管你通过 ControllerA 还是通过 ControllerB 去访问 ControllerBase,都会访问到同一份实例,不会重复创建。


回到你的例子

ros_control 框架中,Controller 模板类是所有具体控制器的一个中间基类,它通常可能和其他基类(比如插件接口、硬件接口适配器等)一起被多重继承,并且这些基类的共同祖先是 ControllerBase

如果 Controller 不使用 virtual 继承 ControllerBase,一旦你有另外一个基类也继承了 ControllerBase,那么最终你的实际控制器类就会陷入菱形继承问题。

因此,设计者直接在 Controller 这里用 public virtual ControllerBase预防将来在多重继承中出现菱形继承的问题


总结

virtual 在这里的作用是:

  1. 解决/预防菱形继承 导致的重复基类子对象问题。
  2. 保证唯一性:所有继承链里只保留一份 ControllerBase 成员。
  3. 提高兼容性与健壮性:即使未来添加新的中间类,也不会造成二义性访问。

一句话总结
因为 ros_control 框架的控制器体系存在多重继承的可能性,Controller 类用 virtual 继承 ControllerBase 是为了让最终派生类只保留一份 ControllerBase 实例,避免菱形继承带来的二义性与数据重复。


那为什么不所有的继承都使用虚拟继承呢?

这个问题很关键,很多人第一次学 virtual inheritance 的时候也会想——既然解决了菱形继承的问题,那干脆所有继承都写成虚拟继承是不是更安全?
答案是:不应该所有继承都用虚拟继承,原因主要有四点。


1. 虚拟继承有额外开销(复杂度 + 性能)

  • 普通继承时,基类子对象位置在编译期固定在派生类对象的内存布局中,访问基类成员只需要固定偏移量
  • 虚拟继承为了支持“唯一基类”,编译器会引入 虚基表(vbase table) 来在运行时计算基类子对象真实位置。
  • 结果:
    • 内存布局更复杂,访问虚基类成员速度可能略慢(需要多一次指针间接跳转)。
    • 对象大小一般会增加(多一个指向虚基表的指针)。

2. 虚拟继承降低可读性和直观性

  • 普通继承:构造函数按照继承链自动初始化基类对象。
  • 虚拟继承:
    • 最终派生类 必须负责虚基类的初始化,即使中间基类也定义了初始化逻辑,这会让初始化代码更分散。
    • 容易让新手迷惑“这个基类的构造为什么不能在中间类里调用生效?”

例如:

struct A {A(int x) { ... }
};
struct B : virtual A {B() : A(1) {} // 这里并不会真正构造 A
};
struct C : virtual A {};
struct D : B, C {D() : A(42) {} // 最终派生类必须管理 A 的构造
};

B 中初始化 A(1) 是无效的,因为虚基类初始化由 D 决定。


3. 虚拟继承不必要时反而制造麻烦

  • 如果类层次很简单,没有菱形继承或公共基类重复问题,虚拟继承不会带来好处,反而增加了内存间接访问、初始化复杂度。
  • 而且虚拟继承使得类设计绑定得更紧,对编译器和链接器都增加额外工作量。

4. 虚拟继承是为了特殊场景而设计的

  • 它的主要用途就是解决多重继承中公共基类的重复,比如:
    • 接口类(多重继承常见)
    • 框架基类(如 ControllerBase
  • 在常规一条继承链中(单继承),虚拟继承是完全没有意义的。

在 ROS ros_control 里的设计哲学

  • ControllerBase 是公共框架基类,有可能在多重继承中被重复继承 → 用虚拟继承。
  • 其他不需要多重继承的类就用普通继承,保持简单、高效。
  • 这样只在必要的地方引入虚拟继承,避免全局复杂化。

一句话总结
虚拟继承是为了解决菱形继承的特殊工具,不是通用继承方式。它带来额外的运行和设计复杂度,因此只有在可能出现重复基类的情况下才使用,比如框架核心接口类。


文章转载自:

http://r7VH2ijf.nwrzf.cn
http://CatT2jd2.nwrzf.cn
http://lEY688uf.nwrzf.cn
http://k8J0tbCH.nwrzf.cn
http://4ODjwYWD.nwrzf.cn
http://cpdxrGlJ.nwrzf.cn
http://EQWj89PM.nwrzf.cn
http://XcYMA5Oy.nwrzf.cn
http://jL9FygUd.nwrzf.cn
http://fLIufbSI.nwrzf.cn
http://OV6u8qc1.nwrzf.cn
http://qeAurGSH.nwrzf.cn
http://BZXlJcEy.nwrzf.cn
http://qZX7tgDI.nwrzf.cn
http://3FrUOQWO.nwrzf.cn
http://bnkJ2D7e.nwrzf.cn
http://Avqk2FKZ.nwrzf.cn
http://Wjp8lH80.nwrzf.cn
http://9fasB0aW.nwrzf.cn
http://kN9BQN42.nwrzf.cn
http://fFi8N6Sf.nwrzf.cn
http://MPZfAmLv.nwrzf.cn
http://fEOY1Cfn.nwrzf.cn
http://evd1vLB3.nwrzf.cn
http://DO55A60K.nwrzf.cn
http://pXrlRYfU.nwrzf.cn
http://We4di6hh.nwrzf.cn
http://sP9oYgHo.nwrzf.cn
http://KvJPTlit.nwrzf.cn
http://KyWZ07Z0.nwrzf.cn
http://www.dtcms.com/a/364103.html

相关文章:

  • HI3519DRFCV500/HI3519DV500海思核心板IPC算力2.5T图像ISP超高清智能视觉应用提供SDK软件开发包
  • 平衡车-ADC采集电池电压
  • 从 Arm Compiler 5 迁移到 Arm Compiler 6
  • HandyControl 解决不全局引入控件部分内容不显示问题
  • 论文学习30:LViT: Language Meets Vision Transformerin Medical Image Segmentation
  • 给大模型开卷考试的机会——写给开发者的 RAG 技术入门
  • 2025年女性最实用的IT行业证书推荐:赋能职业发展的8大选择
  • Shell编程从入门到实践:基础语法与正则表达式文本处理指南
  • RPM 构建错误: /var/tmp/rpm-tmp.gAmM5N (%prep) 退出状态不好,怎么办
  • HBuilder X 4.76 开发微信小程序集成 uview-plus
  • 关于IDE的相关知识之一【使用技巧】
  • GFSK信号生成算法原理详解
  • 避免侵权!这6个可免费下载字体网站能放心商用
  • 「数据获取」《安徽建设统计年鉴》(2002-2007)(2004、2006缺失)(获取方式看绑定的资源)
  • 【世纪龙科技】汽车专业数字化课程资源包-虚拟仿真实训资源建设
  • MYSQL配置复制拓扑知识点
  • 告别集成烦恼!H-ZERO iframe 支持第三方系统 / AI 助手轻松接入
  • 【机器学习入门】5.3 线性回归原理——从模型定义到参数求解,手把手带练
  • 模型常见训练超参数介绍(1)
  • Vue.js 中深度选择器的区别与应用指南
  • Corrosion: 1靶场渗透
  • 新手也能轻松选!秒出PPT和豆包AI PPT优缺点解析
  • 自学嵌入式第三十三天:网络编程-UDP
  • SpringMVC的RequestMapping注解与请求参数绑定
  • 六、结构化开发方法
  • 《2025年AI产业发展十大趋势报告》三十五
  • MySQL数据库——概述及最基本的使用
  • Netty从0到1系列之NIO
  • 命令小工具
  • 文华财经wh6波段多空指标-变色K做多做空信号,抄底逃顶主图幅图