多态性可分为运行时的多态性(虚函数重写),和编译时的多态性(重载,包括构造函数多态),今天小编就来说说关于c语言虚函数理解 CC语言16?下面更多详细答案一起来看看吧!
c语言虚函数理解 CC语言16
多态性可分为运行时的多态性(虚函数重写),和编译时的多态性(重载,包括构造函数多态)。
1) 编译时多态性:同一对象在收到相同的消息却产生不同的函数调用,一般通过函数重载来实现,在编译时就实现了绑定,属于静态绑定。
2) 运行时多态性:不同对象在收到相同消息时产生不同的动作,一般通过虚函数来实现。运行时的多态性是通过virtual函数重写和动态绑定实现的。
virtual就是告诉编译器,函数不是使用静态绑定,而是要使用动态绑定。
实现多态主要采用引用和指针。传值这种方式是复制数据,其类型编译器就已决定,而多态是类型要等到执行期才能决定,所以不使用传值方式来实现多态参数传递。
1 多态的作用对于封装,可以使代码模块化。
对于继承,可以扩展已经存在的代码。
而多态,是为了接口重用。同样的功能不要再增加杂七杂八的名字。
2 多态最常见的用法多态最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,即没有利用C 多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。
3 扩展了解隐藏规则本来仅仅区别重载与覆盖并不算困难,但是C 的隐藏规则使问题复杂性陡然增加。
这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
1) 如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏。(注意别和重载混淆)
2) 如果派生类的函数与基类的函数相同,但是基类没有virtual关键字,那么基类的函数将被隐藏。(注意别和覆盖混淆)。
4 多态实现过程动态多态性发生在程序运行期,是动态绑定。动态多态则是通过继承、虚函数、指针来实现。程序在运行时才决定调用的函数,通过父类指针调用子类的函数,可以让父类指针具有多种形态。
4.1 在基类中的某成员函数被声明为虚函数后,在之后的派生类中可以重新来定义它。但定义时,其函数原型、函数名、参数个数、参数类型的顺序,都必须和基类中的原型完全相同。
4.2 必须通过基类指针指向派生类对象,才能通过虚函数实现运行时的多态性。
4.3 虚函数具有继承性,只要在基类中显式声明了虚函数,在派生类中函数名前的virtual可以略去,因为系统会根据其是否和基类中虚函数原型完全相同来判断是不是虚函数。一个虚函数无论被公有继承了多少次,它仍然是虚函数。
4.4 使用虚函数,派生类必须是基类公有派生的。
4.5 虚函数必须是所在类的成员函数,而不能是友元函数,也不能是静态成员函数。因为虚函数调用要靠特定的对象类决定应该激活哪一个函数。
4.6 构造函数不能是虚函数,但析构函数可以是虚函数。
5 多态中的虚函数虚函数只是定义一类相同的行为,通过重载成具体的个体的函数实现个体不同的行为方式。
名字相同,代表共同的行为,但重载后的派生类有了差异性,但由于虚函数的指针有了不同的指向(不同的派生类对象),因而可以产生不同的行为。
虚函数的主要作用是将其类和各个派生类的通用功能抽象出来,实现通用部分,而在派生类中写特定代码。因此,从多个对象抽象出功能交集是非常重要的,而交集中的功能使用虚函数实现。有时基类会为这些虚函数提供一个默认的实现函数。
6 纯虚函数与抽象类为了方便使用多态特性,编程者常常需要在基类中定义虚拟函数。在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出猴子、犀牛等子类, 但动物本身生成对象明显不合常理。
为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数,则编译器要求在派生类中必须予以重载以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。
纯虚函数是指不指定任何实现的函数。如果类只包含纯虚函数,或从纯虚函数继承而来,并且没有提供任何实现,则该类为抽象类。程序中不能创建抽象类对象,只能使用抽象类进行派生,而所有从抽象类派生而来的类必须实现纯虚函数。线虚函数使用纯指示符(=0)声明,代码如下:
virtual CalArea() = 0;
抽象类提供通用功能,但是因为太通用而无法实现具体的功能。
引入抽象基类和纯虚函数的主要目的是为了实现一种接口的效果。在面向对象的编程语言中,为了更好的表示客观世界。所以有些类可以什么都不实现只是提供一个共享的接口。这就是纯虚函数,而含有纯虚函数的基类即为抽象基类。
7 多态性是如何实现程序的可扩展性?派生类是一类特殊的基类,例如本科生、硕士生和博士生都是从学生类派生的。由于所有学生都要写论文,所以基类(学生类)中有一个“写论文”的虚函数。但每类学生写论文的要求是不一样的,因此在本科生、硕士生和博士生类中都有一个对应于虚函数的“写论文”函数。当需要向所有学生布置写论文的任务时,可以用一个学生类的指针遍历所有的学生。由于多态性,每类学生执行的是自己类新增的“写论文”函数,按照自己类规定的要求写论文。如果学校决定要招收工程硕士,那么系统中必须有一个“工程硕士”的类。工程硕士也是一类学生,所以也从学生类派生。工程硕士也要写论文,论文要求和其他几类学生不同,所以工程硕士类也要有一个“写论文”的函数。如果建好了工程硕士这个类,向全校学生布置写论文的工作流程还和以前一样。由此可见,当扩展系统时,只需要增加一些新的类,而主程序不变。
多态的关键在于编译器行为:建立虚函数表、为对象建立虚函数指针。
8 动态多态实例#include <iostream> #include <string> using namespace std; class Graph { protected: double x; double y; public: Graph(double x, double y); virtual void showArea(); }; Graph::Graph(double x,double y) { this->x=x; this->y=y; } void Graph::showArea() { cout<<"couting Graph's Area"<<endl; } class Rectangle:public Graph { public: Rectangle(double x, double y):Graph(x,y){}; void showArea(); }; void Rectangle::showArea() { cout<<"Rectangle's area: "<<x*y<<endl; } class Triangle:public Graph{ public: Triangle(double d, double h):Graph(d,h){}; void showArea(); }; void Triangle::showArea() { cout<<"Triangle's area: "<<x*y*0.5<<endl; } class Circle:public Graph { public: Circle(double r):Graph(r,r){}; void showArea(); }; void Circle::showArea() { cout<<"Circle's Area: "<<3.14*x*y<<endl; } int main() { Graph * g; Rectangle rec(8,5); g = &rec; g->showArea(); Triangle tri(6,5); g = &tri; g->showArea(); Circle cir(2); g = ○ g->showArea(); system("pause"); return 0; } /* Rectangle's area: 40 Triangle's area: 15 Circle's Area: 12.56 */
-end-
,