浅析C++的多态机制

一、     多态机制综述

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.     输出


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值