virtual关键字

来源:互联网 发布:淘宝店铺扣48分 编辑:程序博客网 时间:2024/09/21 06:33
  virtual的使用依托于类。

1、虚函数
被virtual修饰的成员函数称为虚函数。虚函数对于多态具有决定性的作用,有虚函数才能构成多态。我们知道,基类指针可以指向派生类对象(但是基类指针只能调用基类方法),但是派生类指针不能指向基类对象(为了避免派生类指针调用了基类中没有的派生类方法)。在基类中,被virtual修饰的成员函数(如print)在派生时,如果在派生类重写了该方法print,那么在调用该方法print的时候,将是根据对象的实际类型来决定是调用基类还是派生类中的该方法print。如下:

class Base{public:    virtual void print()    {        cout << "Base print" << endl;    }};class Derive : public Base{public:    void print()    {        cout << "Derive print" << endl;    }};int main(){    Derive d;    Base * b = &d;    b->print();//输出 "Derive print"     return 0;}

测试结果表明:在调用print的时候,基类指针b实际指向的对象是什么类型(Derive),就调用哪个类型的print(Derive中的)。下面是一个更形象的例子说明,virtual所支持的多态性。

#include <iostream>using namespace std;//军队class Troops{public:    virtual void fight(){ cout<<"Strike back!"<<endl; }};//陆军class Army: public Troops{public:    void fight(){ cout<<"--Army is fighting!"<<endl; }};//99A主战坦克class _99A: public Army{public:    void fight(){ cout<<"----99A(Tank) is fighting!"<<endl; }};//武直10武装直升机class WZ_10: public Army{public:    void fight(){ cout<<"----WZ-10(Helicopter) is fighting!"<<endl; }};//长剑10巡航导弹class CJ_10: public Army{public:    void fight(){ cout<<"----CJ-10(Missile) is fighting!"<<endl; }};//空军class AirForce: public Troops{public:    void fight(){ cout<<"--AirForce is fighting!"<<endl; }};//J-20隐形歼击机class J_20: public AirForce{public:    void fight(){ cout<<"----J-20(Fighter Plane) is fighting!"<<endl; }};//CH5无人机class CH_5: public AirForce{public:    void fight(){ cout<<"----CH-5(UAV) is fighting!"<<endl; }};//轰6K轰炸机class H_6K: public AirForce{public:    void fight(){ cout<<"----H-6K(Bomber) is fighting!"<<endl; }};int main(){    Troops *p = new Troops;    p ->fight();    //陆军    p = new Army;    p ->fight();    p = new _99A;    p -> fight();    p = new WZ_10;    p -> fight();    p = new CJ_10;    p -> fight();    //空军    p = new AirForce;    p -> fight();    p = new J_20;    p -> fight();    p = new CH_5;    p -> fight();    p = new H_6K;    p -> fight();    return 0;}
实验结果:

Strike back!
–Army is fighting!
—-99A(Tank) is fighting!
—-WZ-10(Helicopter) is fighting!
—-CJ-10(Missile) is fighting!
–AirForce is fighting!
—-J-20(Fighter Plane) is fighting!
—-CH-5(UAV) is fighting!
—-H-6K(Bomber) is fighting!

2、虚析构函数
实例化一个派生类对象的时候,首先将实例化基类对象,然后再实例化派生部分。同样,在销毁一个对象的时候,也希望首先调用派生类的析构函数,然后再调用基类的析构函数。如下:

//构造顺序和析构顺序class Base{public:    Base()    {        cout << "Base" << endl;    }    ~Base()    {        cout << "Base del" << endl;    }};class Derive : public Base{public:    Derive() :Base()    {        cout << "Derive" << endl;    }    ~Derive()    {        cout << "Derive del" << endl;    }};int main(){    Derive d;    return 0;}

//结果:
Base
Derive
Derive del
Base del
但是,如果使用new运算符建立一个派生类临时对象(如Derive),并将该对象与基类指针关联时,那么在用delete回收动态内存的时候,将会发生一个情况:系统会只执行基类的析构函数,而不执行派生类的析构函数。
即根据上述(1、虚函数)的理论,在销毁动态内存中的派生类对象时,基类指针将调用基类的析构函数。如下:

//未用virtual修饰的基类析构函数class Base{public:    Base()    {        cout << "Base" << endl;    }     ~Base()    {        cout << "Base del" << endl;    }};class Derive : public Base{public:    Derive() :Base()    {        cout << "Derive" << endl;    }    ~Derive()    {        cout << "Derive del" << endl;    }};int main(){    Derive * d = new Derive;    delete d;    return 0;}

结果:
Base
Derive
Base del
但是如果用virtual来修饰析构函数,那么基类指针将首先调用派生类的析构函数,再调用基类的析构函数。

class Base{public:    Base()    {        cout << "Base" << endl;    }    virtual ~Base()    {        cout << "Base del" << endl;    }};class Derive : public Base{public:    Derive() :Base()    {        cout << "Derive" << endl;    }    ~Derive()    {        cout << "Derive del" << endl;    }};int main(){    Derive * d = new Derive;    delete d;    return 0;}

//结果:
Base
Derive
Derive del
Base del
基类的析构函数如果不用virtual修饰,则编译器将采用静态联编的方式,根据b的指针类型,调用相应类型的析构函数。而加上virtual后,则其后派生出来的所有类中的析构函数都为虚拟析构函数,此时编译器将采用动态联编的方式,即根据b指针实际指向的类型来决定它所调用的析构函数 。

总之,最好把基类的析构函数声明为虚函数,那么从该基类所派生的所有派生类的析构函数也都自动成为虚函数,即使派生类的析构函数与基类的析构函数名字不同。但是构造函数不能声明为虚函数。这是因为在执行构造函数时,类对象还未完成建立过程,当然谈不上函数与类对象的绑定。

3、纯虚函数
一种特殊的虚函数。在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。纯虚函数让类先具有一个操作名称,而没有操作内容,让派生类在继承时再去具体地给出定义。定义示例如下:

class Base{public:    virtual void print() = 0;//声明的后面加上“=0”注释表示此函数为纯虚函数};

含有纯虚函数的类叫做抽象类。这种类不能实例化对象,只能作为基类为派生类服务。除非在派生类中完全实现基类中的所有纯虚函数,否则,派生类也是抽象类,不能实例化对象。

4、虚拟继承
即在继承的时候,除了指定公有/ 私有继承之外,可以另外通过virtual关键字来说明,在基类的多份继承中,将共享一份基类。如下:

class Base{public:    Base(){}};class Derive1 : virtual public Base{public:    Derive1() :Base(){}};class Derive2 : virtual public Base{public:    Derive2() :Base(){}};

通过指定virtual继承,Derive1和Derive2将共享一份Base代码。如果未指定virtual继承,那么Derive1和Derive2将分别拥有一份Base代码。

0 0