分类: c/c++ 2013-06-14 01:44 235人阅读 评论(0) 收藏 举报
1. 静态联编
静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数调用)与执行该操作代码间的关系,确定这种关系称为束定,在编译时的束定称为静态束定。静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高,但灵活性差。
例1:静态联编
[cpp] view plain copy print ?
<SPAN
class
{public:
void
};
class
{public:
void
};
void
{
A
pa=&a;
pa=&b;
}</SPAN>
#include "iostream.h" class A {public: void f() {cout<<"A"<<" ";} }; class B:public A {public: void f() {cout<<"B"<<endl;} }; void main() { A *pa = NULL; A a; B b; pa=&a; pa- >f(); pa=&b; pa- >f(); }
}
该程序的运行结果为: A A
从例1 程序的运行结果可以看出,通过对象指针进行的普通成员函数的调用,仅仅与指针的类型有关,而与此刻指针正指向什么对象无关。要想实现当指针指向不同对象时执行不同的操作,就必须将基类中相应的成员函数定义为虚函数,进行动态联编。
2. 动态联编
实现动态联编需要同时满足以下三个条件:①必须把动态联编的行为定义为类的虚函数。 ②类之间应满足子类型关系,通常表现为一个类从另一个类公有派生而来。③必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。
例2:动态联编
[cpp] view plain copy print ?
<SPAN
class
{public:
virtual
{cout<<"A"<<"
};
class
{public:
virtual
{cout<<"B"<<endl;}
};
void
{
A
pa=&a;
pa=&b;
}</SPAN>
#include "iostream.h" class A {public: virtual void f() //虚函数 {cout<<"A"<<" ";} }; class B:public A {public: virtual void f() //虚函数 {cout<<"B"<<endl;} }; void main() { A *pa = NULL; A a; B b; pa=&a; pa- >f(); pa=&b; pa- >f(); }
}
该程序的运行结果为: A B
从例 2 程序的运行结果可以看出,将基类 A中的函数 f定义为虚函数后,当指针指向不同对象时执行了不同的操作,实现了动态联编。
3. 动态联编分析
动态联编要求派生类中的虚函数与基类中对应的虚函数具有相同的名称、相同的参数个数和相同的对应参数类型、返回值或者相同,或者都返回指针或引用,并且派生类虚函数所返回的指针或引用的基类型是基类中虚函数所返回的指针或引用的基类型的子类型。如果不满足这些条件,派生类中的虚函数将丢失其虚特性,在调用时进行静态联编。
例3:通过指向基类的指针来调用虚函数
[cpp] view plain copy print ?
<SPAN
class
{
virtual
virtual
void
void
};
class
{public:
virtual
virtual
virtual
void
};
void
{
pb=&d;
pb-
pb-
}
该程序的运行结果:
derived
base
base
base
#include "iostream.h" class base { public: virtual void fun1() {cout<<"base fun1"<<endl;} virtual void fun2() {cout<<"base fun2"<<endl;} void fun3() {cout<<"base fun3"<<endl;} void fun4() {cout<<"base fun4"<<endl;} }; class derived:public base {public: virtual void fun1() {cout<<"derived fun1"<<endl;} virtual void fun2(int x) {cout<<"derived fun2"<<endl;} virtual void fun3() {cout<<"derived fun3"<<endl;} void fun4() {cout<<"derived fun4"<<endl;} }; void main() { base *pb; derived d; pb=&d; //通过指向基类的指针来调用虚函数 pb- >fun1(); pb- >fun2(); pb- >fun3(); pb- >fun4(); } 该程序的运行结果: derived fun1 base fun2 base fun3 base fun4
本例中函数 fun1 在基类 base 和派生类 derived 中均使用了关键字 virtual定义为虚函数,并且这两个虚函数具有相同的参数个数、参数类型和返回值类型。因此,当指针 pb 访问 fun1 函数时,采用的是动态联编。函数 fun2 在基类 base 和派生类 de-rived 中定义为虚函数,但这两个虚函数具有不同的参数个数。函数 fun2 丢失了其虚特性, 在调用时进行静态联编。函数 fun3 在基类 base 中说明为一般函数, 在派生类 de-rived 中定义为虚函数。在这种情况下, 应该以基类中说明的成员函数的特性为标准, 即函数 fun3 是一般成员函数, 在调用时采用静态联编。函数 fun4 在基类 base 和派生类 derived 中均说明为一般函数, 因此基类指针 pb 只能访问 base 中的成员。
例4: 通过基类对象的引用来调用虚函数
[cpp] view plain copy print ?
<SPAN
class
{
CPoint(double
{
virtual
{
private:
double
}
class
{
CRectangle(double
double
{
private:
double
}
CRectangle::CRectangle(double
#include "iostream.h" class CPoint { public: CPoint(double i, double j) { x=i; y=j; } virtual double Area() //虚函数 { return 0.0; } private: double x, y; } ; class CRectangle:public CPoint { public: CRectangle(double i, double j, double k, double l); double Area() { return w*h; } private: double w, h; } ; CRectangle::CRectangle(double i,double j,double k,double l):CPoint(i, j)
[cpp] view plain copy print ?
<SPAN
void
{
void
{CRectangle
fun(rec);
}
该程序的运行结果为:
{w=k; h=l;} void fun(CPoint &s) { cout<<s.Area()<<endl; } //通过基类对象的引用来调用虚函数 void main() {CRectangle rec(3.0, 5.2, 15.0, 25.0); fun(rec); } 该程序的运行结果为: 375例 4 中的成员函数 Area 在基类 CPoint 中使用了关键字virtual 定义为虚函数, 在派生类 CRectangle 中定义为一般函数,但是进行了动态联编, 结果为15*25 即 375。这是因为一个虚函数无论被公有继承多少次, 它仍然保持其虚特性。 在派生类中重新定义虚函数时, 关键字 virtual可以写也可不写, 但为了保持良好的编程风格, 避免引起混乱时, 应写上该关键字。
4. 小结