【C++】虚继承(virtual base classes)

这篇具有很好参考价值的文章主要介绍了【C++】虚继承(virtual base classes)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【C++】虚继承(virtual base classes)

1. 使用原因

在多重继承(Multiple Inheritance) 的情况下,尤其是菱形继承时,容易出现问题,关于菱形继承见下图:

这个类 A 由另外两个类 B 和 C 继承。这两个类都继承到另一个新类 D 中
【C++】虚继承(virtual base classes),C++,c++,开发语言
从图中我们可以看出,类 A 的数据/成员函数被两次继承到类 D。一个通过 B 类继承,另一个通过 C 类继承。当类 A 的任意数据/成员函数被类 D 的对象访问时,对于将调用哪个数据/成员函数会产生歧义,是调用通过 B 继承的,还是调用通过 C 继承的。这会混淆编译器并显示错误。

举个具体的例子
已知类 B 和 C 继承自 A 类,在 A 类中有变量 a

class A
{
public:
	int a;
};

在 B 中定义获取 a 的方法 Get, C 中定义设置 a 的方法 Set,

class B : public A
{
public:
	 int Get() { return a; }
};

class C : public A
{
public:
	void Set(int val) { a = val; }
};

此时 D 类 public 继承 B 和 C

class D : public B, public C
{};

那么使用 D 实例化的对象 dobject,调用 Get 和 Set 接口,所操作的变量 a 为同一个吗?

#include <iostream>
int main(int argc, char* argv[])
{
	D dobject;
	dobject.Set(10);
	std::cout << dobject.Get() << std::endl;// undefined value
	
	return 0;
}

答案是否定的,B,C 继承 A,则 B,C 中均有一个 a 变量, B 中的 Get 只能操作 B 中的 a, C 中的 Set 只能操作 C 中的 a ,D 同时拥有了B 和 C 的所有属性,这就是问题所在了。

2. 解决方法

在类 B C 继承类 A 时,使用虚继承 (virtual inheritance),或者叫虚基类 (virtual base classes) ,继承时额外使用 virtual 关键字

虚继承的语法为

class Derived : public virtual Base
{
};
// 或
class Derived : virtual public Base
{
};

virtual 关键字和继承限定符(public,protected,private) 谁在前,谁在后都可以。

也就是:

【C++】虚继承(virtual base classes),C++,c++,开发语言

class B : public virtual A
{
public:
	int Get() { return a; }
};

class C : virtual public A
{
public:
	void Set(int val) { a = val };
};

现在,只有数据/成员函数的一个副本将被复制到类 C 和类 B 中, 类 A 成为虚基类 (virtual base class)。虚基类提供了一种节省空间并避免使用多个继承的类层次结构中出现歧义的方法。将基类指定为虚拟基类时,它可以多次充当间接基类,而不会重复其数据成员。其数据成员的单个副本由使用虚拟基的所有基类共享。

此时 D 再正常继承 B,C 则不会出现问题,
在构建 D 类对象时,构建 B 和 C 时,不构建 B 和 C 类对象中的 A,只构建一次 A ,则在 D 的对象中只存在一个 a 变量

class D : public B, public C
{};
#include <iostream>
int main(int argc, char* argv[])
{
	D dobject;
	dobject.Set(10);
	std::cout << dobject.Get() << std::endl;// 10
	
	return 0;
}

3. 例题练习

我们可以看一下菱形继承虚继承 具体是如何实例化的

#include <iostream>

class A {
public:
  A(const char* s) 
  { std::cout << s << std::endl; }
};

class B : public virtual A {
public:
    B(const char* s1, const char* s2) : A(s1)
    { std::cout << s2 << std::endl; }
};

class C : public virtual A {
public:
    C(const char* s1, const char* s2) : A(s1) 
    { std::cout << s2 << std::endl; }
};

class D : public B, C {
public:
    D(const char* s1, const char* s2, const char* s3, const char* s4) 
    : B(s1, s2), C(s3, s4), A(s1)
    {
        std::cout << s4 << std::endl;
    }
};

int main(int argc, char* argv[])
{
    D dobject("class A", "class B", "class C", "class D");

	return 0;
}

由于B,C 为虚继承,实例化 B, C 对象时不会构造 A ,则打印为

class A
class B
class D
class D

参考链接文章来源地址https://www.toymoban.com/news/detail-642999.html

  • CppReference
    https://en.cppreference.com/w/cpp/language/derived_class

