原文地址: http://blog.csdn.net/neiloid/article/details/6934129
加入自己一些理解....
在C++中,联编是指一个计算机程序的不同部分彼此关联的过程。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。
1. 静态联编
静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数调用)与执行该操作代码间的关系,确定这种关系称为束定,在编译时的束定称为静态束定。静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高,但灵活性差。
例1:静态联编
#include"iostream.h"
class A
{public:
voidf(){cout<<"A"<<"";}
};
classB:publicA
{public:
voidf(){cout<<"B"<<endl;}
};
Void main()
{A*pa=NULL;
Aa;Bb;
pa=&a;pa->f();
pa=&b;pa->f();
}
该程序的运行结果为:A A
从例1程序的运行结果可以看出,通过对象指针进行的普通成员函数的调用,仅仅与指针的类型有关,而与此刻指针正指向什么对象无关。要想实现当指针指向不同对象时执行不同的操作,就必须将基类中相应的成员函数定义为虚函数,进行动态联编。
2. 动态联编
动态联编是指联编在程序运行时动态地进行,根据当时的情况来确定调用哪个同名函数,实际上是在运行时虚函数的实现。这种联编又称为晚期联编,或动态束定。动态联编对成员函数的选择是基于对象的类型,针对不同的对象类型将做出不同的编译结果。C++中一般情况下的联编是静态联编,但是当涉及到多态性和虚函数时应该使用动态联编。动态联编的优点是灵活性强,但效率低。
动态联编规定,只能通过指向基类的指针或基类对象的引用来调用虚函数,其格式为:指向基类的指针变量名->虚函数名(实参表)或基类对象的引用名.虚函数名(实参表)
实现动态联编需要同时满足以下三个条件:
① 必须把动态联编的行为定义为类的虚函数。
② 类之间应满足子类型关系,通常表现为一个类从另一个类公有派生而来。
③ 必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。
例2:动态联编
#include"iostream.h"
classA
{public:
Virtual voidf()//虚函数
{cout<<"A"<<"";}};
classB:publicA
{public:
Virtual voidf()//虚函数
{cout<<"B"<<endl;}
};
voidmain()
{ A*pa=NULL;
Aa;Bb;
pa=&a;
pa->f();
pa=&b;
pa->f();
}
该程序的运行结果为:A B
从例2程序的运行结果可以看出,将基类A中的函数f定义为虚函数后,当指针指向不同对象时执行了不同的操作,实现了动态联编。
3. 动态联编分析
动态联编要求派生类中的虚函数与基类中对应的虚函数具有相同的名称、相同的参数个数和相同的对应参数类型、返回值或者相同,或者都返回指针或引用,并且派生类虚函数所返回的指针或引用的基类型是基类中虚函数所返回的指针或引用的基类型的子类型。如果不满足这些条件,派生类中的虚函数将丢失其虚特性,在调用时进行静态联编。
例3:通过指向基类的指针来调用虚函数
#include"iostream.h"
Class base
{
public:
virtual void fun1(){cout<<"base fun1"<<endl;}
virtual void fun2(){cout<<"base fun2"<<endl;}
virtual void fun3(){cout<<"base fun3"<<endl;}
void fun4(){cout<<"base fun4"<<endl;}
void fun5(){cout<<"base fun5"<<endl;}
};
Class derived: public base{
public:
virtual void fun1(){cout<<"derived fun1"<<endl;}
virtual void fun2(int x){cout<<"derived fun2"<<endl;}
void fun3(){cout<<"derived fun3"<<endl;}
virtual Void fun4(){cout<<"derived fun4"<<endl;}
void fun5(){cout<<"derived fun5"<<endl;}
};
Void main()
{
base* pb;
derived d;
pb=&d;//通过指向基类的指针来调用虚函数
pb->fun1();
pb->fun2();
pb->fun3();
pb->fun4();
pb->fun5();
}
结果:
deriver fun1
base fun2
deriver fun3
base fun4
base fun5
为什么这样呢?我们可以查看一下上两个类的虚函数表, VS2010提供了方便的工具
执行命令:
cl /d1reportSingleClassLayoutXXX 文件名
(XXX 表示类名)
图中红色框框的就是 类中的虚拟表,base 类的fun1, fun2, fun3用virtual声明,所以该类虚拟表有三个指针地址
再看看Drived类的。。
Base fun1 用了 virtual 声明 , 所以调用 子类 d->fun1() 时,调用的是 d 的
Base fun2 虽用了 virtual 声明, 但 子类 的 fun2(int x) 有参数 与base 的不同,所以d -> fun2() 调用的是 b 的
Base fun3 用了 virtual 声明,也是调用的 d 的, 子类 d 的 fun3() 没用virtual声明, 但由于继承base fun3()且同名,其也 具有 virtual 特性,可以不明写 virtual, 但一般为了程序明了,还是加上较好,增强阅读性。 我们验证一下,现在增 加一个类
Inherit 继承于 Derived
class Inherit:public Derived
{
public:
Inherit(void);
~Inherit(void);
public :
void fun3() { printf("inherit fun3"); }
void fun4() { printf("inherit fun4"); }
};
再看看其虚拟表
Inherit类的 fun3, fun4 都没有用 virtual声明, 但这两都是虚函数。。
执行
Base *b = newInherit();//Derived();
b->fun1();
b->fun2();
b->fun3();
b->fun4();
b->fun5();
调用结果
derived fun1
base fun2
inherit fun3 <--inherit的老爸Derived没有明写virtual,但Derived的老爸Base的fun3是virtual,那么特性将不会因继承消失
base fun4 <--- inherit fun4从Derived继承virtual,但这里用的是Base的指针,Base fun4没有virtual特性,所以就这样
base fun5
那么用Drived的指针调用inherit 看看
Derived *d = newInherit();//Derived();
d->fun1();
d->fun2(2);
d->fun3();
d->fun4();
d->fun5();
结果:
derived fun1
derived fun2
inherit fun3
inherit fun4 <-----看。。。
deriverd fun5
另,参考资料
http://blog.csdn.net/haoel/article/details/1948051