Version 3.0
多态第九章
2
回顾
继承
访问控制
继承中的构造函数和析构函数
函数覆盖
3
目标
虚函数
纯虚函数
抽象类
动态绑定
虚析构函数
4
虚函数
基类的指针可以指向派生类的对象
但调用的函数却是基类的函数
虚函数可以解决这个问题
5
示例 2-1
#include <iostream.h>
class Shapes
{
public:
void draw() //基类中的函数
{
cout << "绘制基本形状 \n";
}
};
class Circle,public Shapes
{
private:
int radius;
public:
void draw() //在派生类中重新定义
{
cout << "绘制圆形 \n";
}
};
6
示例 2-2
class Square,public Shapes
{
private:
int length;
public:
void draw() //在派生类中重新定义
{
cout << "绘制正方形 \n";
}
};
void main()
{
Circle c;
Square s;
Shapes* ptr;
ptr = &c;
ptr->draw();
ptr = &s;
ptr->draw();
}
调用 Shapes
的 draw()方法
7
虚函数 的定义
希望使用 draw( )函数绘制不同对象
draw( )函数必须在基类中被声明为虚函数
virtual void draw() //基类中的虚函数
{
cout << "绘制基本形状 \n";
}
8
虚函数的覆盖
派生类替换基类提供的虚函数实现
编译器确保调用函数的正确版本
9
演示使用虚函数演示前面的例子
10
注意要点
virtual关键字
虚函数必须在声明它的类中有定义
在派生类中重新定义的虚函数必须具有相同的参数
11
纯虚函数 2-1
基类无法(或没有必要)提供虚函数的实现
将虚函数声明为纯虚函数
virtual void print() = 0;
12
纯虚函数 2-2
派生类要创建对象,必须实现纯虚函数
不能创建含有纯虚函数的类的对象
13
抽象类 2-1
包含纯虚函数的类
只能用作基类
不能创建抽象类的对象
class 类名
{
virtual 类型 函数名 (参数表 ) = 0;
};
14
抽象类 2-2
继承了抽象类而没有提供纯虚函数的实现,
这个类也是抽象类
– 抽象类提供接口而不暴露实现细节
– 在许多商业用途的库和应用程序框架中使用
15
动态绑定 2-1
成员函数调用中代码地址的确定
– 非虚成员函数是根据指向对象的指针的类型静态地(在编译时)选择
– 虚成员函数是动态(在运行时)解析的。根据对象的类型而不是根据指向对象的指针的类型来动态地(在运行时)选择
16
动态绑定 2-2
使用虚函数,系统将使用动态绑定
在程序设计中提供更强的功能和灵活性
处理过程中占用一些开销
17
虚析构函数 2-1
调用析构函数是为了释放由构造函数分配的内存空间
如果基类的析构函数是非虚的,则不能用指向派生类的指针调用派生类的析构函数
需要虚析构函数
18
虚析构函数 2-2
虚函数绑定到与对象所属的类,而不是与指针所对应的类
派生类的实例总是包含一个基类的实例
需要调用这两个类的析构函数以确保所有空间都被释放
注意:构造函数不能是虚函数
19
示例 2-1
#include <iostream.h>
class Alpha
{
private:
char* alpha_ptr;
public:
Alpha() //构造函数不能是虚函数
{
alpha_ptr = new char[5];
}
virtual ~Alpha() //虚析构函数
{
delete[] alpha_ptr;
cout << "Alpha的析构函数 " << endl;
}
};
20
示例 2-2
class Beta,public Alpha
{
private:
char* ptrderived;
public:
Beta()
{
ptrderived = new char[100];
}
~Beta()
{
delete[] ptrderived;
cout << "Beta的析构函数 " << endl;
}
};
void main()
{
Alpha *ptr = new Beta;
delete ptr;
}
21
总结
虚函数
纯虚函数
抽象类
动态绑定
虚析构函数