荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Deny (孤苦伶仃一个人,哈哈), 信区: Program
标  题: 解析动态联编(上篇)
发信站: 荔园晨风BBS站 (Tue Dec 25 19:47:30 2001), 转信

解析动态联编(上篇)
作者:南京邮政局计算机中心 tingya

文章摘要
多态性是C++最主要的特征,多态性的实现得益于C++中的动态联编技术。文章通过
对动态联编的关键技术虚拟函数表进行深入的剖析,解析的动态联编的过程极其技
术要领。

关键字
多态性 动态联编 VTABLE 虚函数

文章正文

一 从多态性谈动态联编的必要性

在进入主题之前先介绍一下联编的概念。联编就是将模块或者函数合并在一起生成
可 执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外
部访问也分配正确的内存地址。按照联编所进行的阶段不同,可分为两种不同的联
编方法:静态联编和动态联编。在编译阶段就将函数实现和函数调用关联起来称之
为静态联编,静态联编在编译阶段就必须了解所有的函数或模块执行所需要检测的
信息,它对函数的选择是基于指向对象的指针(或者引用)的类型。反之在程序执
行的时候才进行这种关联称之为动态联编,动态联编对成员函数的选择不是基于指
针或者引用,而是基于对象类型,不同的对象类型将做出不同的编译结果。C语言
中,所有的联编都是静态联编。C++中一般情况下联编也是静态联编,但是一旦涉
及到多态性和虚函数就必须使用动态联编。
多态性是面向对象的核心,它的最主要的思想就是可以采用多种形式的能力,通过
一个用户名字或者用户接口完成不同的实现。通常多态性被简单的描述为"一个接
口,多个实现。在C++里面具体的表现为通过基类指针访问派生类的函数和方法。

下面我们看一个静态联编的例子,这种静态联编导致了我们不希望的结果。 //1.
cpp
1. #include <iostream.h>
2. class shape{
3.  public:
4.    void draw(){cout<<"I am shape"<<endl;}
5.    void fun(){draw();}
6. };
7. class circle:public shape{
8.  public:
9.    void draw(){cout<<"I am circle"<<endl;}
10. };
11. main(){
12.    class circle  oneshape;
13.    oneshape.fun();
14. }

程序的输出结果我们希望是"I am circle",但事实上却输出了"I am shape"的结
果,造成这个结果的原因是静态联编。静态联编需要在编译时候就确定函数的实现
,但事实上编译器在仅仅知道shape的地址时候无法获取正确的调用函数,它所知
道的仅是shape::draw(),最终结果只能是draw操作束缚到shape类上。产生"I
am shape"的结果就不足为奇了。
为了能够引起动态联编,我们只需要将需要动态联编的函数声明为虚函数即可。动
态联编只对虚函数起作用。我们在通过基类而且只有通过基类访问派生类的时候,
只要这个基类中直接的或者间接(从上上层继承)的包含虚函数,动态联编将自动
唤醒。下面我们将上面的程序稍微改一下。
//2.cpp
1. #include <iostream.h>
2. class shape{
3.  public:
4.    virtual void draw(){cout<<"I am shape"<<endl;}
5.    void fun(){draw();}
6. };
7. class circle:public shape{
8.  public:
9.    void draw(){cout<<"I am circle"<<endl;}
10. };
11. main(){
12.    class circle  oneshape;
13.    fun(&oneshape);
14. }
程序执行得到了正确的结果"I am circle"。代码在VC6.0中执行。

到目前为止我们不清楚动态联编的执行机制,但我们可以做个猜测。正如上面所说
,对于函数的实际的对象类型不同,联编结果也应该不同。在静态联编中,执行的
困难在于无法通过基类知道需要联编的子对象的确切类型。在1.cpp中shape的派生
类既可能是circle,也可能是其余的rectangle或者square等等,到底应该静态联编
哪一个呢。迷惑正在于此。动态联编在编译的时候应该也是不知道联编的确切对象
类型的,(如果知道的话就成了静态联编了),因此它只能通过一定的机制,使得
在执行时候能够找到和调用正确的函数体。可以想象,为了达到这个目的,一些相
关信息应该封装在对象自身中。这些信息有点象身份证明,标识自己,这样在动态
联编的时候,编译器可以根据这些标记找到相应的函数体,"不要跑,就是你了"。

