中国大学MOOC程序设计与算法(三):C++ 面向对象程序设计 第六周 多态 笔记 之 多态的实现原理

第六周 多态
1.虚函数和多态的基本概念
2.多态实例:魔法门之英雄无敌
3.更多多态程序实例
4.多态的实现原理
5.虚析构函数、纯虚函数和抽象类

4.多态的实现原理

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

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, 1212,16,也可能是其他,有对齐问题

为什么都多了4个字节?多态实现的关键 — 虚函数表。

每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,这个虚函数表是编译器自动生成的,当可执行程序装到内存里的时候,这个类会多一块内存,列出这个类的虚函数地址。当声明这个类的一个对象时,就会自动多出一块4个字节的内存,用来存放一个指针,指向这个类的虚函数表的地址,这块内存位于对象内存的头部。也就是说,对象有额外的4个字节存放一个指向其所属类的虚函数表的地址,访问这个虚函数表就可以得到该类所有虚函数的地址,进而调用该类的虚函数。
中国大学MOOC程序设计与算法(三):C++ 面向对象程序设计 第六周 多态 笔记 之 多态的实现原理_第1张图片
中国大学MOOC程序设计与算法(三):C++ 面向对象程序设计 第六周 多态 笔记 之 多态的实现原理_第2张图片

pBase = pDerived;
pBase->Print();

多态的函数调用语句被编译成一系列根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的指令。
这里的话pBase指向的是对象pDerived,所以,是在对象pDerived的内存中找到指向的虚函数表,再在这个表里找到虚函数Print()的地址,然后调用。这样的结果就是pBase->Print();没有调用pBase所属Base类的虚函数Print(),而是pBase所指向对象pDerived所属Derived类的虚函数Print()。

多态的代价
(1)空间上:每个有虚函数的对象都多出来4个字节指向其所属类的虚函数表地址;
(2)时间上:查虚函数表需要额外的时间开销。

#include 
using namespace std;
class A {
	public: virtual void Func() { cout << "A::Func" << endl; }
};
class B:public A {
	public: virtual void Func() { cout << "B::Func" << endl; }
};
int main() {
	A a;
	A * pa = new B();
	pa->Func();//访问类B的虚函数表地址
	//64位程序指针为8字节
	long long * p1 = (long long * ) & a;//类A的虚函数表地址
	long long * p2 = (long long * ) pa;//类B的虚函数表地址
	* p2 = * p1;//用类A的虚函数表地址替换类B的虚函数表地址
	pa->Func();//访问类A的虚函数表地址
	return 0;
}
输出:
B::Func
A::Func

你可能感兴趣的:(中国大学MOOC程序设计与算法(三):C++ 面向对象程序设计 第六周 多态 笔记 之 多态的实现原理)