您的位置:首页 > 编程语言 > C语言/C++

C++中多态的实现原理

2014-06-06 08:51 519 查看
http://www.cnblogs.com/dongzhiquan/archive/2011/10/25/2223251.html

多态是面向对象的基本特征之一。而虚函数是实现多态的方法。那么virtual function到底如何实现多态的呢?

1 基类的内存分布情况

请看下面的sample

class A

{

void g(){.....}

};

则sizeof(A)=1;

如果改为如下:

class A

{

public:

virtual void f()

{

......

}

void g(){.....}

}

则sizeof(A)=4! 这是因为在类A中存在virtual function,为了实现多态,每个含有virtual function的类中都隐式包含着一个静态虚指针vfptr指向该类的静态虚表vtable, vtable中的表项指向类中的每个virtual function的入口地址

例如 我们declare 一个A类型的object :

A c;

A d;

则编译后其内存分布如下:





从 vfptr所指向的vtable可以看出,每个virtual function都占有一个entry,例如本例中的f函数。而g函数因为不是virtual类型,故不在vtable的表项之内。说明:vtab属于类成员静态pointer,而vfptr属于对象pointer

2 继承类的内存分布状况

假设代码如下:

public B:public A

{

public :

int f() //override virtual function

{

return 3;

}

};



A c;

A d;

B e;

编译后,其内存分布如下:





从中我们可以看出,B类型的对象e有一个vfptr指向vtable address:0x00400030 ,而A类型的对象c和d共同指向类的vtable address:0x00400050a

3 动态绑定过程的实现

我们说多态是在程序进行动态绑定得以实现的,而不是编译时就确定对象的调用方法的静态绑定。

其过程如下:

程序运行到动态绑定时,通过基类的指针所指向的对象类型,通过vfptr找到其所指向的vtable,然后调用其相应的方法,即可实现多态。

例如:

A c;

B e;

A *pc=&e; //设置breakpoint,运行到此处

pc=&c;

此时内存中各指针状况如下:





可以看出,此时pc指向类B的虚表地址,从而调用对象e的方法。

继续运行,当运行至pc=&c时候,此时pc的vptr值为0x00420050,即指向类A的vtable地址,从而调用c的方法。

这就是动态绑定!(dynamic binding)或者叫做迟后联编(lazy compile)。

为了更加透析多态的原理,我们可以debug 程序在runtime时候的对象内存分布情况。

以下面这段简单的程序为例

// SimpleStack.cpp
: Defines the entry point for the console application.

//

#include "stdafx.h"

class Base

{

public:

int m_data;

static int m_staticvalue;

Base(int data)

{

m_data=data;

}

virtual void DoWork()

{

}

};

class AnotherBase

{

public:

virtual void AnotherWork()

{}

};

class DerivedClass:public Base,public AnotherBase

{

public:

DerivedClass(int t_data):Base(t_data)

{}

virtual void
DoWork()

{

}

virtual void AnotherWork()

{

}

};

int Base::m_staticvalue=1;

int main(int argc, char* argv[])

{

DerivedClass b(1);

b.DoWork();

return 0;

}

当程序运行后我们设置很简单的breakpoint: bp simplestack!derivedclass::dowork. 断点命中后的call stack如下:

0:000> kb

ChildEBP RetAddr Args to Child

0012ff20 0040102a 00daf6f2 00daf770 7ffd7000 SimpleStack!DerivedClass::DoWork

0012ff80 004012f9 00000001 00420e80 00420dc0 SimpleStack!main+0x2a

0012ffc0 7c817077 00daf6f2 00daf770 7ffd7000 SimpleStack!mainCRTStartup+0xe9

0012fff0 00000000 00401210 00000000 78746341 kernel32!BaseProcessStart+0x23

这时,我们可以看看DerivedClass对象的内存内分布情况:

0:000> dt SimpleStack!DerivedClass 0012ff74

+0x000 __VFN_table : 0x0040c020 //指向虚表的指针1

+0x004 m_data : 1

=0040d030 Base::m_staticvalue : 1 //(类成员)

+0x008 __VFN_table : 0x0040c01c //指向虚表的指针2

可以看到,DerivedClass对象中包含两个指向虚表的指针,地址分别为0x0040c020 和0x0040c01c 。一个为指向override了BaseClass的方法的虚表,一个指向orverride了AnotherBase方法的虚表。

可以查看对应虚表中的方法:

0:000> dds 0x0040c01c

0040c01c 00401140 SimpleStack!DerivedClass::AnotherWork

0040c020 00401110 SimpleStack!DerivedClass::DoWork

0040c024 004010e0 SimpleStack!Base::DoWork

0040c028 004011a0 SimpleStack!AnotherBase::AnotherWork

......

通过以上分析,应该可以透析多态的本质了。

这种看内存分配方案真的不错的,^-^

自己也试了一下,虽然常用VC,不过也没注意过

#include <iostream>

using namespace std;

class base2

{

public:

virtual std::string ff(){return "base2";}

protected:

private:

};

class base

{

public:

virtual std::string f1(){return "base";}

virtual std::string f2(){return "base";}

virtual std::string f3(){return "base";}

protected:

private:

};

class child : public base , public base2

{

public:

virtual std::string f2(){return "child";}

virtual std::string f3(){return "child";}

protected:

private:

};

class grant : public child

{

public:

virtual std::string f3(){return "grant";}

protected:

private:

};

void main()

{

base a;

child b;

grant c;

string sa = a.f1();

string sb = b.f1();

string sc = c.f1();

}

// 内存状态

- a {...}

- __vfptr 0x0042f024 const base::`vftable'

[0x0] 0x00401014 base::f1(void)

[0x1] 0x00401055 base::f2(void)

[0x2] 0x00401032 base::f3(void)

- b {...}

- base {...}

- __vfptr 0x0042f038 const child::`vftable'{for `base'}

[0x0] 0x00401014 base::f1(void)

[0x1] 0x00401050 child::f2(void)

[0x2] 0x0040106e child::f3(void)

- base2 {...}

- __vfptr 0x0042f034 const child::`vftable'{for `base2'}

[0x0] 0x00401023 base2::ff(void)

- c {...}

- child {...}

- base {...}

- __vfptr 0x0042f05c const grant::`vftable'{for `base'}

[0x0] 0x00401014 base::f1(void)

[0x1] 0x00401050 child::f2(void)

[0x2] 0x0040103c grant::f3(void)

- base2 {...}

- __vfptr 0x0042f058 const grant::`vftable'{for `base2'}

[0x0] 0x00401023 base2::ff(void)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: