虚函数|纯虚函数|虚基类|位域|sizeof用法

一、引入原因
   1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
   2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
   为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重载以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。

二、纯虚函数实质
    类中含有纯虚函数则它的VTABLE表不完全,有一个空位,所以,不能生成对象(编译器绝对不允许有调用一个不存在函数的可能)。在它的派生类中,除非重 载这个函数,否则,此派生类的VTABLE表亦不完整,亦不能生成对象,即它也成为一个纯虚基类。

三、 虚函数与构造、析构函数
   1、构造函数本身不能是虚拟函数;并且虚机制在构造函数中不起作用(在构造函数中的虚拟函数只会调用它的本地版本)。
      想一想,在基类构造函数中使用虚机制,则可能会调用到子类,此时子类尚未生成,有何后果!?。
   2、析构函数本身常常要求是虚拟函数;但虚机制在析构函数中不起作用。
      若类中使用了虚拟函数,析构函数一定要是虚拟函数,比如使用虚拟机制调用delete,没有虚拟的析构函数,怎能保证delete的是你希望delete的对象。
      虚机制也不能在析构函数中生效,因为可能会引起调用已经被delete掉的类的虚拟函数的问题。

四、对象切片
    向上映射(子类被映射到父类)的时候,会发生子类的VTABLE 完全变成父类的VTABLE的情况。这就是对象切片。
   原因:向上映射的时候,接口会变窄,而编译器绝对不允许有调用一个不存在函数的可能,所以,子类中新派生的虚拟函数的入口在VTABLE中会被强行“切”掉,从而出现上述情况。

五、虚拟函数使用的缺点
    优点讲了一大堆,现在谈一下缺点,虚函数最主要的缺点是执行效率较低,看一看虚拟函数引发的多态性的实现过程,你就能体会到其中的原因。
、虚基类
虚继承和虚基类的定义是非常的简单的,同时也是非常容易判断一个继承是否是虚继承
的,虽然这两个概念的定义是非常的简单明确的,但是在C++语言中虚继承作为一个比较生
僻的但是又是绝对必要的组成部份而存在着,并且其行为和模型均表现出和一般的继承体系
之间的巨大的差异(包括访问性能上的差异),现在我们就来彻底的从语言、模型、性能和
应用等多个方面对虚继承和虚基类进行研究。
    首先还是先给出虚继承和虚基类的定义。
    虚继承:在继承定义中包含了virtual关键字的继承关系;
    虚基类:在虚继承体系中的通过virtual继承而来的基类,需要注意的是:
            struct CSubClass : public virtual CBase {}; 其中CBase称之为CSubClass
            的虚基类,而不是说CBase就是个虚基类,因为CBase还可以不不是虚继承体系
            中的基类。
    有了上面的定义后,就可以开始虚继承和虚基类的本质研究了,下面按照语法、语义、
模型、性能和应用五个方面进行全面的描述。

    1. 语法
       语法有语言的本身的定义所决定,总体上来说非常的简单,如下:
           struct CSubClass : public virtual CBaseClass {};
       其中可以采用public、protected、private三种不同的继承关键字进行修饰,只要
       确保包含virtual就可以了,这样一来就形成了虚继承体系,同时CBaseClass就成为
       了CSubClass的虚基类了。
       其实并没有那么的简单,如果出现虚继承体系的进一步继承会出现什么样的状况呢?
       如下所示:
            /*
             * 带有数据成员的基类
             */
            struct CBaseClass1
            {
                CBaseClass1( size_t i ) : m_val( i ) {}
            
                size_t m_val;
            };
            /*
             * 虚拟继承体系
             */
            struct CSubClassV1 : public virtual CBaseClass1
            {
                CSubClassV1( size_t i ) : CBaseClass1( i ) {}
            };            
            struct CSubClassV2 : public virtual CBaseClass1
            {
                CSubClassV2( size_t i ) : CBaseClass1( i ) {}
            };            
            struct CDiamondClass1 : public CSubClassV1, public CSubClassV2
            {
                CDiamondClass1( size_t i ) : CBaseClass1( i ), CSubClassV1( i ), CSubClassV2( i ) {}
            };            
            struct CDiamondSubClass1 : public CDiamondClass1
            {
                CDiamondSubClass1( size_t i ) : CBaseClass1( i ), CDiamondClass1( i ) {}
            };
       注意上面代码中的CDiamondClass1和CDiamondSubClass1两个类的构造函数初始化列
       表中的内容。可以发现其中均包含了虚基类CBaseClass1的初始化工作,如果没有这
       个初始化语句就会导致编译时错误,为什么会这样呢?一般情况下不是只要在
       CSubClassV1和CSubClassV2中包含初始化就可以了么?要解释该问题必须要明白虚
       继承的语义特征,所以参看下面语义部分的解释。
七、位域与sizeof用法
含位域结构体的sizeof
前面已经说过,位域成员不能单独被取sizeof值,我们这里要讨论的是含有位域的结构
体的sizeof,只是考虑到其特殊性而将其专门列了出来。
C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,
允许其它类型类型的存在。
使用位域的主要目的是压缩存储,其大致规则为:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字
段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字
段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果位域字段之间穿插着非位域字段,则不进行压缩;
4) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
示例1:
struct BF1
{
char f1 : 3;
char f2 : 4;
char f3 : 5;
};
其内存布局为:
|_f1__|__f2__|_|____f3___|____|
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
0 3 7 8 1316
位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只
能从下一个字节开始。因此sizeof(BF1)的结果为2。
示例2:
struct BF2
{
char f1 : 3;
short f2 : 4;
char f3 : 5;
};
由于相邻位域类型不同,在VC6中其sizeof为6
示例3:
struct BF3
{
char f1 : 3;
char f2;
char f3 : 5;
};
非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。
现象:
class X{};
class Y: public virtual X{};
class Z: public virtual X{};
class A: public Y, public Z{};


  cout<<sizeof(X)<<endl;
  cout<<sizeof(Y)<<endl;
  cout<<sizeof(Z)<<endl;
  cout<<sizeof(A)<<endl;

VC的结果为都为1,4,4,8,注意虚表指针

转自:http://blog.163.com/tyw_andy/blog/static/11679021200792311382744/

posted @ 2013-09-26 12:07  Jack Tung  阅读(388)  评论(0)    收藏  举报