精华内容
下载资源
问答
  • 虚基类与虚继承

    2015-09-04 13:25:11
    引入虚基类的目的是为了解决类继承过程中产生的二义性问题;这种二义性问题常见于具有菱形继承关系的类中; 比如:有四个类:A、B、C、D;它们之间的继承关系是:B继承A,C继承A,D继承B和C;这就形成了一个菱形的继承关系;...

    虚继承就是虚基类的使用;
    引入虚基类的目的是为了解决类继承过程中产生的二义性问题;这种二义性问题常见于具有菱形继承关系的类中;
    比如:有四个类:A、B、C、D;它们之间的继承关系是:B继承A,C继承A,D继承B和C;这就形成了一个菱形的继承关系;具有这种继承关系的图叫做有向无环图;
    那么类D就有两条继承路径:D-->B-->A和D-->C-->A;而类A是派生类D的两条继承路径上的公共基类,那么这个公共基类就会在派生类D的对象中产生多个基类子对象;这个时候在引用派生类D的对象时,就会产生明显的二义性;要解决这个二义性,就必须将这个基类设定为虚基类;

    虚基类的定义格式如下:
    class <派生类>: virtual <继承方式> <基类名>{};

    例如:
    class A:
    {
      public:
        void fun();
      protected:
        int a;
    };

    class B: virtual public A
    {
      protected:
        int b;
    };

    class C: virtual pulic A
    {
      protected:
        int c;
    };

    class D: public B, public C
    {
      public:
        int g();
      protected:
        int d;
    };

    这样的话,不同继承路径上的虚基类子对象在派生类中被合并成一个子对象了,这便是虚基类的作用,这样就可以消除合并之前出现的二义性问题;这时在派生类D的对象中只存在一个类A的子对象;

    C++中的二义性检查是在访问权限或类型检查之前进行的;

    注意:
    引进虚基类之后,派生类(子类)的对象中只存在一个虚基类的子对象;当一个类拥有虚基类的时候,编译系统会为这个类的对象定义一个指针成员,并让它指向虚基类的子对象;该指针被称为虚基类指针;这个概念与虚函数表指针不同;在内存中,一般情况下,虚基类子对象在派生类对象中是放置在派生类对象所占内存块的尾部,这一点由编译器来决定的;

    虚基类的构造函数:
    为了初始化虚基类的子对象,派生类的构造函数要调用基类的构造函数.由于派生类的对象中只有一个虚基类子对象,那么就必须要保证虚基类的子对象只能被初始化一次,也就是说,虚基类的构造函数只能被调用一次;由于继承的层次可能会很深,C++规定:把真正创建对象时所指定的类称为是最派生类,虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的;如果一个派生类有一个直接或间接的虚基类,那么派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用,如果没有列出,则表示使用该虚基类的缺省构造函数来初始化派生类对象中的虚基类子对象;
    从虚基类直接或间接继承的派生类的成员初始化列表中必须列出对该虚基类构造函数的调用,但是只有真正用于创建对象的那个最派生类的构造函数才会真正调用虚基类的构造函数,而该派生类的基类中所列出的对这个虚基类的构造函数的调用在实际执行中被忽略,这样就保证对虚基类的子对象只初始化一次;
    C++又规定:在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,则虚基类的构造函数先于非虚基类的构造函数被执行;也就是说,执行虚基类构造函数的优先级要高于执行非虚基类构造函数的优先级;
    在需要使用虚基类的场合,由于每一个继承类都必须包含初始化语句,而这些初始化语句仅仅只在最底层子类(最派生类)中才实际调用;这样就会使得某些上层子类得到的虚基类子对象的状态不是自己所期望的(因为自己的初始化语句被压制了/跳过了),所以,一般建议不要在虚基类中包含任何数据成员(不要有状态),只可以作为接口类来提供;
    虚基类实例地址 = 派生类的vbptr + 派生类的vbptr到虚基类实例地址的偏移量;

    菱形继承关系的类图:
    虚继承---虚基类 - 哥哥 - 哥哥

    虚继承---虚基类 - 哥哥 - 哥哥

    虚继承---虚基类 - 哥哥 - 哥哥

    虚继承---虚基类 - 哥哥 - 哥哥虚继承---虚基类 - 哥哥 - 哥哥虚继承---虚基类 - 哥哥 - 哥哥虚继承---虚基类 - 哥哥 - 哥哥虚继承---虚基类 - 哥哥 - 哥哥

    阅读(2000)| 评论(0)
    展开全文
  • Mayuyu带你学虚基类

    2014-03-31 13:44:11
    最近Mayuyu在学习C++,对Mayuyu来说C++还是很容易的,因为人家本来就冰雪聪明啊,哈哈!不开玩笑啦,进入 正题。今天Mayuyu将会带领你们...用五个字概括引入虚基类的目的:消除二义性。我们先来看一段代码: #include

    最近Mayuyu在学习C++,对Mayuyu来说C++还是很容易的,因为人家本来就冰雪聪明啊,哈哈!不开玩笑啦,进入

    正题。今天Mayuyu将会带领你们一起来学习虚基类。

     

    那么,听到虚基类这个词,你的第一个反应就是什么是虚基类,为什么C++要引入它。那么现在可爱的Mayuyu将会给

    你详细探讨。

     

    用五个字概括引入虚基类的目的:消除二义性。我们先来看一段代码:

    #include <iostream>
    #include <string.h>
    #include <stdio.h>
    
    using namespace std;
    
    class A
    {
        protected:
            int x;
        public:
            void setterA(int x)
            {
                this->x = x;
            }
    };
    
    class B:public A
    {
        public:
            void changeB()
            {
                x += 10;
            }
    };
    
    class C:public A
    {
        public:
            void changeC()
            {
                x += 20;
            }
    };
    
    class D:public B,public C
    {
        public:
            void show()
            {
                cout<<x<<endl;
            }
    };
    
    int main()
    {
        D *d = new D();
        d->setterA(10);
        d->show();
        return 0;
    }
    

     

    嗯,从上面的代码中可以看出,类B和类C继承父类A,同时类D又继承类B和类C。我们都知道,一般情况下,一个类

    要继承另一个类,那么子类从父类中继承下来的所有数据都是重新拷贝了一份,而不是共享原来父类的所有数据。

     

    那么代码中,类B和类C都拷贝了一份类A的数据,然后呢,类D又来拷贝类B和类C的所有数据。现在在类D中,我们有

    一个方法叫做show(),它输出的是x的值。这样问题就来了,x有两份拷贝,调用的是哪一个x,类B的还是类C的?

     

    所以到了这里,上述代码编译就会出错,同样的道理主函数中d->setterA(10),到底是调用的类B的setterA()

    方法还是类B的setterA()方法,这个不能确定。这个就是我们所说的二义性。那么我们必须要消除这种二义性才

    行啊,怎么消除呢?实际上有两种方法:

     

      (1)在调用时候指明要调用所属类的数据,比如修改为cout<<B::x<<endl和d->B::setter(10)。

      (2)让类B和类C拥有同一个拷贝,而不是两个不同的拷贝,这就是我们要讲的虚基类。

     

    也就是说,在上面的代码中,A,B,C,D四个类的关系如下:

     

     

    而在虚基类中类B和类C共享父类A的所有数据,那么就应该是如下拓扑结构

     

     

    这样,因为只有一个拷贝,所以直接用A的数据就不会引起二义性了,这就是引入虚基类的好处。

     

    所以,我们总结一下:

        当一个类的多个直接基类是从另一个共同基类派生而来的,这些直接基类中从上一级基类继承来的成员就拥有

    相同的名称。在派生类的对象中,这些同名成员在内存中同时拥有多个拷贝。如何进行分辨呢?当然引入虚基类是

    好的啦!

     

    虚基类的语法格式如下:

     

        class 派生类名:virtual 继承方式 父类名

        {

             //...

        }

     

    这样所有的派生类继承同一个基类时只有一个拷贝了,就消除了二义性。

     

    现在Mayuyu就可以把原代码修改如下:

     

    #include <iostream>
    #include <string.h>
    #include <stdio.h>
    
    using namespace std;
    
    class A
    {
        protected:
            int x;
        public:
            void setterA(int x)
            {
                this->x = x;
            }
    };
    
    class B:virtual public A
    {
        public:
            void changeB()
            {
                x += 10;
            }
    };
    
    class C:virtual public A
    {
        public:
            void changeC()
            {
                x += 20;
            }
    };
    
    class D:public B,public C
    {
        public:
            void show()
            {
                cout<<x<<endl;
            }
    };
    
    int main()
    {
        D *d = new D();
        d->setterA(10);
        d->show();
        return 0;
    }
    

     

    这样编译顺利通过,哈哈!至此虚基类的核心就差不多讲完了。

     

    至此,Mayuyu感谢你的阅读!!!求评论!

     

    展开全文
  • 虚继承---虚基类

    2012-10-04 13:37:58
    引入虚基类的目的是为了解决类继承过程中产生的二义性问题;这种二义性问题常见于具有菱形继承关系的类中; 比如:有四个类:A、B、C、D;它们之间的继承关系是:B继承A,C继承A,D继承B和C;这就形成了一个菱形的继承关系;...

    虚继承就是虚基类的使用;
    引入虚基类的目的是为了解决类继承过程中产生的二义性问题;这种二义性问题常见于具有菱形继承关系的类中;
    比如:有四个类:A、B、C、D;它们之间的继承关系是:B继承A,C继承A,D继承B和C;这就形成了一个菱形的继承关系;具有这种继承关系的图叫做有向无环图;
    那么类D就有两条继承路径:D-->B-->A和D-->C-->A;而类A是派生类D的两条继承路径上的公共基类,那么这个公共基类就会在派生类D的对象中产生多个基类子对象;这个时候在引用派生类D的对象时,就会产生明显的二义性;要解决这个二义性,就必须将这个基类设定为虚基类;

    虚基类的定义格式如下:
    class <派生类>: virtual <继承方式> <基类名>{};

    例如:
    class A:
    {
      public:
        void fun();
      protected:
        int a;
    };

    class B: virtual public A
    {
      protected:
        int b;
    };

    class C: virtual pulic A
    {
      protected:
        int c;
    };

    class D: public B, public C
    {
      public:
        int g();
      protected:
        int d;
    };

    这样的话,不同继承路径上的虚基类子对象在派生类中被合并成一个子对象了,这便是虚基类的作用,这样就可以消除合并之前出现的二义性问题;这时在派生类D的对象中只存在一个类A的子对象;

    C++中的二义性检查是在访问权限或类型检查之前进行的;

    注意:
    引进虚基类之后,派生类(子类)的对象中只存在一个虚基类的子对象;当一个类拥有虚基类的时候,编译系统会为这个类的对象定义一个指针成员,并让它指向虚基类的子对象;该指针被称为虚基类指针;这个概念与虚函数表指针不同;在内存中,一般情况下,虚基类子对象在派生类对象中是放置在派生类对象所占内存块的尾部,这一点由编译器来决定的;

    虚基类的构造函数:
    为了初始化虚基类的子对象,派生类的构造函数要调用基类的构造函数.由于派生类的对象中只有一个虚基类子对象,那么就必须要保证虚基类的子对象只能被初始化一次,也就是说,虚基类的构造函数只能被调用一次;由于继承的层次可能会很深,C++规定:把真正创建对象时所指定的类称为是最派生类,虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的;如果一个派生类有一个直接或间接的虚基类,那么派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用,如果没有列出,则表示使用该虚基类的缺省构造函数来初始化派生类对象中的虚基类子对象;
    从虚基类直接或间接继承的派生类的成员初始化列表中必须列出对该虚基类构造函数的调用,但是只有真正用于创建对象的那个最派生类的构造函数才会真正调用虚基类的构造函数,而该派生类的基类中所列出的对这个虚基类的构造函数的调用在实际执行中被忽略,这样就保证对虚基类的子对象只初始化一次;
    C++又规定:在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,则虚基类的构造函数先于非虚基类的构造函数被执行;也就是说,执行虚基类构造函数的优先级要高于执行非虚基类构造函数的优先级;
    在需要使用虚基类的场合,由于每一个继承类都必须包含初始化语句,而这些初始化语句仅仅只在最底层子类(最派生类)中才实际调用;这样就会使得某些上层子类得到的虚基类子对象的状态不是自己所期望的(因为自己的初始化语句被压制了/跳过了),所以,一般建议不要在虚基类中包含任何数据成员(不要有状态),只可以作为接口类来提供;
    虚基类实例地址 = 派生类的vbptr + 派生类的vbptr到虚基类实例地址的偏移量;

    菱形继承关系的类图:
    虚继承---虚基类 - 哥哥 - 哥哥

    虚继承---虚基类 - 哥哥 - 哥哥

    虚继承---虚基类 - 哥哥 - 哥哥

    虚继承---虚基类 - 哥哥 - 哥哥虚继承---虚基类 - 哥哥 - 哥哥虚继承---虚基类 - 哥哥 - 哥哥虚继承---虚基类 - 哥哥 - 哥哥虚继承---虚基类 - 哥哥 - 哥哥

    展开全文
  • 引入虚基类的目的是为了解决类继承过程中产生的二义性问题;这种二义性问题常见于具有菱形继承关系的类中; 比如:有四个类:A、B、C、D;它们之间的继承关系是:B继承A,C继承A,D继承B和C;这就形成了一个菱形的继承关系;...

    虚继承就是虚基类的使用;
    引入虚基类的目的是为了解决类继承过程中产生的二义性问题;这种二义性问题常见于具有菱形继承关系的类中;
    比如:有四个类:A、B、C、D;它们之间的继承关系是:B继承A,C继承A,D继承B和C;这就形成了一个菱形的继承关系;具有这种继承关系的图叫做有向无环图;
    那么类D就有两条继承路径:D-->B-->A和D-->C-->A;而类A是派生类D的两条继承路径上的公共基类,那么这个公共基类就会在派生类D的对象中产生多个基类子对象;这个时候在引用派生类D的对象时,就会产生明显的二义性;要解决这个二义性,就必须将这个基类设定为虚基类;< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />

    虚基类的定义格式如下:
    class <派生类>: virtual <继承方式> <基类名>{};

    例如:
    class A:
    {
      public:
        void fun();
      protected:
        int a;
    };

    class B: virtual public A
    {
      protected:
        int b;
    };

    class C: virtual pulic A
    {
      protected:
        int c;
    };

    class D: public B, public C
    {
      public:
        int g();
      protected:
        int d;
    };

    这样的话,不同继承路径上的虚基类子对象在派生类中被合并成一个子对象了,这便是虚基类的作用,这样就可以消除合并之前出现的二义性问题;这时在派生类D的对象中只存在一个类A的子对象;

    C++中的二义性检查是在访问权限或类型检查之前进行的;

    注意:
    引进虚基类之后,派生类(子类)的对象中只存在一个虚基类的子对象;当一个类拥有虚基类的时候,编译系统会为这个类的对象定义一个指针成员,并让它指向虚基类的子对象;该指针被称为虚基类指针;这个概念与虚函数表指针不同;在内存中,一般情况下,虚基类子对象在派生类对象中是放置在派生类对象所占内存块的尾部,这一点由编译器来决定的;

    虚基类的构造函数:
    为了初始化虚基类的子对象,派生类的构造函数要调用基类的构造函数.由于派生类的对象中只有一个虚基类子对象,那么就必须要保证虚基类的子对象只能被初始化一次,也就是说,虚基类的构造函数只能被调用一次;由于继承的层次可能会很深,C++规定:把真正创建对象时所指定的类称为是最派生类,虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的;如果一个派生类有一个直接或间接的虚基类,那么派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用,如果没有列出,则表示使用该虚基类的缺省构造函数来初始化派生类对象中的虚基类子对象;
    从虚基类直接或间接继承的派生类的成员初始化列表中必须列出对该虚基类构造函数的调用,但是只有真正用于创建对象的那个最派生类的构造函数才会真正调用虚基类的构造函数,而该派生类的基类中所列出的对这个虚基类的构造函数的调用在实际执行中被忽略,这样就保证对虚基类的子对象只初始化一次;
    C++又规定:在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,则虚基类的构造函数先于非虚基类的构造函数被执行;也就是说,执行虚基类构造函数的优先级要高于执行非虚基类构造函数的优先级;
    在需要使用虚基类的场合,由于每一个继承类都必须包含初始化语句,而这些初始化语句仅仅只在最底层子类(最派生类)中才实际调用;这样就会使得某些上层子类得到的虚基类子对象的状态不是自己所期望的(因为自己的初始化语句被压制了/跳过了),所以,一般建议不要在虚基类中包含任何数据成员(不要有状态),只可以作为接口类来提供;
    虚基类实例地址 = 派生类的vbptr + 派生类的vbptr到虚基类实例地址的偏移量;(具体的计算公式的解释请看博客:《虚函数和虚继承寻址》

    http://blog.csdn.net/dandinghelhg/article/details/11520275

    展开全文
  • //尼玛这都能行,被踢大了二、虚函数和一般函数虚函数就是加了vritual关键字的函数,引入虚函数的目的是为了实现多态性(在此为运行时的多态性),即可以通过父类的指针调用子类的对象,从而产生不同的...
  • 在这里我使用了一个空类K,不要被这个东西所迷惑,我使用这个空类的目的主要是为了让它产生虚基类表指针而又不引入虚基类成员变量,这样我就可以少叙述一些关于虚基类成员排放的东西,而将焦点聚集在引入的那个虚...
  • 引入虚函数的目的

    千次阅读 2014-03-30 01:04:21
    引入虚函数的目的就是为了让指向派生类对象的基类指针可以调用覆盖了基类虚方法的那个虚方法。 把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的...
  • 基类中,引入虚函数的目的,是为了派生类(子类)中实现多态。 有些基类,不能够实例化,实例化没有意义,比如说你定义了一动物为基类,该基类实例化没有实际意义,还是抽象的,不存在的具体对象。 为此,在基类...
  • c++函数详解

    2017-03-21 17:05:47
    博主邮箱:2930526477@qq.com(有志同道合之人,可以加qq交流交流编程心得)1、引入虚函数的目的基类的派生类中就可以通过重写虚函数来实现对基类虚函数的覆盖。当基类的指针指向派生类的对象时,基类指针对虚函数...
  • C++基础知识(二)

    2005-01-09 11:41:00
     虚基类的引入和说明  前面简单地介绍了要引进虚基类的原因。实际上,引进虚基类的真正目的是为了解决二义性问题。  虚基类说明格式如下:  virtual <继承方式><基类名>  其中,...
  • 析构函数

    2010-12-20 15:42:00
    在删除指向子类的基类指针时(base *p=new derived; delete p;),如果基类析构函数不为虚函数,则不会调用子类的析构。 引入虚析构函数的目的应该是正确释放指向子类的基类指针。
  • C++: 多态 函数

    2015-12-11 11:32:00
    一.多态: 1.多态是什么:具有不同...1.引入目的:可以通过基类指针或引用来访问基类和派生类中同名函数(简化多态函数调用). 方法:先用基类指针指向该对象即可. 2.重载与函数: 重载处理是同一层次上同...
  • 实验8 继承与派生

    2018-08-29 20:29:11
    实验8 继承与派生 一、实验目的 理解继承的含义,掌握派生类的定义和实现方法。... 理解虚基类在类的继承层次中的作用,虚基类的引入对程序运行时的影响,能够对使用虚基类的简单程序写出程序结果。 二、知...
  • C++抽象设计目的

    2017-07-24 09:49:19
    1.纯虚函数是在基类中声明的虚函数,首先最起码是在基类的虚函数 2.纯虚函数在基类中是没有定义,这里指基类,但任何子类中都要定义该纯虚函数实现方法 3.在基类中实现纯虚函数方法是函数原型后加“=0”...
  • c++ 函数以及抽象类

    2013-09-17 15:22:52
    多态性,动态绑定,c++多态性通过函数来实现。 抽象类: 引入目的: 1、方便使用多态特性,需要在基类中定义函数。 2、有些情况下,不允许基类本身生成对象,e.g. 动物可以派生出狗、猫等子类,这些子类可以...
  • 掌握派生类成员的标识与访问中同名覆盖原则、二元作用域分辨符和虚基类的用法 掌握派生类构造函数和析构函数的定义及调用次序 理解运算符重载的目的,掌握运算符重载函数的编写方法 二、实验准备 类的继承和派生 ...
  • 4.8、多路径继承 4.8.1、虚继承的引用和说明 引入目的:解决多路径继承而引起的二义性问题。 格式:virtual<继承方式>...4.8.2、虚基类的构造函数 虚基类构造函数必须只被调用一次,目的是要保...
  • 3.掌握派生类成员的标识与访问中同名覆盖原则、二元作用域分辨符和虚基类的用法 4.掌握派生类构造函数和析构函数的定义及调用次序 5.理解运算符重载的目的,掌握运算符重载函数的编写方法 二、实验准备 1. 类的...
  • C++ 抽象类

    千次阅读 2015-03-24 12:02:46
    抽象类是为了抽象和设计的目的而建立的,处于继承层次结构的上层。 具体类是能够建立对象的类。 一、纯虚函数定义.  纯虚函数是在基类中声明的函数,它在基类中没有定义,但要求任何派生类都要定义...
  • 最佳答案 函数是指一个类中你希望重载成员函数,当你用一个基类指针或引用指向一个继承类对象时候,你调用一个函数, 实际调用是继承类版本。 有纯虚函数类是抽象类,不能生成对象,只能派生...
  • C++面试题001

    2021-01-19 16:47:15
    引入虚函数的目的是为了动态绑定; 纯虚函数声明:virtual returntype func(parameter)=0;引入纯虚函数是为了派生接口。(使派生类仅仅只是继承函数的接口) 2.基类为什么需要虚析构函数? 防止内存泄漏。想去借助...
  • C++面试题

    2020-09-24 20:04:11
    引入虚函数的目的是为了动态绑定; 纯虚函数声明:virtual returntype func(parameter)=0;引入纯虚函数是为了派生接口。(使派生类仅仅只是继承函数的接口) 2.基类为什么需要虚析构函数? 防止内存泄漏。想去借助...
  • C++面试常见题

    千次阅读 2020-03-19 12:12:28
    引入虚函数的目的是为了动态绑定; 纯虚函数声明:virtual returntype func(parameter)=0;引入纯虚函数是为了派生接口。(使派生类仅仅只是继承函数的接口) 2.基类为什么需要虚析构函数? 防止内存泄...
  • C++面试常见题之美

    2020-01-29 08:43:49
    引入虚函数的目的是为了动态绑定; 纯虚函数声明:virtual returntype func(parameter)=0;引入纯虚函数是为了派生接口。(使派生类仅仅只是继承函数的接口) 2.基类为什么需要虚析构函数? 防止内存泄...
  • C++面试常见题总结

    2019-03-01 12:50:40
    C++面试常见题总结 ...引入虚函数的目的是为了动态绑定; 纯虚函数声明:virtual returntype func(parameter)=0;引入纯虚函数是为了派生接口。(使派生类仅仅只是继承函数的接口) 2.基类为什么需要虚...

空空如也

空空如也

1 2 3
收藏数 42
精华内容 16
关键字:

引入虚基类的目的