400 likes | 538 Views
多态性与虚函数. 函数重载与静态联编. 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);. 静态联编与动态联编. 静态联编 : 在编译时就能够确定调用哪个函数 动态联编
E N D
函数重载与静态联编 • 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(); //调用哪个函数?
问题的解决--virtual class base { //...... virtual int function(); }; class derived: public base { //...... int function(); }; pObj->function(); //根据pObj所指的对象正确调用相应函数
虚函数的含义 • 是一个类的成员函数 • 可以为派生类对象使用,是所有对象的通用实现 • 派生类可以通过编写自己的成员函数来替代基类的虚函数,这种替代是基类预见到的、默认的、甚至是赞成的。
虚函数举例 学生(Student) 本科生 (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(); //... };
纯虚函数 • 含义: • 纯虚函数是将要被派生类实现的函数 • 用法: • 具有纯虚函数的类不能实例化 • 派生类如果还有纯虚函数,则还不能实例化 • 能实例化的派生类必须实现所有纯虚函数 • 作用: • 为抽象基类的定义提供了手段 • 为所有派生类设计一个标准接口,方便高层应用逻辑的设计
虚函数的使用 • 好处: 多态性 • 缺点: 增加运行开销 • 每个对象增加一个指针 • 调用虚函数时要查表
虚函数的内部实现 v-table 学生对象 Student::SelectCourse PostGrad::SelectCourse DoctorCand::SelectCourse Student::SelectCourse PostGrad::SelectCourse DoctorCand::SelectCourse name age studentID ...... v-pointer 如果没有虚函数,则对象没有这个特殊成员
虚函数与类层次的关系 A virtual virtual只要在 基类中说明一次 B C D E
虚函数与类层次的关系 virtual关系开始于 说明它的那个类 A B C virtual D E
虚函数与类层次的关系 A *pA; B b; C *pC; D d; pA = &b; pA->func(); //调用A::func() pC = &d; pC->func(); //调用D::func()
小结 • 虚函数与函数重载 • 静态联编与动态联编 • 多态性 • 纯虚函数 • 虚析构函数 • 虚函数的实现
其它虚函数的例子 • 多边形 面积计算 • 电话 拨号方式 • 图形 绘制图形