virtual关键字
2015-11-04 22:01
246 查看
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代码。
相关文章推荐
- FOJ 1608 Huge Mission 线段树
- 基带信号、载波信号和宽带信号
- 五猴分桃问题详解
- OC基础知识 -- 类和对象
- 将jeecms中微信后台功能和前台认证以及接收消息的功能,修改为插件形式
- 动态规划求最长递增子序列(longest increasing subsequence)
- hdu2547 无剑无我
- hdoj3975Easy Fruit Ninja
- Tiny6410+K9GAG08U0E
- 修改MySQL默认数据库存放路径
- leetcode-Valid Anagram
- AngularJS API之equal比较对象
- Ubuntu 14.04数据库服务器--mysql的安装和配置
- 《编写有效用例》第二篇阅读体会
- 浅谈 Swift 2 中的 Objective-C 指针
- node在安装完成后,出现node不是内部或外部命令
- BOM
- C++11新特性之 CALLBACKS
- lightoj 1116 - Ekka Dokka 【分解因子】
- lightoj 1078 - Integer Divisibility 【同余】