多态性与虚函数
DESCRIPTION
多态性与虚函数. 函数重载与静态联编. void print(char) void print(int) void print(float) void print(char *) …… print("Hello, overload!");. 函数重载与静态联编. class MyClass { public: MyClass(); MyClass(int i); MyClass(char c); ); MyClass c2(34);. 静态联编与动态联编. 静态联编 : 在编译时就能够确定调用哪个函数 动态联编 - PowerPoint PPT PresentationTRANSCRIPT
多态性与虚函数
函数重载与静态联编• void print(char)
• void print(int)
• void print(float)
• void print(char *)
• ……
• print("Hello, overload!");
函数重载与静态联编class MyClass {
public:MyClass();MyClass(int i);MyClass(char c);
);
MyClass c2(34);
静态联编与动态联编
• 静态联编 :– 在编译时就能够确定调用哪个函数
• 动态联编– 在运行时才能够动态确定调用哪个函数
动态联编的基础• C++ 允许指向基类的指针可以指向其派生
类的对象 (down casting)
base *pObj; //base 是一个基类derived derivedObj; //derived 是 base 的派生类
pObj = &derivedObj; // 基类指针可以指向派生类对象
down casting 带来的问题class base {
//...... int function();};class derived: public base {
//......int function();
};pObj->function(); // 调用哪个函数 ?
问题的解决 --virtualclass base {
//...... virtual int function();};class derived: public base {
//...... int function();
};pObj->function(); // 根据 pObj 所指的对
象正确调用相应函数
虚函数的含义• 是一个类的成员函数
• 可以为派生类对象使用,是所有对象的通用实现
• 派生类可以通过编写自己的成员函数来替代基类的虚函数,这种替代是基类预见到的、默认的、甚至是赞成的。
虚函数举例学生( Studen
t)
本科生(UndeGrad)
研究生(PostGrad)
硕士研究生(MastCand)
博士研究生(DoctCand)
class Student {
protected:
char name[30];
int age;
//......
public:
virtual void SelectCourse(); // 选课virtual int CalculateCredit(); // 计算总学分//...
};
//UndeGrad 没有重新实现两个虚函数,// 它使用基类的实现class UndeGrad: public Student {
//......public:
void Practice(); // 工程实践//...
};
//PostUndeGrad 重新实现了两个虚函数class PostGrad: public Student {
//......int superVisorID; // 导师
public:void SelectCourse(); // 选课int CalculateCredit(); // 计算总学分//...
};
//MasterCand 使用 PostGrad 提供的虚函数class MasterCand: public PostGrad {
//......
public:
//...
};
//DoctorCand 重新实现了两个虚函数class DoctorCand: public PostGrad {
//......
public:
void SelectCourse(); // 选课int CalculateCredit(); // 计算总学分
//...
};
void main()
{
Student * s;
// 程序运行过程中动态创建学生对象,可能是以下各种语句// s = new UndeGrad ();
// s = new PostGrad ();
// s = new DoctCand ();
s = new MasterCand ();
s->SelectCourse(); // 调用 PostGrad::Selectcourse()
}
virtual 的含义• virtual 只在类继承时才发挥作用
• virtual 告诉编译器,它所修饰的函数需要动态联编( dynamic binding )。
•在调用该函数时,需要根据对象的实际类型决定使用类继承层次中哪个类的成员函数
使用虚函数的例子// students 存放所有学生的信息
Student * students[300]; // 指针数组int studNum; // 学生数目
// 在程序运行过程中动态建立了 num 个学生对象//students[studNum++] = new DoctCand();
//students[studNum++] = new UndeGrad();
//......
使用虚函数的例子// 所有学生选课void StudentSelectCourse(){
for(int i = 0; i < studNum; i++)students[i]->SelectCourse()
}
void StudentSelectCourse(){
for(int i = 0; i <studNum; i++) { switch (student[i]->type) {
case GRAD: // 本科生 student[i]->UndeGrad::SelectCourse(); break; case POST: //研究生
student[i]->PostGrad::SelectCourse(); break; case DOCT: // 博士生
student[i]->DoctCand::SelectCourse(); break;//......
}
不用虚函数机制的程序非常复杂
虚函数带来的好处• 简化程序
– 可以编写为多种不同对象类型正确工作的代码
• 方便维护– 类层次的修改、实现的修改产生的影响小
• 是多态性的语言实现
多态性• 含义:
– 一个对象在其生存期内可以具有多种形态– 一种调用可以有多种执行方式
• 作用:– 让使用和实现分开,实现了一定的封装– 简化了程序设计,高层操作和低层操作分开
多态性的好处void StudentSelectCourse(){
for(int i = 0; i < studNum; i++)students[i]->SelectCourse()
}
复杂的实现
特殊的虚函数
• 虚析构函数– 保证了对象的正确释放
• 纯虚函数– 必须由派生类定义的虚函数
析构函数
void RemoveAllStudents(){
for(int i = 0; i < studNum; i++) delete students[i];
}调用析构函数
Student::~Student()
虚析构函数class UndeGrad : Student {
//...char * somePersonalInfo;
public:UndeGrad();~UndeGrad();//...... 其它操作函数
};
派生类中动态分配了空间
虚析构函数class Student {
//...
public:
Student();
virtual ~Student();
//...... 其它操作函数};
定义虚析构函数
虚析构函数
void RemoveAllStudents(){
for(int i = 0; i < studNum; i++) delete students[i];
}调用正确的析构函数
虚析构函数• 作用:
– 保证调用正确的析构函数– 保证对象的正确释放– 保证系统资源的合理使用
• 注意:– 构造函数不能是虚函数– 析构函数可以(而且应该)定义为虚函数
纯虚函数class Abstract_Student {
protected:
char name[30];
int age;
//......
public:
virtual void SelectCourse() = 0;
virtual int CalculateCredit() = 0;
//...
};
//UndeGrad 必须实现基类的纯虚函数class UndeGrad: public Abstract_Student {
//......public:
void SelectCourse() ; int CalculateCredit();//...
};
//PostGrad 可以保持两个纯虚函数的定义class Abstract_PostGrad: public Abstract_Student {
//......int superVisorID; // 导师
public:void SelectCourse() = 0;int CalculateCredit() = 0 ;//...
};
//DoctorCand 必须实现两个虚函数class DoctorCand: public Abstract_PostGrad {
//......
public:
void SelectCourse();
int CalculateCredit();
//...
};
纯虚函数• 含义 :– 纯虚函数是将要被派生类实现的函数
• 用法 :– 具有纯虚函数的类不能实例化– 派生类如果还有纯虚函数,则还不能实例化– 能实例化的派生类必须实现所有纯虚函数
• 作用 :– 为抽象基类的定义提供了手段– 为所有派生类设计一个标准接口,方便高层应用逻辑的设计
虚函数的使用
•好处: 多态性
•缺点: 增加运行开销– 每个对象增加一个指针– 调用虚函数时要查表
虚函数的内部实现
nameagestudentID......v-pointer
Student::SelectCoursePostGrad::SelectCourseDoctorCand::SelectCourseStudent::SelectCoursePostGrad::SelectCourseDoctorCand::SelectCourse
v-table学生对象
如果没有虚函数,则对象没有这个特殊成员
虚函数与类层次的关系
A
B C
D E
virtual 只要在基类中说明一次
virtual
虚函数与类层次的关系virtual 关系开始于说明它的那个类A
B C
D E
virtual
虚函数与类层次的关系A *pA;B b;C *pC;D d;pA = &b;pA->func(); // 调用 A::func()pC = &d;pC->func(); // 调用 D::func()
小结• 虚函数与函数重载• 静态联编与动态联编• 多态性• 纯虚函数• 虚析构函数• 虚函数的实现
其它虚函数的例子• 多边形 面积计算• 电话 拨号方式• 图形 绘制图形