1、虚函数 virtual
虚函数可以参与多态,而普通函数不能。
class base { virtual int get() ; }; int base::get(){ }
Note:
virtual关键字只用在类定义的函数声明里,写函数体时不用。
构造函数和静态成员函数不能是虚函数。
2、多态:增强程序可扩充性
(1)派生类的指针可以赋给基类指针
通过基类指针调用基类和派生类中的同名虚函数时:
若该指针指向一个基类的对象,那么被调用是基类的虚函数;
若该指针指向一个派生类的对象,那么被调用的是派生类的虚函数。
(2)派生类的对象可以赋给基类引用
通过基类引用调用基类和派生类中的同名虚函数时:
若该引用引用的是一个基类的对象,那么被调用是基类的虚函数;
若该引用引用的是一个派生类的对象,那么被调用的是派生类的虚函数。
Note:用基类指针数组存放指向各种派生类对象的指针,然后遍历该数组,就能对各个派生类对象做各种操作,是很常用的做法。
注意,Attention!见下面程序:
class Base { public: void fun1() { fun2(); } virtual void fun2() { cout << "Base::fun2()" << endl; } }; class Derived:public Base { public: virtual void fun2() { cout << "Derived:fun2()" << endl; } }; int main() { Derived d; Base * pBase = & d; pBase->fun1(); return 0; }
输出:
Derived:fun2()
原因:在非构造函数,非析构函数的成员 函数中调用虚函数,是多态!!!
class Base { public: void fun1() { this->fun2(); } //this是基类指针,fun2是虚函数,所以是多态 virtual void fun2() { cout << "Base::fun2()" << endl; } }; class Derived:public Base { public: virtual void fun2() { cout << "Derived:fun2()" << endl; } }; int main() { Derived d; Base * pBase = & d; pBase->fun1(); return 0; }
在构造函数和析构函数中调用虚函数,不是多态。
在派生类中和基类中虚函数同名同参数表的函数,不加virtual也自动成为虚函数。
3、多态实现的关键:虚函数表
每个对象虚函数表的存放以及虚函数表的查询过程会分别增加程序的空间开销和时间开销。
4、虚析构函数
class CSon{ public: ~CSon() { }; }; class CGrandson : CSon{ public: ~CGrandson() { }; } int main(){ CSon *p = new CGrandson; delete p; return 0;
在delete p时只调用了基类的析构函数,没有调用派生类的析构函数,可能造成内存空间无法回收。
解决方法:把基类的析构函数声明为virtual
• 派生类的析构函数 virtual可以不进行声明
• 通过基类的指针删除派生类对象时,首先调用派生类的析构函数,然后调用基类的析构函数
Note:类如果定义了虚函数, 则最好将析构函数也定义成虚函数。注意,不允许以虚函数作为构造函数。
class son{ public: virtual ~son() { cout<<"bye from son"<<endl; }; }; class grandson : public son{ public: ~grandson(){ cout<<"bye from grandson"<<endl; }; }; int main(){ son *pson; pson=new grandson; delete pson; return 0; }
5、抽象类
(1)纯虚函数:没有函数体的虚函数。
class A { private: int a; public: virtual void Print( ) = 0 ; //纯虚函数 void fun() { cout << “fun”; } };
(2)抽象类:包含纯虚函数的类。
只能作为积累来派生新类使用;不能创建抽象类的对象;抽象类的指针和引用可以指向由抽象类派生出来的类的对象。
A a; // 错, A 是抽象类, 不能创建对象 A * pa; // ok, 可以定义抽象类的指针和引用 pa = new A; //错误, A 是抽象类, 不能创建对象
抽象类中,成员函数可以调用纯虚函数,但构造函数/析构函数内部不能调用纯虚函数。
如果一个类从抽象类派生而来,它必须实现基类中的所有纯虚函数,才能成为非抽象类。