到了这里,关于【C++】虚继承(virtual base classes)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ES6——class类实现继承

    赶快悄悄的努力起来吧,不苒在这里衷心祝愿各位大佬都能顺利通过面试。 面试专栏分享,感觉有用的小伙伴可以点个订阅,不定时更新相关面试题:面试专栏 。 在ES6 中新增了 extends ,用于实现类的继承。 MDN中对 extends 的解释是这么说的: **定义:**** exten

    2023年04月10日
    浏览(34)
  • c、c++、java、python、js对比【面向对象、过程;解释、编译语言;封装、继承、多态】

    目录 内存管理、适用 区别 C 手动内存管理:C语言没有内置的安全检查机制,容易出现内存泄漏、缓冲区溢出等安全问题。 适用于系统级编程 C++ 手动内存管理:C++需要程序员手动管理内存,包括分配和释放内存,这可能导致内存泄漏和指针错误。 适用于游戏引擎和系统级编

    2024年02月08日
    浏览(73)
  • ts的class类的使用与继承, es6新增的class类

    前言:         ts的class类的使用与继承, es6新增的class类。         在ES6中为了更好的把js设计成面向对象的语言的语法特征,提出了class 类,class的本质是函数,类不可重复声明,类定义不会被提升,让js更像面向对象编程的语法。         类名建议大写,在严格要求

    2024年02月05日
    浏览(42)
  • js继承的几种方式(原型链继承、构造函数继承、组合式继承、寄生组合式继承、ES6的Class类继承)

    实现原理: 子类的原型指向父类实例 。子类在自身实例上找不到属性和方法时去它父类实例(父类实例和实例的原型对象)上查找,从而实现对父类属性和方法的继承 缺点: 子类创建时不能传参(即没有实现super()的功能); 父类实例的修改会影响子类所有实例 实现原理:

    2024年02月07日
    浏览(45)
  • c#继承(new base)的使用

    C#中的继承是面向对象编程的重要概念之一,它允许一个类(称为子类或派生类)从另一个类(称为父类或基类)继承属性和行为。 继承的主要目的是实现代码重用和层次化的组织。子类可以继承父类的字段、属性、方法和事件,而无需重新实现这些成员。通过继承,我们可

    2024年02月09日
    浏览(36)
  • c# 继承 new,base的使用

    在C#中,继承是指一个类(称为派生类或子类)从另一个类(称为基类或父类)继承属性和方法的机制。继承可以帮助我们重用代码并构建层次化的类结构。要使用继承,在定义类时使用冒号(:)指定其基类。下面是一个简单的C#继承示例: 在上面的示例中,类Animal是基类,

    2024年01月24日
    浏览(36)
  • python中的类class: 继承、覆盖、重写、重载、扩展、多态、封装

    使用  class  创建类。类中有方法、属性。 1.1 __init__() 函数 类的内置  __init__()  函数。所有类都有一个名为 __init__() 的函数,它在启动类时执行。 使用 __init__() 函数将值赋给对象属性,或者在创建对象时需要执行的其他操作。 每次使用类创建新对象时,都会 自动调

    2024年02月08日
    浏览(49)
  • 什么是JavaScript中的类(Class)和继承(Inheritance)?它们与原型链继承相比有哪些优缺点?

    JavaScript中的类(Class)和继承(Inheritance)是面向对象编程的重要概念。类是一种抽象的概念,它定义了一组属性和方法,并且这些属性和方法可以在同一组对象中重复使用。继承是类之间的层次关系,其中一个类继承了另一个类的属性和方法,从而能够共享其代码和行为。 在Ja

    2024年02月12日
    浏览(55)
  • python类(class)的定义、使用、继承、应用的通俗易懂理解

    1.为什么使用类         一个类(class)可以包囊众多函数,所以一般使用类的情况下,都需要用到众多有共性的函数,把这些可能会一起调用的函数或者需要接连调用的函数归为一个类,这样需要使用的时候可以轻易的找到。         在处理不同细节时使用的类区别

    2024年02月22日
    浏览(35)
  • C++类开发的第六篇(虚拟继承实现原理和cl命令的使用的bug修复)

    接上一篇末尾虚拟继承的简单介绍之后,这篇来详细讲一下这个内存大小是怎么分配的。 cl 是 Microsoft Visual Studio 中的 C/C++ 编译器命令。通过在命令行中键入 cl 命令,可以调用 Visual Studio 的编译器进行编译操作。 cl 命令提供了各种选项和参数,用于指定源文件、编译选项、

    2024年03月09日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包