继承与多态6:多态与虚函数
来源:互联网 发布:软件测试薪水 编辑:程序博客网 时间:2024/06/03 17:00
多态与虚函数
1. 多态性
一个函数可有多种实现形式。同一个函数可通过多种方法进行调用(或用一个相同的名字定义具有不同实现的函数)。由于1)派生类中同名函数的出现;2)基类指针可存放派生类对象地址和基类引用可作为派生类对象的别名,导致了派生类的多态。C++中支持两种多态性,即静态多态和动态多态。静态多态(也称为静态绑定static binding):编译时的多态性,即在编译过程中决定了函数的确切调用,也叫静态联编。如根据基类指针的类型在编译时确定调用基类的函数。如函数重载和运算符重载,编译系统根据重载函数的参数个数、类型及顺序的差别,在编译时就确定了程序中函数的调用与哪个函数绑定。动态多态(也称为动态绑定dynamic binding):在运行时确定调用哪个函数称为动态绑定。在程序执行前,无法根据函数名和参数来确定该调用哪一个函数,必须在程序运行时根据具体情况来动态的确定调用哪个函数,这是在运行过程中发生的, 编译系统在编译时是无法确定的。
2. 虚函数
为使类中的一个成员函数能动态绑定,需要做两件事情(缺一不可):1)在基类中,函数必须声明为虚函数(virtual function);
使用关键字virtual声明的函数是虚函数。virtual只能出现在类声明中,在类实现中不可再加virtual关键字。在派生类中定义与基类的虚函数同名的函数,它仍为虚函数(当类中的某个成员函数被定义为虚函数后,则在该类所有的派生类中,该函数始终保持虚函数的特征,不必在派生类的函数声明中再使用关键字virtual。)虚函数必须是类的非静态成员函数。
2)必须使用基类的指针变量或引用形式调用该虚函数:基类指针指向基类对象和派生类对象时;基类引用作为基类对象和派生类对象的别名时(引用)。例如:已知,Circle类中的getArea()函数为虚函数,则:
- Circle *p;
- Cylinder t(1,2);
- p=&t;
- cout<<p->getArea() ; //输出圆柱体的表面积
- Circle &rc=t;
- cout<<rc.getArea(); //输出圆柱体的表面积
虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其内容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当用基类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一样,指明了实际所应该调用的函数 。下面举例说明虚函数的作用:
- #include <iostream>
- using namespace std;
- class A
- {
- public:
- virtual void disp()//虚函数定义
- {
- cout<<"class A"<<endl;
- }
- };
- class B: public A
- {
- public:
- void disp()//派生类声明中,不用再加关键字virtual,但它仍然是虚函数。
- {
- cout<<"class B"<<endl;
- }
- };
- class C: public B
- {
- public:
- void disp()
- { cout<<"class C"<<endl;}
- void show()
- { cout<<"class CC"<<endl;}
- };
- void display(A *x )//指针形式调用虚函数
- {
- x->disp();
- }
- void main()
- {
- A a;
- B b;
- C c;
- display(&a);
- display(&b);
- display(&c);
- }
如果 没有virtual关键字的声明,由于B,C是A的派生类,而display()函数的参数是A类的指针,此时为静态绑定,会显示3个class A;相反地如果带有virtual关键字,动态绑定,会显示class A,class B, class C。下面再举一例,以Circle类为基类公有派生出Cylinder类和Sphere类:
- //circle.h
- #ifndef CIRCLE_H
- #define CIRCLE_H
- class Circle
- {
- protected:
- double radius;
- public:
- Circle(double r);
- virtual double getArea();//虚函数声明
- double getRadius();
- void setRadius(double);
- double getPerimeter();
- };
- #endif
- // cylinder.h文件
- #include "circle.h"
- class Cylinder: public Circle
- {
- protected:
- double height;
- public:
- Cylinder();
- Cylinder(double h);
- Cylinder(double r,double h);
- double getArea();//虚函数,不带virtual关键字
- double getVolume();
- double getHeight();
- void setHeight (double h);
- };
- // sphere.h文件,Sphere类的定义
- #include "circle.h"
- class Sphere: public Circle
- {
- public:
- Sphere():Circle(1)
- {}
- Sphere(double r):Circle(r)
- {}
- double getArea()//虚函数的实现
- {
- return 4*Circle::getArea();
- }
- double getVolume()
- {
- return 4*Circle::getArea()*radius/3;
- }
- };
- //circle.cpp
- #include "circle.h"
- Circle::Circle(double r)
- {
- radius= r;
- }
- double Circle::getArea()//虚函数的定义
- {
- return radius* radius *3.14159;
- }
- double Circle::getPerimeter ()
- {
- return 2*3.14159*radius;
- }
- double Circle::getRadius()
- {
- return radius;
- }
- void Circle::setRadius(double r)
- {
- radius=r;
- }
- //cylinder.cpp文件,Cylinder类的实现
- #include "Cylinder.h"
- Cylinder::Cylinder():Circle(1)
- { height=1; }
- Cylinder::Cylinder(double h) :Circle(1)
- { height=h; }
- Cylinder::Cylinder(double r,double h):Circle (r)
- { height=h;}
- double Cylinder::getHeight()
- { return height;}
- void Cylinder::setHeight(double h)
- { height=h;}
- double Cylinder::getArea()//虚函数的实现
- { return 2*Circle::getArea()+getPerimeter()*height; }
- double Cylinder::getVolume()
- { return Circle::getArea()*height; }
- //test.cpp
- #include <iostream>
- using namespace std;
- #include "cylinder.h"
- #include "sphere.h"
- void main()
- {
- Circle *p;
- Cylinder d(1.5,10);
- Sphere s(2);
- p=&d;
- cout<<p->getArea()<<endl;
- p=&s;
- cout<<p->getArea();
- //cout<<p->getVolume();//error: class circle没有getVolume()成员
- }
注:类的对象不能实现运行时多态,只能用对象指针或对象引用形式。
3. 虚函数总结
虚函数是一种类似于函数重载的方法,用于实现动态绑定。什么样的成员函数应声明为虚函数?如果一个基类中定义的函数需要在派生类中重定义,应该将其声明为虚函数,以实现不同功能,从而避免混淆和错误(如Circle类的getArea()函数)。另一方面,如果一个函数不会被重定义,将其声明为非虚函数会更有效(如Circle类的getRadius(), setRadius()等),因为在运行时动态绑定虚函数会花费更多的时间和系统资源。虚析构函数:如果一个基类的析构函数被说明为虚函数,则它的派生类中的析构函数是虚析构函数,不管它是否使用了virtual关键字。注意:构造函数不能声明为虚函数。指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。
多态还有个关键之处就是一切用指向基类的指针或引用来操作对象
0 0
- 继承与多态6:多态与虚函数
- 理解继承、虚函数与多态
- 虚函数与多继承
- 多继承与虚函数
- 多继承与虚函数
- 继承与多态(五):虚复制构造函数
- 多态与虚继承
- 继承与多态
- 继承与多态
- 继承与多态
- 继承与多态
- 继承与多态
- 继承与多态
- 继承与多态
- 继承与多态
- 继承与多态
- 继承与多态
- 继承与多态
- BITACM 1046 Leftmost Digit
- Unity3d VBO 上升太快导致卡死
- Android开发规范
- 第一周项目1
- [ACM]最长回文子串
- 继承与多态6:多态与虚函数
- 今天运行代码过程中解决的几个问题
- 练手小程序3.按给定的字母顺序打印字符串
- HDU(2057)十六进制数相加
- 引用与指针
- c++ 虚函数表工作原理
- Yii 联表查询
- 使用Perfmon和PAL工具查看Server性能--PerfMon入门指南
- input元素属性type类型