一、虚函数的作用和用法
在使用父类指针指向子类对象时,常常需要调用子类的特有函数。但是由于父类指针的类型限制,不能直接访问子类中定义的成员函数。可是使用虚函数,就可以让父类指针调用子类的特有函数了。
虚函数是通过在函数声明前加上virtual关键字表示,并且通常在父类中实现一个虚函数,子类继承该函数后,如果定义了与父类中函数名、参数、返回值都一致的虚函数,就直接覆盖了父类的虚函数。这样,在通过父类指针引用子类对象时,调用虚函数实际执行的是子类中的虚函数。
下面是关于多态的例子:
class Shape{ public: virtual double getArea(){ return -1; //Shape类中的纯虚函数 } }; class Circle:public Shape{ int r; public: Circle(int r=0):r(r){}; double getArea(){ return 3.14*r*r; } }; class Rectangle:public Shape{ int w; int h; public: Rectangle(int w=0,int h=0):w(w),h(h){}; double getArea(){ return w*h; } }; int main(){ Shape* shape1 = new Circle(5); //通过父类指针引用子类对象 Shape* shape2 = new Rectangle(3,4); cout<getArea()< getArea()< 二、抽象类和纯虚函数
虚函数在父类中并没有实现,但是也可以定义为纯虚函数。纯虚函数就是只有函数声明,没有函数实现的虚函数,它的格式为:virtual 返回类型 函数名() = 0; 抽象类也是一种使用纯虚函数的方法。抽象类是无法被实例化的类,只能被继承来实例化。
在上一个例子中,Shape类中的getArea()函数就是一个纯虚函数。这种情况下,Shape类就成为了一个抽象类。如果删除了圆形和矩形类中的getArea()函数,那么这两个类就不能实例化了。
三、动态类型转换和typeid
有时候在编程过程中,要对父类指针进行转换成子类指针,这个过程就是动态类型转换。要想动态类型转换成功,需要保证父类指向的实际是子类对象。
使用动态类型转换需要涉及到typeid关键字,typeid运算符返回的是一个类型信息的对象,可以使用返回的信息来判断类型是否匹配。使用基本的类型名判断在面对多重继承和虚继承的情况下会出现问题,所以应该使用type_info来实现动态类型转换。
下面是关于动态类型转换的例子:
class Shape{ public: virtual void print(){ cout<<"This is a Shape"<print(); Circle* circle = dynamic_cast (shape); if(circle!=NULL){ circle->print(); }else{ cout<<"Null pointer"< 四、虚基类
在多重继承的时候,如果某个子类同时继承了两个基类,那么在父类指针转换成子类指针的时候就会出现问题。这种情况下,虚继承就可以帮助我们解决问题。虚继承可以避免每个基类都拥有自己的一份拷贝,有效节省了内存空间,同时也解决了指针转换的问题。
class A{ public: int a; }; 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; }; int main(){ D* d = new D(); A* a = d; return 0; }