实际上的动态联编过程是什么样的呢。

二 对象类型信息

为了证明我们的猜想,我们用下面的一个程序进行测试,下面的程序将获取普通的
类和包含虚函数的类的字节大小。程序代码如下。 //3.cpp
1. #include <iostream.h>
2. class shape_novirtual{
3.      int   a;
4.   public:
5.      void  draw(){cout<<"shape_novirtual::draw()"<<endl;}
6. };
7. class shape_virtual1{
8.      int   a;
9.   public:
10.      virtual void draw(){cout<<"shape_virtual::draw()"<<endl;}
11. };
12. class shape_virtual2{
13.      int  a;
14.   public:
15.      virtual void draw(){cout<<"shape_virtual2::draw()"<<endl;}
16.      virtual void draw1(){cout<<"shape_virtual2::draw1()"<vendl;}
17. };
18. main(){
19.   cout<<"sizeof(int)"<<sizeof(int)<<endl;
20.   cout<<"sizeof(class shape_novirtual):
"<<sizeof(shape_novirtual)<<endl;
21.   cout<<"sizeof(void*):"<<sizeof(void*)<<endl;
22.   cout<<"sizeof(class shape_virtual):
"<<sizeof(shape_virtual)<<endl;
23.   cout<<"sizeof(class shape_virtual2):
"<<sizeof(shape_virtual2)<<endl;
24. }
VC6.0中运行结果如下: sizeof(int)4
sizeof(class shape_novirtual):4
sizeof(void*):4
sizeof(class shape_virtual1):8
sizeof(class shape_virtual2):8
Press any key to continue
从上面可以看出,没有虚函数的类shape_novirtual的大小为4,正好为int a的大
小。而带有虚函数的类shape_virtual1和shape_virtual2的大小除了int a的大小
还多出了4格个字节的大小,这个大小正好是void*指针的大小。到现在为止我们基
本上可以说带有虚函数的对象自身确实插入了一些指针信息,而且这个指针信息并
不随着虚函数的增加而增大。
如果我们将每个类的成员变量int a去掉,VC6.0运行结果就会变成下面的情况。
sizeof(int)4
sizeof(class shape_novirtual):1
sizeof(void*):4
sizeof(class shape_virtual1):4
sizeof(class shape_virtual2):4
Press any key to continue
上面的运行结果应该让人感到例外。既然size(int)为4,现在没有了这个成员变量
,类shape_novirtual应该字节大小为0,但事实上C++编译器不允许对象为零长度
。试想一个长度为0的对象在内存中怎么存放?怎么获取它的地址?为了避免这种
情况,C++强制给这种类插入一个缺省成员,长度为1。如果有自定义的变量,变量
将取代这个缺省成员。



--
  ╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬◤      ◣ ╬
  ╬╬╬烛光开始闪烁╬流下最后的眼泪╬╬╬╬╬╬╬╬╬╬╬╬╬ ◤◤      ▉╬
  ╬╬╬╬╬╬╬╬╬一种回忆╬一个思索╬╬╬╬╬╬╬╬╬╬╬Δ ◥ ㄧ◣   ▊╬
  ╬╬╬╬╬╬剩下一片漫漫的黑╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▊╬   ∥ ◥ ▋╬
  ╬╬╬╬╬╬╬╬╬╬╬╬╬是渐渐觉醒的灵魂╬╬╬╬╬╬╬╬▊╬ ◣∥   ◤ ╬
  ╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▆▁▁◢◤ ◥╬╬

※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.1.201]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店