http://blog.youkuaiyun.com/gaoxin1076/article/details/8298279
转自
自己总结添加了一些东西方便自己复习!
首先我们知道的是,动态联编 和 静态联编 都是多态性的一种体现。
关于面向对象的三个基本要素:封装(类型抽象), 继承 和 多态。
首先我们从概念性上面了解了 动态联编 和 静态联编 的功能:实现了多态性。
然后我们从最最基本的开始讲解。
1.什么是 联编?
我参考了下面这个博客:
http://bdxnote.blog.163.com/blog/static/8444235200911311348529/
联编是指一个计算机程序自身彼此关联的过程,在这个联编过程中,需要确定程序中的操作调用(函数调用)与执行该操作(函数)的代码段之间的映射关系;按照联编所进行的阶段不同,可分为静态联编和动态联编;
例如
A类中有fun这个函数, B类中也有fun这个函数,现在我在类外的main函数里面调用fun 函数。
那么main函数就是函数调用,调用fun函数,
而A类中的fun函数和B类中的fun函数就是执行该操作的代码段
所以现在联编就是实现两者的映射关系。
class A
{
void func() {cout<<"It's A"<<endl;
};
class B
{
void func() {cout<<"It's B"<<endl;
};
int main()
{
func();
}
联编就是决定将main函数中的func()的函数调用映射到A中的func函数还是B中的func函数的过程。
2.静态联编 和 动态联编 的定义
知道了什么事联编,那么再来理解动态联编 和静态联编也就不难了
静态联编:
是指联编工作是在程序编译连接阶段进行的,这种联编又称为早期联编;因为这种联编是在程序开始运行之前完成的;
在程序编译阶段进行的这种联编又称静态束定;在编译时就解决了程序中的操作调用与执行该操作代码间的关系,确定这种关系又被称为束定;编译时束定又称为静态束定;
拿上面的例子来说,静态联编就是在编译的时候就决定了main函数中调用的是A中的func还是B中的func。一旦编译完成,那么他们的映射关系就唯一确定了。
动态联编:
编译程序在编译阶段并不能确切地知道将要调用的函数,只有在程序执行时才能确定将要调用的函数,为此要确切地知道将要调用的函数,要求联编工作在程序运行时进行,这种在程序运行时进行的联编工作被称为动态联编,或动态束定,又叫晚期联编;C++规定:动态联编是在虚函数的支持下实现的;
动态联编在编译的时候还是不知道到底应该选择哪个func函数,只有在真正执行的时候,它才确定。
静态联编和动态联编都是属于多态性的,它们是在不同的阶段进对不同的实现进行不同的选择;
其实多态性的本质就是选择。因为存在着很多选择,所以就有了多态。
3.静态联编
首先还是拿个例子来说事吧。
#include <iostream>
using namespace std;
class shape{
public:
void draw(){cout<<"I am shape"<<endl;}
void fun(){draw();}
};
class circle:public shape{
public:
void draw(){cout<<"I am circle"<<endl;}
};
void main(){
circle oneshape;
oneshape.fun();
}
现在我们详细具体定义了一开始的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,也就确定了映射关系,完成了联编。