C++虚基类

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

1、为什么要引入虚基类

如果一个派生类是从多个基类派生出来的,而这些基类又有一个共同的基类,则在这个派生类中访问这个共同的基类中的成员时,可能会产生二义性。

比如有以下结构

class  B{
  protected:  int a;
  public: B( ){ ...} };
class B1: public  B{
  public: base1( ){...} };
class B2: public  B{
  public: B2( ){...} }; 
class D:public B1,public B2{ . . .}

C++虚基类
C++虚基类
以下程序会报错,因为a具有二义性。

class B {
  protected:
   int a;
  public:
   B( ){ a=5; cout<<"B a="<<a<<endl;}  };
class B1:public B{
   public:
    B1( ){ a=a+10; cout<<"B1 a="<<a<<endl;} };
class B2:public B{
  public:
    B2( ){ a=a+20; cout<<"B2 a="<<a<<endl;} };
class D:public B1,public B2{
  public:
    D( ){ cout<<"D a="<< a<<endl;} };
    //将D的构造函数改成如下结构能够通过运行
    // D( ){ cout<<"B1 a="<<B1:: a<<endl;  cout<<"B2 a="<< B2::a<<endl;}  };
int main( )
{ D  obj; return  0;  }
B a=5
B1 a=15
B a=5
B2 a=25
B1 a=15
B2 a=25

能够看到a被初始化了两次,我们希望a只被再第一次初始化以后就不再继续初始化了。那么我们要使用虚基类。

2、虚基类的概念

如果将公共基类说明为虚基类。那么,对同一个虚基类的构造函数只调用一次,这样从不同的路径继承的虚基类的成员在内存中就只拥有一个拷贝。从而解决了以上的二义性问题。

继承方式

 class  派生类名:继承方式 virtual 基类名
 {}class  派生类名: virtual 继承方式 基类名
 {}

我们将上面的继承结构改成如下所示

class B{
  protected:  int a;
  public: B( ){ ...} };
class B1: public virtual B{
  public: B1( ){...} };
class B2: public virtual B{
  public: B2( ){...} }; 
class D:public B1,public B2{ . . .}

C++虚基类
C++虚基类

3、虚基类的初始化

虚基类的初始化与一般的多重继承的初始化在语法上基本上是一样的,但有一些特殊的规定:
对同一个虚基类的构造函数只调用一次,且是在第一次出现时调用;

修改后代码

class B {
  protected:
   int a;
  public:
   B( ){ a=5; cout<<"B a="<<a<<endl;}  };
class B1:public  virtual B{
   public:
    B1( ){ a=a+10; cout<<"B1 a="<<a<<endl;} };
class B2:public  virtual B{
  public:
    B2( ){ a=a+20; cout<<"B2 a="<<a<<endl;} };
class D:public B1,public B2{
  public:
    D( ){ cout<<"D a="<< a<<endl;} };
int main( )
{ D  obj; return  0;  }

运行结果

B    a=5
B1   a=15
B2   a=35
D    a=35

可以看到a只被初始化了一次。

4、关于虚基类子类构造函数

如果在虚基类中定义有带形参的构造函数,并且没有定义默认形式的构造函数,则整个继承结构中,所有直接或间接的派生类都必须在构造函数的成员初始化表中列出对虚基类构造函数的调用,以初始化在虚基类中定义的数据成员。

class B {  int a;
  public:
   B(int sa)
  { a=sa;  cout<<"Constructing B"<<endl; } };
class B1:virtual public B{  int b;
  public:
   B1(int sa,int sb):B(sa)
   { b=sb; cout<<"Constructing B1"<<endl; } };
class B2:virtual public B{  int c;
  public:
   B2(int sa,int sc):B(sa)
  { c=sc; cout<<"Constructing B2"<<endl; } };
class D:public B1,public B2 {
   int d;
  public:
   D(int sa,int sb,int sc,int sd): B(sa),B1(sa,sb),B2(sa,sc)
   { d=sd; cout<<"Constructing D"<<endl;} };
 int main()
{ D  obj(2,4,6,8); return 0; }

任何一个类的上层(不一定是直接继承虚基类),只要含有虚基类都要再构造函数中调用虚基类的构造函数。

运行结果

Constructing B
Constructing B1
Constructing B2
Constructing D

5、虚基类构造函数调用顺序

若同一层次中同时包含虚基类和非虚基类,应先调用虚基类的构造函数,再调用非虚基类的构造函数,最后调用派生类构造函数;文章来源地址https://www.toymoban.com/news/detail-477789.html

 class Xpublic Y,virtual public Z{
        //…
    };
    X  one;
    定义类X的对象one后,将产生如下的调用次序。
    Z( )Y( )X( )

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

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

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

相关文章

  • C静态库的创建与使用--为什么要引入静态库?

    C源程序需要经过预处理、编译、汇编几个阶段,得到各自源文件对应的 可重定位目标文件 ,可重定位目标文件就是各个源文件的二进制机器代码,一般是.o格式。比如:util1.c、util2.c及main.c三个C源文件,经过预处理器、编译器、汇编器的处理,就可以得到各自的目标文件u

    2024年02月08日
    浏览(43)
  • Redis——关于它为什么快?使用场景?以及使用方式?为何引入多线程?

    目录 1.既然redis那么快,为什么不用它做主数据库,只用它做缓存? 2.Redis 一般在什么场合下使用?  3.redis为什么这么快? 4.Redis为什么要引入了多线程? redis设计者的初衷,就只是为了存储 小量级的共享数据 。 所以,他敢直接单线程直接干上去,因为数据量小,所以够快

    2024年01月25日
    浏览(53)
  • 分布式 - 谈谈你对分布式的理解,为什么引入分布式?

    不啰嗦,我们直接开始! 真正了解分布式系统的概念,日后工作中具有分布式系统设计思想。 能否在设计中对系统稳定性方面考虑周全。 能构建高 QPS 健壮的系统架构。 问题分析: 各种分布式框架层出不穷,Spring Cloud,阿里的 Dubbo,无论使用哪一个,原理都相同,考察下基

    2024年02月15日
    浏览(52)
  • web中为什么要引入service层以及前端控制器DispatchServlet的作用以及原理剖析

    review: 最初的做法是: 一个请求对应一个Servlet,这样存在的问题是servlet太多了 把一些列的请求都对应一个Servlet, IndexServlet/AddServlet/EditServlet/DelServlet/UpdateServlet - 合并成FruitServlet 通过一个operate的值来决定调用FruitServlet中的哪一个方法 使用的是switch-case 在上一个版本中,Ser

    2024年02月04日
    浏览(41)
  • 为什么要学习C++

    UINX操作系统诞生之初是用汇编语言编写的。 随着UNIX的发展,汇编语言的开发效率成为一个瓶颈。 寻找新的高效开发语言成为UNIX开发者需要解决的问题。 当时BCPL语言成为了当时的选择之一。 Ken Thomposn对BCPL进行简化得到了B语言。 但是B语言不是直接生成机器码,而是生成中

    2024年02月10日
    浏览(46)
  • 为什么 C 语言没有被 C++ 取代?

    今日话题,为什么 C 语言没有被 C++ 取代?C语言之所以没有被C++完全取代,有几个主要原因。首先,C++的编译器实现相对复杂,这对于一些嵌入式平台来说是一个问题。许多嵌入式系统只支持C语言,因此C++在这些平台上无法使用。即使在支持C++的嵌入式平台上,也常常存在不

    2024年01月19日
    浏览(70)
  • 有了NULL,为什么C++还需要nullptr?

    目录 1.引言 2.类型安全 3.函数重载 4.代码清晰性 5.示例 6.总结 在C++编程中,nullptr是一个类型安全的空指针常量,自C++11起被引入。然而,在此之前,程序员们通常使用NULL或0来表示空指针。那么,为什么有了NULL之后,C++还需要引入nullptr呢?本文将从类型安全、函数重载和代

    2024年02月21日
    浏览(51)
  • 为什么C++这么复杂还不被淘汰?

    C++是一门广泛使用的编程语言,主要用于系统和应用程序的开发。尽管C++具有一些复杂的语法和概念,但它仍然是编程界的重量级选手,在编程语言排行榜中一直位居前列。 为什么C++这么复杂还不被淘汰呢? C++有以下优势 1、C++具有高性能 C++是一门编译型语言,可以直接编

    2024年02月05日
    浏览(51)
  • c++ 移动构造方法为什么要加noexcept

    最近看了候捷老师的c++的教程, 他说移动构造方法要加noexcept,  在vector扩容的时候, 如果有移动构造方法没有加noexcept,是不会调用的. 个人感觉有些神奇, 这就去查下一探究竟. 测试代码如下:  执行结果如下: 我们知道vector 是要扩容的, 在A(A a) 并没有添加noexcept, 所

    2024年02月10日
    浏览(79)
  • C++ vector元素类型为什么不能是引用

    vectorT 引用必须要进行初始化,不能初始化为空对象,初始化后不能改变指向 引用是别名,不是对象,没有实际地址, 不能定义引用的指针 ,也 不能定义引用的引用 推荐一个零声学院项目课,个人觉得老师讲得不错,分享给大家: 零声白金学习卡(含基础架构/高性能存储

    2024年02月15日
    浏览(59)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包