动态联编 和 静态联编

转载 2013年12月02日 23:35:45

关于 动态联编 和 静态联编 这个概念,自己听了老师上课讲的课仍然没有明白原理。

那么既然这样,只能自己去学习了。


首先我们知道的是,动态联编 和 静态联编 都是多态性的一种体现。

关于面向对象的三个基本要素:封装(类型抽象), 继承 和 多态。

首先我们从概念性上面了解了 动态联编 和 静态联编 的功能:实现了多态性。


然后我们从最最基本的开始讲解。


1.什么是 联编?

我参考了下面这个博客:

http://bdxnote.blog.163.com/blog/static/8444235200911311348529/


联编是指一个计算机程序自身彼此关联的过程,在这个联编过程中,需要确定程序中的操作调用(函数调用)与执行该操作(函数)的代码段之间的映射关系;按照联编所进行的阶段不同,可分为静态联编和动态联编;

仔细读读红色字体的那部分句子。我们就能很清楚的明白什么是联编了。给大家举个最通俗易懂的例子好了:

A类中有fun这个函数, B类中也有fun这个函数,现在我在类外的main函数里面调用fun 函数。

那么main函数就是函数调用,调用fun函数,

而A类中的fun函数和B类中的fun函数就是执行该操作的代码段

所以现在联编就是实现两者的映射关系。

  1. class A  
  2. {     
  3.     void func() {cout<<"It's A"<<endl;  
  4.   
  5. };  
  6.   
  7. class B  
  8. {     
  9.     void func() {cout<<"It's B"<<endl;  
  10.   
  11. };  
  12. int main()  
  13. {  
  14.     func();  
  15. }  
联编就是决定将main函数中的func()的函数调用映射到A中的func函数还是B中的func函数的过程。


2.静态联编 和 动态联编 的定义

知道了什么事联编,那么再来理解动态联编 和静态联编也就不难了

静态联编:
是指联编工作是在程序编译连接阶段进行的,这种联编又称为早期联编;因为这种联编是在程序开始运行之前完成的;
在程序编译阶段进行的这种联编又称静态束定;在编译时就解决了程序中的操作调用与执行该操作代码间的关系,确定这种关系又被称为束定;编译时束定又称为静态束定;

拿上面的例子来说,静态联编就是在编译的时候就决定了main函数中调用的是A中的func还是B中的func。一旦编译完成,那么他们的映射关系就唯一确定了。


动态联编:
编译程序在编译阶段并不能确切地知道将要调用的函数,只有在程序执行时才能确定将要调用的函数,为此要确切地知道将要调用的函数,要求联编工作在程序运行时进行,这种在程序运行时进行的联编工作被称为动态联编,或动态束定,又叫晚期联编;C++规定:动态联编是在虚函数的支持下实现的;

动态联编在编译的时候还是不知道到底应该选择哪个func函数,只有在真正执行的时候,它才确定。


静态联编和动态联编都是属于多态性的,它们是在不同的阶段进对不同的实现进行不同的选择;

其实多态性的本质就是选择。因为存在着很多选择,所以就有了多态。


3.静态联编

首先还是拿个例子来说事吧。

  1. #include <iostream>  
  2. using namespace std;  
  3. class shape{  
  4.   public:  
  5.     void draw(){cout<<"I am shape"<<endl;}  
  6.     void fun(){draw();}  
  7. };  
  8. class circle:public shape{  
  9.   public:  
  10.     void draw(){cout<<"I am circle"<<endl;}  
  11. };  
  12. void main(){  
  13.     circle  oneshape;  
  14.     oneshape.fun();  
  15. }  
现在我们详细具体定义了一开始的A类和B类以及func函数。让我们来分析一下:

调用oneshape.fun()的时候,进入类shape中的fun函数。

现在我们的问题就是:fun函数调用的draw到底是shape里面的draw还是circle中的draw??

答案是:它调用了cshape这个基类的draw函数。所以输出了 I am shape

那么一直困扰我的问题是:为什么调用基类的draw而不是派生类中得draw呢?

书上好像没有具体讲,上课的时候老师那也根本不会讲。

自己想了一下,应该可以从汇编的角度理解:

1.调用oneshape.fun()这里是一个跳转指令,进入类shape中的fun函数所在的代码段

2.类shape的代码段是依次顺序放置的。进入fun函数后,现在我们要调用draw的地址。

由于没有另外的数据结构来保存draw的地址,所以程序所知道的,必然只有在shape类中的draw地址了,仅仅用一个跳转指令

在我的vs2010的反汇编调试窗口下是这样的一句代码:

013B1546  call        shape::draw (13B10F5h) 

很明确这里指出了shape::draw,也就确定了映射关系,完成了联编。


4.动态联编

从第3节中我们看到了静态联编的不足(大概教材中就是这样说的)。

刚才我讲了,

由于没有另外的数据结构来保存draw的地址,所以程序所知道的,必然之后在shape类中的draw地址了,仅仅用一个跳转指令

所以我们想实现动态编联,其实就是想弄出个数据结构来,这个数据结构用来存放 映射关系,也就是联编。

所以c++才搞了个虚函数。其实虚函数的本质就是搞了个数据结构。也就是虚函数表

关于虚函数表,自己不想多扯了,扯出来又是一大串,大家自己去下面的博客学习吧。

http://blog.csdn.net/haoel/article/details/1948051/

4.1虚函数

大家都不喜欢理解概念。我也不喜欢。但是概念毕竟是要紧的。我已经跟大家说明了为什么要引入虚函数。所以接下来请大家耐心看看下面这段概念吧:

虚函数是动态联编的基础;虚函数是成员函数,而且是非静态的成员函数;虚函数在派生类中可能有不同的实现,当使用这个成员函数操作指针或引用所标识的对象时,对该成员函数的调用采用动态联编方式,即:在程序运行时进行关联或束定调用关系;
动态联编只能通过指针或引用标识对象来操作虚函数;如果采用一般的标识对象来操作虚函数,将采用静态联编的方式调用虚函数;
如果一个类具有虚函数,那么编译器就会为这个类的对象定义一个指针成员,并让这个指针成员指向一个表格,这个表格里面存放的是类的虚函数的入口地址;比如:一个基类里面有一些虚函数,那么这个基类就拥有这样一个表,它里面存放了自己的虚函数的入口地址,其派生类继承了这个虚函数表,如果在派生类中重写/覆盖/修改了基类中的虚函数,那么编译器就会把虚函数表中的函数入口地址修改成派生类中的对应虚函数的入口地址;这就为类的多态性的实现提供了基础;

虚函数按照其声明顺序存放于虚函数表中;
父类的虚函数存放在子类虚函数的前面;
多继承中,每个父类都有自己的虚函数表;
子类的成员函数被存放于第一个父类的虚函数表中;

4.2动态联编举例

最后给大家举个例子,在第三节的那个例子上,下面的代码仅仅是多了一个词:virtual

  1. #include <iostream>  
  2. using namespace std;  
  3. class shape{  
  4.   public:  
  5.     void virtual draw(){cout<<"I am shape"<<endl;}//这里设定了draw是虚函数  
  6.     void fun(){draw();}  
  7. };  
  8. class circle:public shape{  
  9.   public:  
  10.     void draw(){cout<<"I am circle"<<endl;}//虽然没有说明circle类中的draw是虚函数,但是circle其实继承了virtual性质  
  11. };  
  12. void main(){  
  13.     circle  oneshape;  
  14.     oneshape.fun();  
  15. }  
现在我们再来运行一下程序,输出就变成了I am circle

这里的反汇编代码贴出来,自己有空看看:

  1. 00CC15A0  push        ebp    
  2. 00CC15A1  mov         ebp,esp    
  3. 00CC15A3  sub         esp,0CCh    
  4. 00CC15A9  push        ebx    
  5. 00CC15AA  push        esi    
  6. 00CC15AB  push        edi    
  7. 00CC15AC  push        ecx    
  8. 00CC15AD  lea         edi,[ebp-0CCh]    
  9. 00CC15B3  mov         ecx,33h    
  10. 00CC15B8  mov         eax,0CCCCCCCCh    
  11. 00CC15BD  rep stos    dword ptr es:[edi]    
  12. 00CC15BF  pop         ecx    
  13. 00CC15C0  mov         dword ptr [ebp-8],ecx    
  14. 00CC15C3  mov         eax,dword ptr [this]    
  15. 00CC15C6  mov         edx,dword ptr [eax]    
  16. 00CC15C8  mov         esi,esp    
  17. 00CC15CA  mov         ecx,dword ptr [this]    
  18. 00CC15CD  mov         eax,dword ptr [edx]    
  19. 00CC15CF  call        eax    
  20. 00CC15D1  cmp         esi,esp    
  21. 00CC15D3  call        @ILT+440(__RTC_CheckEsp) (0CC11BDh)    
  22. 00CC15D8  pop         edi    
  23. 00CC15D9  pop         esi    
  24. 00CC15DA  pop         ebx    
  25. 00CC15DB  add         esp,0CCh    
  26. 00CC15E1  cmp         ebp,esp    
  27. 00CC15E3  call        @ILT+440(__RTC_CheckEsp) (0CC11BDh)    
  28. 00CC15E8  mov         esp,ebp    
  29. 00CC15EA  pop         ebp    
  30. 00CC15EB  ret    
等自己看了虚函数表之后再来好好理解下。



OK,关于动态联编和静态联编就讲到这里了。哪里有不对的地方还请大家指正。

相关文章推荐

C++之静态联编和动态联编

联编是指一个程序自身彼此关联的一个过程。按照联编所进行的阶段不同,可分为静态联编和动态联编两种。        静态联编        静态联编是指在程序编译连接阶段进行联编。这种联编又称为早期联...

c++动态联编与静态联编

摘要】:本文阐述了静态联编和动态联编的概念和区别,通过具体实例分析了实现动态联编的条件,指出了虚函数是实现动态联编的基础。 【关键词】:静态联编;动态联编;虚函数 在C++中,联编是指一个计算...

静态编联与动态编联总结

编联就是将一个函数(或模块)程序内嵌到调用该函数(或模块)的程序中,然后生成可执行代码的过程。在这个过程中,对所调用的函数(或模块)分配内存地址空间,同时对外部访问也分配内存空间。 根据编联所处的阶段...

多态,覆盖,重载,静态联编,动态联编。

C++多态有两种形式,动态多态和静态多态;动态多态是指一般的多态,是通过类继承和虚函数机制实现的多态;静态多态是通过模板来实现,因为这种多态实在编译时而非运行时,所以称为静态多态。 动态多态例子: ...

C++:静态联编和动态联编

联编就是将模块或者函数合并在一起生成可执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存地址,它是计算机程序彼此关联的过程。按照联编所进行的阶段不同,可分为两种...

C++ 虚函数、静态联编、动态联编

本文将对c++虚函数做个总结。 虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的函数! 若在子类中重写成员函数,将不是使用相同的函数特征标覆盖基...

关于静态联编和动态联编的分析

在C++中,联编是指一个计算机程序的不同部分彼此关联的过程。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。 1. 静态联编 静态联编是指联编工作在编译阶段完成的,这种联编...

静态联编与动态联编之virtual的作用

=========================定义=========================       将一个调用函数连接上正确的被调用函数,这个过程就叫做函数的联编,简称联编。在C+...

C++之静态联编与动态联编及virtual关键字的作用

定义 将一个调用函数连接上正确的被调用函数,这个过程就叫做函数的联编,简称联编。在C++中,一共有两种联编的方式: 静态联编 #define:静态联编是指联编工作出现在编译连接阶段。 ...

C++ 静态编联与动态编联

一,什么是静态编联与动态编联? 将源代码中的函数调用解释为执行特定的函数代码块被称为函数编联。在C语言中这很简单,因为每个函数名都对应一个函数。在C++中,由于函数重载的原因,这项任务更复杂。编译器必...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:动态联编 和 静态联编
举报原因:
原因补充:

(最多只允许输入30个字)