多态的实现原理
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++适合开发系统、驱动程序等的原因。