浅析C++的多态机制
来源:互联网 发布:英文语音朗读软件 编辑:程序博客网 时间:2024/06/05 23:05
一、 多态机制综述
1. 回顾实例
以前在编写C++程序时,我们曾实现过求某个数的绝对值的函数,当时我们做得是重载了三个函数,int fabs(int x), double fabs(double x),float fabs(floatx),那么编译器可以根据我们传入参数的类型来决定到底调用哪一个函数执行。这样我们是不是可以理解为同一种行为有多种表现形式呢?根据对象的类型采取不同的处理方式,这其实就是多态。
2. 多态的本质
通过某种形式的泛化接口,将消息发送给真正需要它的对象。那么如何确定消息到底发送给哪个对象呢?多态分为编译时的多态和运行时的多态,编译时的多态指的是在编译期间,编译器就知道该调用哪个方法或函数,运行时的多态则将消息的发送工作推迟到程序运行期间,根据引用或指针来确定接收消息的对象。
二、 编译时的多态—静态绑定
1. 函数重载
2. 宏多态
#define ADD(a,b)(a)+(b)
int a=1,b=2;
string s1=”aa”,s2=”bb”;
cout<<ADD(a,b);
cout<<ADD(s1,s2);
当程序被编译时,ADD(a,b)和ADD(s1,s2)分别被编译成两个整数相加和两个字符串连接的表达式。
三、运行时的多态—动态绑定
1. 公有继承和虚函数是C++运行时多态的基石
代码实例:
#include <iostream>#include <string>#include <cmath>using namespace std;class Shape{public:virtual double area()=0;};class Point{private:double x;double y;public:Point(double x=0,double y=0){this->x=x;this->y=y;}Point(const Point &other){x=other.x;y=other.y;}Point& operator=(const Point &other){this->x=other.x;this->y=other.y;return *this;}void setX(double x){this->x=x;}void setY(double y){this->y=y;}double getX() const{return x;}double getY() const{return y;}};class Rectangle:public Shape{private:Point left_up; //左上角的点Point right_bottom; //右下角的点double width;double height;void setWidth(){width=fabs(left_up.getX()-right_bottom.getX());}void setHeight(){height=fabs(left_up.getY()-right_bottom.getY());}public:Rectangle(const Point& left,const Point& right){left_up=left;right_bottom=right;setWidth();setHeight();}Rectangle(double left_x,double left_y,double right_x,double right_y){left_up=Point(left_x,left_y);right_bottom=Point(right_x,right_y);setWidth();setHeight();}Rectangle(const Rectangle &other){left_up=other.left_up;right_bottom=other.right_bottom;setWidth();setHeight();}Rectangle& operator=(const Rectangle &other){left_up=other.left_up;right_bottom=other.right_bottom;setWidth();setHeight();return *this;}double area(){return width*height;}};const double PI=3.1415926;class Circle:public Shape{private:Point center;double radius;public:Circle(const Point &c,double r){center=c;radius=r;}Circle(double x=0,double y=0,double r=0){center=Point(x,y);radius=r;}Circle(const Circle &other){center=other.center;radius=other.radius;}Circle& operator=(Circle &other){center=other.center;radius=other.radius;return *this;}double area(){return pow(radius,2)*PI;}};void getAreaSize(Shape *sp){cout<<"面积是:"<<sp->area()<<endl;}void main(){Rectangle r(1.12,2.22,33,41);Circle c(2,3,2);getAreaSize(&r);getAreaSize(&c);}
以上实例中,我们通过公有继承、纯虚函数、指向基类的指针来实现了运行时的多态。
三、 纯虚函数与虚函数
1. 基本形式的对比
virtual 返回类型fun(参数类型参数表)=0; //纯虚函数
virtual 返回类型fun(参数类型参数表) //虚函数
{
// To do Something
}
2. 纯虚函数
(1)纯虚函数相当于接口,没有实现过程。
(2)包含纯虚函数的类称为抽象类,抽象类不能实例化,抽象类的派生类要想实例化,必须实现其基类的纯虚函数,即重新定义该函数。
3. 虚函数
(1)虚函数是非静态的,非内联的。
(2)派生类可以重写(Override)基类的虚函数。
(3)虚函数和公有继承可以实现运行时的多态。
(4)当你使用一个基类指针或引用指向这个基类的公有派生类时,你通过指针或引用调用的基类的虚函数实际是它派生类的重写的版本。
四、 虚函数表 Virtual Table
1. 定义
虚函数表用于存放一个类的虚函数地址,如下图所示:
2. 作用
这张表就像地图一样解决了运行时多态实际要调用的哪个函数来执行的问题。
3. 存放位置
C++编译器确保VT的地址存放于对象实例中所有类元素最前端的位置。
4. 实例
#include <iostream>using namespace std;class Base{public:virtual void f(){cout<<"Base::f()"<<endl;}virtual void g(){cout<<"Base::g()"<<endl;}virtual void h(){cout<<"Base::h()"<<endl;}};class Derive:public Base{public:virtual void f1(){cout<<"Derive::f1()"<<endl;}virtual void g1(){cout<<"Derive::g1()"<<endl;}virtual void h1(){cout<<"Derive::h1()"<<endl;}};void main(){typedef void (*Fun)(void); //Fun代表指向void f(void)函数的指针类型Fun pfun=0; //Derive d;Base b;int i;int VT_addr=*(reinterpret_cast<int*>(&b)); //虚拟表的地址for(i=0;i<3;i++) //调用三个虚函数{int fun_addr=*((reinterpret_cast<int*>(VT_addr))+i);pfun=reinterpret_cast<Fun>(fun_addr);pfun();}}
输出:
五、 VT机制--无虚函数覆盖的一般继承
1. 实例
#include <iostream>using namespace std;class Base{public:virtual void f(){cout<<"Base::f()"<<endl;}virtual void g(){cout<<"Base::g()"<<endl;}virtual void h(){cout<<"Base::h()"<<endl;}};class Derive:public Base{public:virtual void f1(){cout<<"Derive::f1()"<<endl;}virtual void g1(){cout<<"Derive::g1()"<<endl;}virtual void h1(){cout<<"Derive::h1()"<<endl;}};void main(){typedef void (*Fun)(void); //Fun代表指向void f(void)函数的指针类型Fun pfun=0; Derive d;int i;int VT_addr=*(reinterpret_cast<int*>(&d)); //虚拟表的地址for(i=0;i<6;i++) //调用六个虚函数{int fun_addr=*((reinterpret_cast<int*>(VT_addr))+i);pfun=reinterpret_cast<Fun>(fun_addr);pfun();}}
2. 输出:
3. 内存组织形式
六、 VT机制—有虚函数覆盖的一般继承
1. 实例
#include <iostream>using namespace std;class Base{public:virtual void f(){cout<<"Base::f()"<<endl;}virtual void g(){cout<<"Base::g()"<<endl;}virtual void h(){cout<<"Base::h()"<<endl;}};class Derive:public Base{public:void f() //此时f()仍是虚函数{cout<<"Derive::f()"<<endl;}virtual void g1(){cout<<"Derive::g1()"<<endl;}virtual void h1(){cout<<"Derive::h1()"<<endl;}};void main(){typedef void (*Fun)(void); //Fun代表指向void f(void)函数的指针类型Fun pfun=0; Derive d;int i;int VT_addr=*(reinterpret_cast<int*>(&d)); //虚拟表的地址for(i=0;i<5;i++) //调用三个虚函数{int fun_addr=*((reinterpret_cast<int*>(VT_addr))+i);pfun=reinterpret_cast<Fun>(fun_addr);pfun();}}
2. 输出
3. 内存组织形式
4. 运行时的多态
Base *b = newDerive(); //b是指向派生类的指针
b->f(); //此时调用的是派生类的函数
当通过指针b在虚函数表找f()时,找到的是派生类的f(),所以发生了运行时的多态。
七、 VT机制—无虚函数覆盖的多重继承
1. 实例
#include <iostream>using namespace std;class Base1{public:virtual void f(){cout<<"Base1::f()"<<endl;}virtual void g(){cout<<"Base1::g()"<<endl;}virtual void h(){cout<<"Base1::h()"<<endl;}};class Base2{public:virtual void f(){cout<<"Base2::f()"<<endl;}virtual void g(){cout<<"Base2::g()"<<endl;}virtual void h(){cout<<"Base2::h()"<<endl;}};class Base3{public:virtual void f(){cout<<"Base3::f()"<<endl;}virtual void g(){cout<<"Base3::g()"<<endl;}virtual void h(){cout<<"Base3::h()"<<endl;}};class Derive:public Base1,public Base2,public Base3{public:virtual void f1(){cout<<"Derive::f1()"<<endl;}virtual void g1(){cout<<"Derive::g1()"<<endl;}};void main(){typedef void (*Fun)(void);Fun pfun;Derive d;int VT_addrofBase1=*(reinterpret_cast<int*>(&d));int VT_addrofBase2=*(reinterpret_cast<int*>(&d)+1);int VT_addrofBase3=*(reinterpret_cast<int*>(&d)+2);int i;for(i=0;i<5;i++){int fun_addr=*((reinterpret_cast<int*>(VT_addrofBase1))+i);pfun=reinterpret_cast<Fun>(fun_addr);pfun();}for(i=0;i<3;i++){int fun_addr=*((reinterpret_cast<int*>(VT_addrofBase2))+i);pfun=reinterpret_cast<Fun>(fun_addr);pfun();}for(i=0;i<3;i++){int fun_addr=*((reinterpret_cast<int*>(VT_addrofBase3))+i);pfun=reinterpret_cast<Fun>(fun_addr);pfun();}}
2. 输出
3. 内存组织形式
八、 VT机制—有虚函数覆盖的多重继承
1. 实例
#include <iostream>using namespace std;class Base1{public:virtual void f(){cout<<"Base1::f()"<<endl;}virtual void g(){cout<<"Base1::g()"<<endl;}virtual void h(){cout<<"Base1::h()"<<endl;}};class Base2{public:virtual void f(){cout<<"Base2::f()"<<endl;}virtual void g(){cout<<"Base2::g()"<<endl;}virtual void h(){cout<<"Base2::h()"<<endl;}};class Base3{public:virtual void f(){cout<<"Base3::f()"<<endl;}virtual void g(){cout<<"Base3::g()"<<endl;}virtual void h(){cout<<"Base3::h()"<<endl;}};class Derive:public Base1,public Base2,public Base3{public:virtual void f(){cout<<"Derive::f()"<<endl;}virtual void g1(){cout<<"Derive::g1()"<<endl;}};void main(){typedef void (*Fun)(void);Fun pfun;Derive d;int VT_addrofBase1=*(reinterpret_cast<int*>(&d));int VT_addrofBase2=*(reinterpret_cast<int*>(&d)+1);int VT_addrofBase3=*(reinterpret_cast<int*>(&d)+2);int i;for(i=0;i<4;i++){int fun_addr=*((reinterpret_cast<int*>(VT_addrofBase1))+i);pfun=reinterpret_cast<Fun>(fun_addr);pfun();}for(i=0;i<3;i++){int fun_addr=*((reinterpret_cast<int*>(VT_addrofBase2))+i);pfun=reinterpret_cast<Fun>(fun_addr);pfun();}for(i=0;i<3;i++){int fun_addr=*((reinterpret_cast<int*>(VT_addrofBase3))+i);pfun=reinterpret_cast<Fun>(fun_addr);pfun();}}
2. 输出
3. 内存组织形式
4. 运行时多态
Derive d;Base1 *b1=&d;Base1 *b2=&d;Base1 *b3=&d;b1->f(); //调用Derive::f()b2->f(); //调用Derive::f()b3->f(); //调用Derive::f()b1->g() //调用Base1::g()b2->g() //调用Base2::g()b3->g() //调用Base3::g()
九、 VT的安全性--通过基类的指针来访问派生类自己的虚函数
1. 实例
#include <iostream>using namespace std;class Base{public:virtual void f(){cout<<"Base::f()"<<endl;}};class Derive:public Base{public:virtual void f1(){cout<<"Derive::f1()"<<endl;}};void main(){Derive d;
Base *b=&d;
b->f1();}
2. 输出
编译器报错:error C2039: 'f1' : is not a member of'Base'
3.在运行时通过指针来访问
typedef void (*Fun)(void); //Fun代表指向void f(void)函数的指针类型Fun pfun=0; Derive d;int VT_addr=*(reinterpret_cast<int*>(&d)); //虚拟表的地址int fun_addr=*((reinterpret_cast<int*>(VT_addr))+1);pfun=reinterpret_cast<Fun>(fun_addr);pfun();
4. 输出
十、 VT的安全性—访问基类的non-public虚函数成员
1. 实例
#include <iostream>using namespace std;class Base{private:virtual void f(){cout<<"Base::f()"<<endl;}};class Derive:public Base{public:};void main(){typedef void (*Fun)(void); //Fun代表指向void f(void)函数的指针类型Fun pfun=0; Derive d;int VT_addr=*(reinterpret_cast<int*>(&d)); //虚拟表的地址int fun_addr=*((reinterpret_cast<int*>(VT_addr)));pfun=reinterpret_cast<Fun>(fun_addr);pfun();}
2. 输出
- 浅析C++的多态机制
- C++ 多态机制浅析
- C++多态机制浅析
- 多态浅析(C++)
- Android的Binder机制浅析
- oracle的resetlogs机制浅析
- Android的Binder机制浅析
- oracle的resetlogs机制浅析
- Android的Binder机制浅析
- Android的Binder机制浅析
- oracle的resetlogs机制浅析
- 浅析objc的消息机制
- Java 的Event机制浅析
- oracle的resetlogs机制浅析
- oracle的resetlogs机制浅析
- oracle的resetlogs机制浅析
- virtio的eventfd机制浅析
- 浅析java的反射机制
- Android开发 windows中的工程导入到linux中 Eclipse中乱码解决方案
- mac eclipse 阿拉伯语
- Table is marked as crashed and should be repaire 解决方法
- Exception in thread "main" org.hibernate.MappingException: Unknown entity:
- Postgresql函数总结
- 浅析C++的多态机制
- u-boot向linux内核传递启动参数(详细)
- utf与gb2312互转的C代码
- apk安装后不在桌面上生成快捷图标
- new begin
- IE8手动修改搜索提供程序
- 内存管理内幕
- Hadoop权威指南实践--第二章
- 聊天室客户端