虚函数
- 类定义中,前面有virtual关键字的成员函数就是虚函数,且只用写在声明中。
- 构造函数和静态成员函数不能使virtual function
多态表现形式一:
- 派生类 指针可以赋给基类指针
- 通过基类指针调用基类和派生类中同名的虚函数时:
- 1.若该指针指向一个基类对象,那么被调用是基类的虚函数。
- 2.若该指针指向一个派生类对象,那么被调用的是派生类的虚函数
- 这种机制叫多态。
多态表现形式二:
- 派生类对象可以赋给基类引用
- 基类引用调用基类和派生类中同名虚函数
-
- 若该引用引用一个基类对象,则调用基类虚函数;
-
- 若该引用引用一个派生类对象,调用派生类虚函数;
-这种机制也就派生。
- 若该引用引用一个派生类对象,调用派生类虚函数;
多态的作用:
能够增强程序的可扩充性,即程序需要修改或增加功能时,需要改动和增加的代码较少。
纯虚函数:
virtual double area() = 0
,不是具体的对象。
用基类指针数组存放指向各种派生类对象的指针,然后遍历该数组,就能对各个派生对对象进行各种操作。
比如一个compare函数,比较多个派生类(可能不同)的某个double值:
Father* arr[100];
....
int compare(const void* s1,const void* s2)
{
double a1,a2;
Father ** p1; //因为s1、s2是void*,所以必须得用另外指针获得内容
Father ** p2;
p1 = (Father * *) s1; //s1、s2都是指向arr数组的元素,元素就是Father*指针,
p2 = (Father * *) s2; //故s1、s2是指向指针的指针,所以(Father**)来强制转换
a1 = (*p1)->area();//(*p1)的类型为Father*,是基类指针
a2 = (*p2)->area();
if(a1<a2)
return -1;
else if(a1>a2)
return 1;
else
return 0;
}
另一个example:
class Base{
public:
void fun1(){fun2();
}
virtual void fun2(){cout<<"base::fun2()\n";
}
};
class Derived:public Base
{
public:
virtual void fun2(){cout<<"derived::fun2()\n";
}
};
int main()
{
Derived d;
Base* ptr = &d;
ptr->fun1();
}
此时输出derived::fun2()
,因为基类指针ptr指向派生类,this也是基类指针,所以this->fun2()
就是调用派生类的fun2();
在构造函数和析构函数中调用虚函数,不是多态。编译时即可确定,调用的函数是自己的类或基类定义的函数,不会等到运行时才决定调用自己的还是派生的函数。
- 派生类和基类中虚函数同名同参数表的函数,不加virtual也是自动成为virtual
class father
{
public:
void hello(){cout<<"hello from your father\n";}
void bye(){cout<<"bye from your father";}
};
class son:public father
{
public:
void hello() {cout<<"hello from your son\n";}
~son() {bye();}
son(){hello();}
};
class grandson:public son
{
public:
void hello() {cout<<"hello from your grandson\n";}
void bye(){cout<<"byr from your grandson\n";}
grandson(){cout<<"constructor from your grandson\n";}
~grandson(){cout<<"destructor from your grandson\n";}
};
int main()
{
grandson s;
son* ptr = &s;
ptr->hello();
return 0;
}
- 首先grandson生成一个对象,则构造函数的顺序是:基类、派生类、派生类2,又基类木有,所以先打印son类的构造函数。son类构造函数调用了hello(),又因为构造函数调用virtual function不会算是多态,所以还是用son的hello函数。
2.然后son类指针,指向grandson,所以掉用grandson的hello()函数,然后析构从grandson开始。然后到son的析构函数,调用一个bye(),又因为son里没有自己的bye,所以使用的是来自father的virtual bye()(这算一个继承下来的成员函数吧)