virtual关键字

  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代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值