多态和虚函数
- 多态性:指不同类型的对象接收相同的消息时产生的不同的行为.
- 消息:主要是指对类的成员函数的调用
- 不同的行为:指成员函数的不同实现
多态概述
- C++中,多态性分为两种:编译时的多态性 和 运行时的多态性.
- 编译时的多态性:通过函数或运算符的重载来实现
- 运行时的多态性:通过虚函数来实现.它指在程序执行之前,根据函数和参数还无法确定应该调用哪一个函数,必须在程序的执行过程中,根据具体的执行情况动态地确定.
- 联编(binding,又称绑定):就是将一个标识符和一个存储地址联系在一起的过程,或是一个源程序经过编译,连接,最后生成可执行代码的过程
- 两种编译方式:静态联编 和 动态联编
- 静态联编:这种联编在编译阶段完成,由于联编过程在程序运行前完成,所有称为早期联编.
- 动态联编:指这种联编要在程序运行时动态进行,所以又称晚期联编
虚函数
#include <iostream>
using namespace std;
class CShape
{
public:
virtual float area() // 将area定义为虚函数
{
return 0.0;
}
};
class CTriangle : public CShape
{
public:
CTriangle(float h,float w)
{
H = h;
W = w;
}
float area()
{
return (float)(H*W*0.5);
}
private:
float H,W;
};
class CCircle:public CShape
{
public:
CCircle(float r)
{
R = r;
}
float area()
{
return (float)(3.14159265*R*R);
}
private:
float R;
};
int main()
{
CShape *s[2]; // 定义基类CShape的指针
s[0] = new CTriangle(3,4); // s[0]指向派生类CTriangle
cout << s[0]->area() << endl;
s[1] = new CCircle(5); // s[1]指向派生类CCircle
cout << s[1]->area() << endl;
return 0;
}
/*
* 6
* 78.5398
*/
该例子通过虚函数,达到了用基类指针访问派生类对象成员函数的目的,从而使一个函数具有多种不同版本
注意
- 虚函数在重新定义时,参数的个数 和 类型都必须和基类中的虚函数完全匹配
只有通过基类指针才能实现虚函数的多态性,若虚函数的调用是通过普通方式来进行的,则不能实现其多态性.
CShape ss; cout << ss.area() << endl; // 输出结果为0.0
如果不使用
new
来创建相应的派生类对象指针,也可通过使用&
运算符来获取对象的地址void main() { CShape *p1,*p2; CTriangle tri(3,4); CCircle cir(5); p1 = &tri; p2 = ○ cout << p1->area() << endl; cout << p2->area() << endl; }
虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态的成员函数.
- 可以把析构函数定义为虚函数,但是不能将构造函数定义为虚函数.通常在释放基类中和派生类中动态申请存储空间时,也要把析构函数定义为虚函数,以便实现撤销对象时的多态性.
纯虚函数和抽象类
- 在定义一个基类时,若出现:无法定义基类中虚函数的具体实现,其实现完全依赖于其不同的派生类.这时,可将基类中的虚函数声明为纯虚函数
- 声明纯虚函数的格式:
virtual <函数类型><函数名>(<形参表>) = 0;
与一般的虚函数相比,多了个”=0”,把函数赋值为0,本质上是将指向函数的指针的初值赋值为0.注意,纯虚函数不能有具体的实现代码 - 抽象类:指至少包含一个纯虚函数的特殊的类.它本身不能被实例化,即不能声明一个抽象类的对象.**必须通过继承得到派生类后,在派生类中定义了纯虚函数的具体实现代码,才能获得一个派生类的对象.
#include <iostream>
using namespace std;
class CShape
{
public:
virtual float area() = 0; // 将area定义为纯虚函数
};
class CTriangle : public CShape
{
public:
CTriangle(float h,float w)
{
H = h;
W = w;
}
float area()
{
return (float)(H*W*0.5); // 在派生类中定义纯虚函数的具体实现代码
}
private:
float H,W;
};
class CCircle:public CShape
{
public:
CCircle(float r)
{
R = r;
}
float area() // 在派生类中定义纯虚函数的具体实现代码
{
return (float)(3.14159265*R*R);
}
private:
float R;
};
int main()
{
CShape *pShape;
CTriangle tri(3,4);
cout << tri.area() << endl;
pShape = &tri;
cout << pShape->area() << endl;
CCircle cri(5);
cout << cri.area() << endl;
pShape = &cri;
cout << pShape->area() << endl;
return 0;
}
/*
* 6
* 6
* 78.5398
* 78.5398
*/
- 与虚函数使用方法相同,纯虚函数也可以声明指向抽象类的指针,但是该指针不能指向任何抽象类的对象(因为不存在),但可以通过该指针获得对派生类成员函数的调用.