精华内容
下载资源
问答
  • 本文阐述了静态联编和动态联编的概念区别,通过具体实例分析了实现动态联编的条件,指出了虚函数是实现动态联编的基础。
  • 静态联编和动态联编

    2019-05-05 15:49:57
    静态联编和动态联编 1、联编是指一个程序模块、代码之间互相关联的过程。 2、静态联编(static binding),是程序的匹配、连接在编译阶段实现,也称为早期匹配。 重载函数使用静态联编。(没有加virtual) 3、...

    理论:

    静态联编和动态联编

    1、联编是指一个程序模块、代码之间互相关联的过程。

    2、静态联编(static binding),是程序的匹配、连接在编译阶段实现,也称为早期匹配。

        重载函数使用静态联编。(没有加virtual

    3、动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编(迟绑定)。(加了virtual)

    switch 语句和 if 语句是动态联编的例子。

    实例

    #include <iostream>
    using namespace std;
    class A
    {
    private:
        int a;
    public:
        virtual void print()
        {
            cout << "a=" << a << endl;
        }
        A(int a)
        {
            this->a = a;
        }
    };
    class B : public A
    {
    private:
        int b;
    public:
        virtual void print()
        {
            cout << "b=" << b << endl;
        }
        B(int a, int b) : A(a)
        {
            this->b = b;
        }
    };

    void main()
    {
        
        A a1(1);
        B b1(2,3);    
        A *base1 = NULL;
        base1 = &a1;
        base1->print();   //1
        A *base2 = NULL;
        base2 = &b1;
        base2->print();   //3
        
        A &base3 = a1;
        base3.print();  //1
        A &base4 = b1;
        base4.print();  //  3

        system("pause");
    }

    4、理论联系实际

         在没有加virtual之前

    1、C++与C相同,是静态编译型语言

    2、在编译时,编译器自动根据指针的类型判断指向的是一个什么样的对象;所以编译器认为父类指针指向的是父类对象。

    3、由于程序没有运行,所以不可能知道父类指针指向的具体是父类对象还是子类对象

    从程序安全的角度,编译器假设父类指针只指向父类对象,因此编译的结果为调用父类的成员函数。这种特性就是静态联编。

        加上 virtual  动态联编,根据实际的类型执行相应的函数(父类还是子类的函数)

    此篇文章的前序:https://blog.csdn.net/huang1600301017/article/details/89819702

    展开全文
  • 按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。 静态联编是指在编译阶段就将函数实现函数调用关联起来,因此静态联编也叫早绑定,在编译阶段就必须了解所有的函数或模块执行所需要...
  • 像这种,在编译阶段,就已经确定函数的调用地址的情况,就叫静态联编 那么问题来了,我们应该如何实现执行animal.speak()时,最终调用的cat对象中的doSpeak()呢? 答案很简单,只需要在Animal的speak()方法前加个...

    先看下面的代码

    class Animal
    {
    public:
    	    void speak()
    	{
    		cout << "动物在说话" << endl;
    	}
    
    
    };
    
    class Cat :public Animal
    {
    public:
    	void speak()
    	{
    		cout << "小猫在说话" << endl;
    	}
    
    };
    void doSpeak(Animal & animal) 
    {
    	animal.speak();
    }

    接着调用如下代码测试

    Cat cat;
    	doSpeak(cat);

    运行结果如下:

    可能对于大多数人而言,他们会认为输出结果是:小猫在说话。

    然而事实上却不是,出现这样的原因是,在编译的时候,编译器已经将animal.speak();这行代码转化为了具体的函数地址调用,因此这行代码并不受外边参数的影响。像这种,在编译阶段,就已经确定函数的调用地址的情况,就叫静态联编

    那么问题来了,我们应该如何实现执行animal.speak()时,最终调用的cat对象中的doSpeak()呢?

    答案很简单,只需要在Animal的speak()方法前加个virtual,具体如下

    class Animal
    {
    public:
    	virtual void speak()
    	{
    		cout << "动物在说话" << endl;
    	}
    
    };

    此时,你再调用下边的代码

    Cat cat;
    	doSpeak(cat);

    打印的结果就是:小猫在说话。

    由此可看出,virtual的作用就是告诉编译器,不要在编译阶段就确定speak()的调用地址,只在运行的时候,才根据参数决定speak()调用地址。像这种情况就叫做动态联编,在其他的编程语言里,动态联编也被叫做多态。

    C++ 动态联编(多态)原理剖析

    展开全文
  • 按照联编所进行的阶段不同,可分为静态联编和动态联编。 例如A类中有fun这个函数, B类中也有fun这个函数,现在我在类外的main函数里面调用fun 函数。那么main函数就是函数调用,调用fun函数,而A...

    1.什么是联编? 
           联编是指一个计算机程序自身彼此关联的过程,在这个联编过程中,需要确定程序中的操作调用(函数调用)与执行该操作(函数)的代码段之间的映射关系;按照联编所进行的阶段不同,可分为静态联编和动态联编。
           例如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.静态联编和动态联编定义 
    (1).静态联编 
           是指联编工作是在程序编译连接阶段进行的,这种联编又称为早期联编;因为这种联编是在程序开始运行之前完成的;在程序编译阶段进行的这种联编又称静态束定;在编译时就解决了程序中的操作调用与执行该操作代码间的关系,确定这种关系又被称为束定;编译时束定又称为静态束定; 拿上面的例子来说,静态联编就是在编译的时候就决定了main函数中调用的是A中的func还是B中的func。一旦编译完成,那么他们的映射关系就唯一确定了。 
    (2).动态联编
           编译程序在编译阶段并不能确切地知道将要调用的函数,只有在程序执行时才能确定将要调用的函数,为此要确切地知道将要调用的函数,要求联编工作在程序运行时进行,这种在程序运行时进行的联编工作被称为动态联编,或动态束定,又叫晚期联编;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();//输出“I am shape”
        oneshape.draw();//输出“I am circle”
    }
    

    4.动态联编 
            虚函数是动态联编的基础;虚函数是成员函数,而且是非静态的成员函数;虚函数在派生类中可能有不同的实现,当使用这个成员函数操作指针或引用所标识的对象时,对该成员函数的调用采用动态联编方式,即:在程序运行时进行关联或束定调用关系; 
            动态联编只能通过指针或引用标识对象来操作虚函数;如果采用一般的标识对象来操作虚函数,将采用静态联编的方式调用虚函数; 
            如果一个类具有虚函数,那么编译器就会为这个类的对象定义一个指针成员,并让这个指针成员指向一个表格,这个表格里面存放的是类的虚函数的入口地址;比如:一个基类里面有一些虚函数,那么这个基类就拥有这样一个表,它里面存放了自己的虚函数的入口地址,其派生类继承了这个虚函数表,如果在派生类中重写/覆盖/修改了基类中的虚函数,那么编译器就会把虚函数表中的函数入口地址修改成派生类中的对应虚函数的入口地址;这就为类的多态性的实现提供了基础; 虚函数按照其声明顺序存放于虚函数表中; 父类的虚函数存放在子类虚函数的前面; 多继承中,每个父类都有自己的虚函数表; 子类的成员函数被存放于第一个父类的虚函数表中。
     

    展开全文
  • C++:静态联编和动态联编

    千次阅读 2016-09-07 16:00:53
    C++:静态联编和动态联编标签(空格分隔): c++C静态联编和动态联编 指针引用类型的兼容性 虚拟成员和动态编联 为什么C中默认的编联方式是静态编联 动态编联如何工作 注意事项 友元 没有重定义 重新定义将隐藏...

    C++:静态联编和动态联编

    标签(空格分隔): c++



    在程序中如何确定应该调用哪一个函数呢?在C++中,将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编(binding)。在编译过程中进行的联编被称作静态联编(static binding),又称为早期联编(early binding)。然而对于虚拟成员来说,编译就就无法确定具体应该调用哪一个方法了。于是C++引入了另一种联编机制——动态联编(dynamic binding),也叫作晚期联编(late binding)1

    指针和引用类型的兼容性


    在C++中,不允许一种类型的指针指向另一种类型。然而,指向基类的引用或者指针却可以引用派生类对象,而不必进行显示的类型转换。这种由基类指针指向派生类对象的转换方式被称作向上类型转换(upcasting),这使得公有继承不需要进行显示的类型转换。与之相反的过程——将基类指针或引用转换为派生类指针或者引用——被称作向下类型转换(downcasting)。如果不适用强制类型转换则向下类型转换是不被允许的。

    虚拟成员和动态编联


    如果在基类没有将成员声明为virtual,则方法调用时将根据指针类型选择。因为指针类型在编译时已知,因此编译器编译时会之间关联到非虚拟的成员方法。然而,如果基类将一个成员声明为virtual,则编译器就无法在编译时确定到底指针当前指向的是哪一个对象了。

    为什么C++中默认的编联方式是静态编联


    这里从两个方面考虑:
    首先考虑效率,为了使程序能够在运行时期进行决策,必须采取一些方法来跟踪基类指针或引用指向的对象类型,这增加了额外的处理开销。所以采取静态编联能够显著的提高效率。这也符合C++的设计原则:不为了不使用的特性付出代价。
    其次考虑概念模型,在类设计时,可能包含一些不在派生类中重新定义的成员函数。这样能够显著提升运行效率。

    动态编联如何工作


    通常,编译器处理虚拟函数的方法是:给每一个对象添加一个隐藏成员。隐藏成员中保存了一个指向函数数组的指针。这种指针被称为虚函数表(virtual function table,vtbl)。虚拟函数表中存储了为类对象进行声明的虚函数的地址。例如,基类对象包含一个指针,该指针指向基类中所有的虚函数的地址表。派生类对象将包含一个指向独立地址表的指针。如果派生类提供了虚函数的新定义,该虚函数将保存虚函数的地址;如果派生类没有重新定义虚拟函数,该vtbl将保存函数原始版本的地址。

    注意事项


    友元

    友元函数不能是虚函数,只有成员才能是虚函数。

    没有重定义

    如果派生类没有重定义函数,将使用该函数的基类版本,如果位于派生链中,将使用最新的虚函数版本。

    重新定义将隐藏方法

    如果派生类重写了基类的方法并且修改了参数列表,则基类的方法将不可见。

    class Base{
    public:
        void print(int size);
    };
    class Imp : Base{
    public:
        void print();
    };

    这种不严谨的代码编写方式在一些要求严格的编译器上回造成编译失败。如果能够编译通过,则基类的带参数的show方法将会不可见。

    总结


    • 每个对象都将会增大存储空间的地址;
    • 对于每个类,编译器都创建一个虚函数地址表(数组);
    • 对于每个函数调用,都需要执行一项额外的操作,即到表中查找地址;
    • 在基类中声明的virtual函数,在整个继承链中都是virtual类型的;
    • 如果重新定义继承的方法,应该确保函数定义与基类声明保持一致。但是,如果返回的是某一种基类的指针,则可以重写为派生类的指针。这种特性被称为返回类型协变(covariance of return type);
    • 如果基类声明被重载了,应该在派生类中重定义所有的基类版本。

    1. Stephen Prata.C++ Primer Plus 6th.人民邮电出版社 2016.3 501~507
    展开全文
  • 静态联编和动态联编 将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编. 静态联编: 在编译过程中进行联编, 又叫早期联编 动态联编:编译器必须生成能够在程序运行时选择正确的虚方法的的方式被称为...
  • 按照联编所进行的阶段不同可分为静态联编和动态联编 举个列子 #include<iostream> using namespace std; class A{ public: void fun() { cout<<"123"<<endl; } }; ...
  • 静态联编动态联编

    千次阅读 2020-03-08 16:02:43
    在C++中,由于重载的缘故,编译器必须查看函数参数以及函数名才能确定使用的是哪个函数,然而睡佳佳编译器可以在编译过程中完成这种两边在编译过程中进行联编,被称为静态联编。虚函数是这项工作变得更加困难,使用...
  • 静态联编:  在编译阶段将函数实现函数调用绑定起来称为静态联编,C语言中,所有联编都是静态联编,C++ 一般情况下也是静态联编
  • 动态联编与静态联编的区别

    千次阅读 2019-03-18 14:40:57
    摘要】:本文阐述了静态联编和动态联编的概念区别,通过具体实例分析了实现动态联编的条件,指出了虚函数是实现动态联编的基础。 【关键词】:静态联编动态联编;虚函数 在C++中,联编是指一个计算机程序的...
  • 静态联编:在编译过程中进行联编被称为静态联编,又称早期联编 动态联编:使用虚函数时,程序使用那一个函数是不能在编译时确定的,因为编译器不知道用户选择的那种类型对象。 所以,编译器必须能够在程序运行时选择...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,938
精华内容 6,375
关键字:

静态联编和动态联编