小众知识

首页 > 正文

请问C++中的静态联编和动态联编的关系究竟是什么?

请问C++中的静态联编和动态联编到底是怎么回事?  


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

    1. 静态联编


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


例1:静态联编

#include "iostream.h"
class A
{
public:
    void f()
    {
        cout << "A" << "";
    }
};
classB:
publicA
{
public:
    void f()
    {
        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;
    }
    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(intx)
    {
        cout << "derived fun2" << endl;
    }
    Virtual void fun3()
    {
        cout << "derived fun3" << endl;
    }
    Void fun4()
    {
        cout << "derived fun4" << endl;
    }
};
Void main()
{
    base *pb;
    derivedd;
    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中说明为一般函数,在派生类derived中定义为虚函数。在这种情况下,应该以基类中说明的成员函数的特性为标准,即函数fun3是一般成员函数,在调用时采用静态联编。函数fun4在基类base和派生类derived中均说明为一般函数,因此基类指针pb只能访问base中的成员。


例4:通过基类对象的引用来调用虚函数

#include"iostream.h"
Class CPoint
{
public:
    CPoint(doublei, doublej)
    {
        x = i;
        y = j;
    }
    Virtual double Area()
    {
        return 0.0;
    }
private:
    doublex, 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. 小结

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

 blue_coco 发表于:2002-11-05 17:48:07
得分:2 

你说的是静态束定和动态束定,
静态束定就象宏,内联在编译是已经定了,
动态束定就象虚函数。

 taotaoer 发表于:2002-11-05 19:14:06
得分:1 

静态的,在编译的时候就已经邦定;但动态的要在运行的时候才能确定 

 josonweng 发表于:2002-11-05 20:25:05
得分:5 

在被同一个类继承的多个类中,都有函数名相同的成员函数,在对象调用这个函数时,就要确定到底要调用哪个基类中的成员函数。采用动态联编就能准确的实现 

 00C 发表于:2002-11-07 13:13:23
得分:5 

静态指的是:编译时就用你定义的那个类型,对你的请求(即,data member,member function)进行绑定。而动态则是:当你调用一个声明为:virtual function,virtual base class‘s  member 你告诉不用当前的你定义时的静态类型来绑定,从而编译器会给你插入代码完成动态绑定!

 jiaclassmate 发表于:2002-11-07 13:41:55
得分:2 

静态联编主要是通过继承和重载实现,而动态联编是通过虚函数实现 

 ddmpqcw 发表于:2002-11-07 15:05:57
得分:5 

简单的说

静态的东西一般在编译期就可以决议,

像类成员的偏移量,静态数组。。。

动态的东西一般是在执行期才能决议的,

像指针的间接调用,多态性,动态数组。。。 

上一篇:怎么增加CSDN可用积分???
下一篇:关于static的问题,请给予指教!

分享到: