多态的实现原理

1、首先,思考一个问题。

“多态”的关键在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定 ---- 这叫“动态联编”。“动态联编” 底是怎么实现的呢?

//运行一段运用多态机制的程序
class Base {
public:
	int i;
virtual void Print() { cout << "Base:Print" ; }
};

class Derived : public Base{
public:
	int n;
	virtual void Print() { cout <<"Drived:Print" << endl; }
};

int main() {
	Derived d;
	cout << sizeof( Base) << ","<< sizeof( Derived ) ;
	return 0;
}
//程序运行输出结果: 8, 12 或:12,16
//(也可能是其他,有对齐问题)

为什么对象比预想的多了四个字节?真的是对齐问题嘛?这是多态实现的关键。

2、多态实现的关键:虚函数表

每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,该类的任何对象中都放着虚函数表的指针(地址)。多出来的4(或者8个)个字节就是用来放虚函数表的地址的。
虚函数表是编译器自动生成的,加到可执行文件中去的。当一个类装入内存时,和这个类关联的虚函数表也被装入了内存中。虚函数表中放着的就是这个类的所有虚函数在内存中的地址。
通过虚函数表的地址可以找到虚函数表,然后通过虚函数的编码(名字)可以找到并调用虚函数。
总之,有了这个表就能找到虚函数函数在内存中的地址。

3、多态是怎么实现的?

pBase = pDerived;
pBase->Print();

多态的函数调用语句被编译成:一系列根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数 的指令。
在这里插入图片描述

4、多态的利弊

(1)优点
有效的提高程序的可扩充性,节省程序员的时间。
(2)缺点
多态程序在运行期间会有额外的时间和空间上的开销。

  • 时间上:查虚函数标。
  • 空间上:每一个有虚函数的对象都会多出4个字节

但是这时一个硬件便宜,人力贵,因此总体来讲是利大于弊。

5、证实有虚函数的类的对象的前4(或者8)个字节存储着虚函数标的地址

#include <iostream>
using namespace std;
class A
{
	public: virtual void Func()
};

class B:public A {
	public: virtual void Func()
};

int main() {
	A a;
	A * pa = new B();
	pa->Func();
	//64位程序指针为8字节
	long long * p1 = (long long* ) & a;
	long long * p2 = (long long* ) pa;
	* p2 = * p1;
	pa->Func();
	return 0;
}
/*输出:
B::Func
A::Func
*/

此例证实了有虚函数的类的对象的前4(或者8)个字节存储着虚函数标的地址。
从此例中也可以看出指针的力量。指针的好处:随便访问内存,针对一个变量,只访问它的一部分。但是没有指针概念的语言只能整体的访问一个变量。
但是指针同时也有其弊端,即使得程序设计变得复杂,容易出错。但从效率上讲,指针会带来很大 好处。这也是为什么C++适合开发系统、驱动程序等的原因。

posted @ 2019-12-08 16:00  江南又一春  阅读(893)  评论(0编辑  收藏  举报