一、 概念:
在开始介绍多态之前,我们需要先理解几个基本概念。以下我们将做一个简要的介绍。
继承:
继承的基本思想是:一个类和另一个类很相似,只是多了一些扩展。
基类和派生类:
在继承的概念中,我们把原始的那个已经定义过的类叫做基类,继承了基类并且做了一定扩展(如添加新的成员)的类叫做派生类。
多态:
当我们在派生类中对基类的成员函数进行了重载,同时在类的外部定义一个函数,他的参数是基类的指针类型或者引用类型的时候,这个函数也可以处理派生类的指针类型和引用类型的变量,并且可以正确调用派生类中重载之后的成员函数。这种特性就是多态。
二、 实例
对于多态的概念没有实例的说明,是很难让人理解的。下面给出具体的例子:
示例1:
- #include <iostream>
- using namespace std;
- class undergraduate
- {
- public :
- undergraduate(double mid, double final)
- {
- undergraduate::mid = mid;
- undergraduate::final = final;
- };
- void show_grade()
- {
- cout << "mid " << mid << ", final " << final << endl;
- };
- protected:
- double mid;
- double final;
- };
- class postgraduate: public undergraduate
- {
- public :
- postgraduate(double mid, double final, double peper):undergraduate(mid, final)
- {
- postgraduate::peper = peper;
- }
- show_grade()
- {
- cout << "mid " << mid << ", final " << final <<" ,peper " << peper << endl;
- }
- protected:
- double peper;
- };
- void show_result(undergraduate& var)
- {
- var.show_grade();
- }
- int main( )
- {
- undergraduate stu_a(82.0, 85.0);
- postgraduate stu_b(87.2,82.2,85.0);
- stu_a.show_grade();
- stu_b.show_grade();
-
- return 0;
- }
这个实例定义了两个类,其中类postgraduate是类undergraduate的派生类。在这个示例中两个类实例分别调用了自己的成员函数,即对于stu_a调用的是undergraduate中的show_grade函数,stu_b调用的是postgraduate中的show_grade函数。
示例2:
我们在类的外部定义了一个函数,该函数有一个undergraduate引用类型的变量。在main函数中调用show_result之后就会出现调用的成员对象不明确的问题。
- void show_result(undergraduate& var)
- {
- var.show_grade();
- }
- 以下是main()
- int main( )
- {
- undergraduate stu_a(82.0, 85.0);
- postgraduate stu_b(87.2,82.2,85.0);
- show_result(stu_a);
- show_result(stu_b);
-
- return 0;
- }
我们的目的是想让main函数分别调用undergraduate中的show_grade成员函数和postgraduate中的show_grade成员函数。然而实际运行的结果却是两次都调用了undergraduate中的成员函数show_grade()。这是由于系统不能区分参数是基类对象还是派生类的对象导致的结果。
为了让系统可以根据传递给这个函数的对象的实际类型来执行正确的函数调用,引入了多态的概念。对于示例1这种在系统编译的时候就已经确定了需要调用的函数的调用我们称为“静态绑定”,而另外一种允许在函数运行的时候根据其传入的参数的类型来决定调用哪个函数的调用我们称为“动态绑定”。
示例3:
- #include <iostream>
- using namespace std;
- class undergraduate
- {
- public :
- undergraduate(double mid, double final)
- {
- undergraduate::mid = mid;
- undergraduate::final = final;
- };
- virtual void show_grade()
- {
- cout << "mid " << mid << ", final " << final << endl;
- };
- protected:
- double mid;
- double final;
- };
- class postgraduate: public undergraduate
- {
- public :
- postgraduate(double mid, double final, double peper):undergraduate(mid, final)
- {
- postgraduate::peper = peper;
- }
- void show_grade()
- {
- cout << "mid " << mid << ", final " << final <<" ,peper " << peper << endl;
- }
- protected:
- double peper;
- };
- void show_result(undergraduate& var)
- {
- var.show_grade();
- }
- int main( )
- {
- undergraduate stu_a(82.0, 85.0);
- postgraduate stu_b(87.2,82.2,85.0);
- show_result(stu_a);
- show_result(stu_b);
- return 0;
- }
该函数在基类中将show_grade成员函数定义为虚函数(在函数之前加virtual关键字),同时派生类也继承了这个虚函数。这样的设置之后,这个程序运行的结果就可以满足我们的目标了,他将根据传递参数的类型分别调用了不同的函数。这就是多态的实现。
三、 虚析构函数
我们来看一下下面这个示例:
- #include <iostream>
- using namespace std;
- class undergraduate
- {
- public :
- undergraduate(double mid, double final)
- {
- undergraduate::mid = mid;
- undergraduate::final = final;
- };
- virtual void show_grade()
- {
- cout << "mid " << mid << ", final " << final << endl;
- };
- ~undergraduate()
- {
- cout << "destructor undergraduated!" << endl;
- }
- protected:
- double mid;
- double final;
- };
- class postgraduate: public undergraduate
- {
- public :
- postgraduate(double mid, double final, double peper):undergraduate(mid, final)
- {
- postgraduate::peper = peper;
- }
- void show_grade()
- {
- cout << "mid " << mid << ", final " << final <<" ,peper " << peper << endl;
- }
- ~postgraduate()
- {
- cout << "destructor postgraduated!" << endl;
- }
- protected:
- double peper;
- };
- void delete_obj(undergraduate* var)
- {
- delete var;
- }
- int main( )
- {
- postgraduate *ptr = new postgraduate(75.2,70.5,89.0);
- delete_obj(ptr);
- return 0;
- }
在该示例中我们new了一个postgraduate类型的类对象,然后将它删除,这个时候问题出现了。这个操作会执行两个操作:一是调用这个对象的析构函数,二是将包含这个对象的内存释放。然而这个时候调用的析构函数却是类undergraduate的析构函数,而不是postgraduate类的析构函数,也和之前系统无法区别传入对象的类型引起的调用混乱一样。因此我们要用虚函数来解决这个问题,当我们将析构函数定义为虚析构函数的之后,析构函数就会先用postgraduate的析构函数,然后再调用undergraduate的析构函数析构基类对象。
四、 注意
1) 必须定义虚函数,否则将无法通过编译
2) 派生类的构造函数需要根据基类的构造函数来定义。通常如果基类的构造函数没有参数,则派生类的构造函数可以有参数也可以没有。而当基类的构造函数有参数类型的时候,则派生类的构造函数一定要有参数。定义的规则如下
<派生类名>(<参数表>) : <基类名>(<参数表>),
3) 派生类的对象可以传递给以基类对象作为参数的函数。这是因为派生类继承了基类,因此含有基类的部分。而真正传递给函数的也只是派生类中基类的部分,而不是整个派生类。
参考了这篇帖子的部分内容,在此表示感谢:
阅读(2759) | 评论(4) | 转发(2) |