理解虚基类、虚函数与纯虚函数的概念

来源:互联网 发布:光影魔术手 mac官网 编辑:程序博客网 时间:2024/05/06 03:01

原文出处 http://www.cnblogs.com/MS-Frank/archive/2008/01/16/1041310.html

加入自己一些理解


 虚基类

       在说明其作用前先看一段代码

class A{public:    int iValue;};class B:public A{public:    void bPrintf(){cout<<"This is class B"<<endl;};};class C:public A{public:    void cPrintf(){cout<<"This is class C"<<endl;};};class D:public B,public C{public:    void dPrintf(){cout<<"This is class D"<<endl;};};void main(){    D d;    cout<<d.iValue<<endl; //错误,不明确的访问    cout<<d.A::iValue<<endl; //正确    cout<<d.B::iValue<<endl; //正确    cout<<d.C::iValue<<endl; //正确}
B 继承 A 的 ivalue

C 继承 A 的 ivalue

D 又直接 从 B , C 继承 ivalue

那么D 中就 存有 A, B, C 的ivalue 了.. 直接调用 d.iValue 编译器并不知道要调用哪个,存在二议性问题,需要采用域限定符来确定调用哪个

(这种同JAVA类从 多个interface 继承同名 成员 也是一样的, 需要加上 “接口名.成员 ”)

不过 类D的实例中就有多个iValue的实例,就会占用内存空间。所以C++中就引用了虚基类的概念,来解决这个问题。


虚基类, 就是在继承父类的时候用 virtual 关键字声明, 表示被继承类是虚基类

class A{public:    int iValue;};class B:virtual public A{public:    int b;};class C:virtual public A{public:    int c;};class D:public B,public C{public:    int d;};void main(){    D d;    cout<<d.iValue<<endl; //正确}

加入virtual 继承, D中的ivalue 就直接从A 中拷贝。 

我们都知道,VC 的编译器对int 是 4个字节,从上面程序中 A 只有int 类型的iValue,所以

sizeof(A) = 4

sizeof(B) = 8 // 从A得到ivalue + 自身的b 变量

sizeof(C) = 8 // 从A得到ivalue + 自身的c 变量

sizeof(D)=16 //从A得到iValue + B的b + C的c + 自身的d ( 如果没有用virtual 则 的D 中 会有 B.ivalue , C.ivalue ..这里节省了内存开销)


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

虚函数

还是先看代码

class A{public:    void funPrint(){cout<<"funPrint of class A"<<endl;};};class B:public A{public:    void funPrint(){cout<<"funPrint of class B"<<endl;};};void main(){    A *p; //定义基类的指针    A a;    B b;    p=&a;    p->funPrint();    p=&b;    p->funPrint();}
输出结果:

funPrint of class A
funPrint of class A
两次的输出一样!!!!

这跟JAVA的有区别啊。 如果是Java就会输出 

funPrint of class A
funPrint of class A
funPrint of class B

为什么会这样?这就涉及到c++的 静态与动态联编 的东西 。。我这里有篇 http://blog.csdn.net/feng283797821/article/details/9073995

要达到用父类指针 调用 继承类成员效果,那就要用到 virtual 声明该基类的函数, virtual特性不会因为继承而消失,就算继承类对应的成员函数没有用virtual声明

下面是用virtual 的栗子

class A{public:    virtual void funPrint(){cout<<"funPrint of class A"<<endl;};};class B:public A{public:    virtual void funPrint(){cout<<"funPrint of class B"<<endl;};};void main(){    A *p; //定义基类的指针    A a;    B b;    p=&a;    p->funPrint();    p=&b;    p->funPrint();}

 

输出结果:

funPrint of class A
funPrint of class B

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


纯虚函数

与其叫纯虚函数还不如叫抽象类,它只是声明一个函数但不实现它,让派生类去实现它,其实这也很好理解。 

这个跟Java , C#的用abstract 声明的类一样, 只不过 C++没有 abstract 关键字


纯虚函数是一种特殊的虚函数,它的一般格式如下:
class <类名>
{
virtual <类型><函数名>(<参数表>)=0;
};
class Vehicle{public:    virtual void PrintTyre()=0; //纯虚函数是这样定义的};class Camion:public Vehicle{public:    virtual void PrintTyre(){cout<<"Camion tyre four"<<endl;};};class Bike:public Vehicle{public:    virtual void PrintTyre(){cout<<"Bike tyre two"<<endl;};};void main(){    Camion c;    Bike b;    b.PrintTyre();    c.PrintTyre();}
如上代码,定义了一个交通工具类(Vehicle),类中有一函数可打印出交通工具的轮胎个数,但交通工具很多轮胎个数自然也就不确定,所以 就把它定义为纯虚函数,也就是光定义函数名不去实现它,类Camion继承了Vehicle并实现了里面的代码,打印出有4个轮胎。Bike类也是一样。 有一点须要注意一下,纯虚函数不能实化化,但可以声明指针。

总结

    虚基类 
    1, 一个类可以在一个类族中既被用作虚基类,也被用作非虚基类。 
    2, 在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的子对象。 
    3, 虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。 
    4, 最派生类是指在继承结构中建立对象时所指定的类。 
    5, 派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用;如果未列出,则表示使用该虚基类的缺省构造函数。 
    6, 从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生 类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象 只初始化一次。 
    7, 在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。 

    虚函数 
    1, 虚函数是非静态的、非内联的成员函数,而不能是友元函数,但虚函数可以在另一个类中被声明为友元函数。 
    2, 虚函数声明只能出现在类定义的函数原型声明中,而不能在成员函数的函数体实现的时候声明。 
    3, 一个虚函数无论被公有继承多少次,它仍然保持其虚函数的特性。 
    4, 若类中一个成员函数被说明为虚函数,则该成员函数在派生类中可能有不同的实现。当使用该成员函数操作指针或引用所标识的对象时 ,对该成员函数调用可采用动态联编。 
    5, 定义了虚函数后,程序中声明的指向基类的指针就可以指向其派生类。在执行过程中,该函数可以不断改变它所指向的对象,调用不同 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。 纯虚函数 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。

    纯虚函数 
    1, 当在基类中不能为虚函数给出一个有意义的实现时,可以将其声明为纯虚函数,其实现留待派生类完成。 
    2, 纯虚函数的作用是为派生类提供一个一致的接口。 
    3, 纯虚函数不能实化化,但可以声明指针。



原创粉丝点击