正在加载……
专注、离线、切勿分心
virtual(虚拟继承-虚函数)
★从语义上来讲,为什么动态多态和虚继承都使用virtual关键字来表示?

virtual的解释
1. Existing or resulting in essence or effect though not in actual fact, form, or name.
实质上的,实际上的:虽然没有实际的事实、形式或名义,但在实际上或效果上存在或产生的;

2. Existing in the mind, especially as a product of the imagination. Used in literary criticism of text.
虚的,内心的:在头脑中存在的,尤指意想的产物。用于文学批评中。

★C++中的virtual关键字采用第一个定义,即被virtual所修饰的事物或现象在本质上存在的,但是没有直观的形式表现,无法直接描述或定义,需要通过其他的间接方式或手段才能够体现出其实际上的效果。
★关键就在于存在、间接共享这三种特征
     ——虚函数是存在
     ——虚函数必须要通过一种间接的运行时(而不是编译时)机制才能够激活(调用)的函数
     ——共享性表现在基类会共享被派生类重载后的虚函数
虚拟继承如何表现:
     ——存在即表示虚继承体系和虚基类确实存在
     ——间接性表现在当访问虚基类的成员时同样也必须通过某种间接机制来完成(通过虚基表来完成)
     ——共享性表现在虚基类会在虚继承体系中被共享,而不会出现多份拷贝


virtual(虚拟继承):

★虚继承:在继承定义中包含了virtual关键字的继承关系
★虚基类:在虚继承体系中的通过virtual继承而来的基类
语法:
class Subclass: public virtual Baseclass
{
       public:    //...
       private:  //...
       protected: //...
};
其中Baseclass称之为Subclass的虚基类;
而不是说Baseclass就是虚基类



虚基派生的构造函数和析构函数:

★对普通的多层继承而言,构造函数的调用是嵌套的,如由C1类派生C2类,C2类又派生C3类时,C1->C2->C3,有:
          C2(总参数表):C1(参数表)
          C3(总参数表):C2(参数表)
★而对图10-6所示的虚基派生来说,如果按照上述规则,应该有:
        B(总参数表):A(参数表)
         C(总参数表):A(参数表)
         D(总参数表):B(参数表),C(参数表)//,A(参数表)
★这样“A(参数表)”将被执行2次,这显然不行。
★实际情况是:
      B(总参数表):A(参数表)
       C(总参数表):A(参数表)
       D(总参数表):B(参数表),C(参数表),A(参数表)
★根据虚基派生的性质,类D中只有一份虚基类A的拷贝,因此A类的构造函数在D类中只能被调用一次。所以,从A类直接派生(B和C)和间接派生(D)的类中,其构造函数的初始化列表中都要列出对虚基类A构造函数的调用。这种机制保证了不管有多少层继承,虚基类的构造函数必须且只能被调用一次
★如果未列出,则表明调用的是虚基类的无参构造函数,不管初始化列表中次序如何,对虚基类构造函数的调用总是先于普通基类的构造函数。
★如果是在若干类层次中,从虚基类直接或间接派生出来的子类的构造函数初始化列表均有对该虚基类构造函数的调用,那么创建一个子类对象的时候只有该子类列出的虚基类的构造函数被调用,其他类列出的将被忽略,这样就保证虚基类的唯一副本只被初始化一次。即虚基类的构造函数只被执行一次。
★继承机制下析构函数的调用顺序:
     ——调用派生类的析构函数
     ——然后调用派生类中成员对象的析构函数
     ——调用普通基类的析构函数
     ——最后调用虚基类的析构函数




小结:
★多态性是面向对象程序设计的又一重要特征,C++中,多态性是通过虚函数实现的。通过虚函数,可以用同一个指针(尤其是指向基类的指针)访问不同对象的成员函数。多态配合类型适应,可以为类层次中的函数调用提供统一接口,具有十分重要的意义。
★首先介绍了静态联编动态联编的含义,进一步引出了虚函数的定义方式,不同方式的访问,虚函数的响应形式有所不同。在某些情况下,在基类中可能无法定义虚函数,这时可以将其设置为纯虚函数,此时的类称为抽象类。纯虚函数和抽象类只能提供接口的作用,抽象类无法创建对象。
★和普通函数一样,多基派生时,虚拟函数也会带来二义性问题,这在类设计时要合理规划,避免出现编译器无法判断的情况。站在整个类层次的角度来看,其中的同名成员函数存在3种关系:重载(overload)、覆盖(override)和隐藏(hide、oversee),理解同名函数间的关系有助于代码的阅读和书写,提高程序设计的效率。



posted on 2018-07-25 20:46  正在加载……  阅读(369)  评论(0编辑  收藏  举报