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

本文详细介绍了C++中的两种联编方式:静态联编和动态联编。静态联编在编译阶段完成,效率高但灵活性低;动态联编则在运行时决定函数调用,灵活性强但效率相对较低。动态联编主要用于实现多态性和虚函数,要求虚函数在派生类中保持一致,并通过基类指针或引用调用。
摘要由CSDN通过智能技术生成

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

1. 静态联编
静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数调用)与执行该操作代码间的关系,确定这种关系称为束定,在编译时的束定称为静态束定。静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高,但灵活性差。
例1:静态联编

#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. 动态联编
       动态联编是指联编在程序运行时动态地进行,根据当时的情况来确定调用哪个同名函数,实际上是在运行时虚函数的实现。这种联编又称为晚期联编,或动态束定。动态联编对成员函数的选择是基于对象的类型,针对不同的对象类型将做出不同的编译结果。C++中一般情况下的联编是静态联编,但是当涉及到多态性和虚函数时应该使用动态联编。动态联编的优点是灵活性强,但效率低。动态联编规定,只能通过指向基类的指针或基类对象的引用来调用虚函数,其格式为: 指向基类的指针变量名- >虚函数名(实参表)或 基类对象的引用名. 虚函数名(实参表)
实现动态联编需要同时满足以下三个条件:①必须把动态联编的行为定义为类的虚函数。 ②类之间应满足子类型关系,通常表现为一个类从另一个类公有派生而来。③必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。

例2:动态联编

#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:通过指向基类的指针来调用虚函数

#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: 通过基类对象的引用来调用虚函数

#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)
{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. 小结

       从以上四个例子中可以看出: 虚函数是实现多态的基础, 是实现动态联编的必要条件之一。 动态联编要靠虚函数来实现, 虚函数要靠动态联编的支持。两者相辅相成, 缺一不可。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值