精华内容
下载资源
问答
  • 动态联编
    2022-05-05 08:41:57

    一、什么是联编

    • 联编:将源代码中的函数调用解释为执行特定的函数代码块被称为函数名的联编(binding)。
    • 静态联编: 在编译过程中进行联编被称为静态联编,又称为早期联编。
    • 动态联编:编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为动态联编,又称为晚期联编。

    二、为什么有两种类型的联编以及为什么默认为静态联编?

    • 效率
      动态联编效率低。因为要跟踪基类指针或引用指向的对象类型,增加了额外开销。
      如果类不需要作为基类,则不需要动态联编。
      如果派生类不需要重新定义类的任何方法,不需要动态联编。

      - 概念
      在设计类时,可能包含一些不在派生类重新定义的成员函数。不将这些函数设置为虚函数,有两方面的好处:首先效率更高;其次指出不需要重新定义该函数。

    三、虚函数工作原理

    • 虚函数表:虚函数表是一个存储了类中虚函数地址的数组,虚函数表是在编译阶段生成的,每个类有一个虚函数表。

    通常编译器处理虚函数的方法是:

    • 给每个对象添加一个隐藏成员,这个隐藏成员指向虚函数表,被称为虚表指针,虚表指针是在运行时(创建对象时)生成的。
    • 基类对象的虚表指针指向基类的虚函数表,派生类对象的虚表指针,指向派生类的虚表。
    • 基类的虚表和派生类的虚表是两个独立的虚表。构建派生类的虚表时,如果派生类提供了虚函数的新定义,则虚函数表将保存新函数的地址;如果派生类没有重新定义虚函数,则保存的是基类中的该虚函数的地址;如果派生类定义了新的虚函数,则该函数的地址被添加到虚表中。

    四、虚函数注意事项

    • 构造函数不能为虚函数。因为:1)创建派生类时要调用派生类的构造函数,而不是基类的构造函数,这和虚函数实现多态的目标不符。2)不同于继承基值,派生类不会继承基类的构造函数,而是调用派生类的构造函数需要先调用基类的构造函数。
    更多相关内容
  • 本文阐述了静态联编和动态联编的概念和区别,通过具体实例分析了实现动态联编的条件,指出了虚函数是实现动态联编的基础。
  • 按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。 静态联编是指在编译阶段就将函数实现和函数调用关联起来,因此静态联编也叫早绑定,在编译阶段就必须了解所有的函数或模块执行所需要...
  • 动态联编和静态联编

    千次阅读 2021-10-23 10:08:16
    一、动态联编和静态联编的基本概念 1.基本概念 将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编。在C语言中这个步骤更简单,因为C语言的函数名不允许重复,即没有函数重载这一说,每个函数名都对应...

    一、动态联编和静态联编的基本概念

    将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编。在C语言中这个步骤更简单,因为C语言的函数名不允许重复,即没有函数重载这一说,每个函数名都对应着一个函数代码块。但是在C++中要复杂,因为C++中允许函数重载,必须得根据函数名和形参列表来对应一个函数代码块。C/C++编译器在编译过程就完成了这种联编。在编译过程中进行联编被称为静态联编早期联编)。
    但是虚函数的产生使得静态联编变的困难,因为父类的虚函数允许被子类重写。当我们用一个父类指针指向一个子类对象的时候,编译器编译阶段可以知道父类指针的类型,但是它不能够直接用父类指针的类型中的虚函数作为本次调用的函数代码块。因为可能子类对虚函数进行重写了,这种情况下用户明显是想要调用重写后的函数。那么可能要说了,那编译器通过对象类型调用该类型中的重写后的函数不就可以实现编译阶段早期绑定了?然而,通常情况下,只有在运行时才能确定对象的模型。对于虚函数,编译器要通过动态联编晚期联编)的方式确定对应的函数代码块。即在运行时,通过对象类型确定调用的虚函数的函数代码块。重写了,就调用在对象类型中重写的函数对应的函数代码块,没有重写,那么就调用父类虚函数对应的函数代码块。
    总结:
    对于普通成员函数,如果通过对象调用普通成员函数,编译器直接调用该对象类型中的该函数对应的函数代码块;如果通过指针调用普通成员函数,那么编译器会直接根据指针类型调用该类型中的该函数对应的函数代码块。这些都是在编译阶段确定的,都是静态联编
    对于虚函数,如果通过对象调用虚函数,编译器会直接通过对象的类型如果是通过指针或者引用的方式调用虚函数,那么编译器将无法确定该指针类型中的虚函数对应的代码块是否是用户想要调用的。因为如果是父类指针指向子类对象的话,当子类没有对父类虚函数重写,我们意思肯定是调用父类的虚函数,如果重写了,我们意思肯定是调用子类重写后的函数,这个时候编译器不能直接说因为是父类指针,就直接去调用父类中的虚函数对应的代码块。所以有了虚函数指针和虚函数表的概念,通过运行时查虚函数表的方式,确定要调用的函数的代码块的地址,因为如果子类没有重写这个虚函数的话,虚函数表中放的是父类的虚函数,如果子类对父类的虚函数重写了,那么重写后的函数的地址会覆盖掉父类虚函数的地址,调用的就是重写后的函数了。(对于虚函数指针和虚函数表,请点击此处

    有一种说法,说对于调用普通成员函数来说,都是静态联编,这个没问题。但是说对于虚函数来说,如果是通过对象调用虚函数,不会经过查虚函数表,是静态联编;如果是通过指针调用虚函数,就会经过查虚函数表,是动态联编

    二、指针和引用类型的兼容性

    父类指针可以指向子类对象。指向子类对象的父类指针的使用请点击此处。将子类对象的类型转化为父类对象,为向上强制转换,编译器可以直接隐式转换,而父类对象的类型转换为子类对象的类型,为向下强制转换,必须显示转换。
    隐式向上转换是基类指针或者基类引用可以指向基类对象或派生类对象,因此需要动态联编。

    class A 
    {
    public:
    	virtual void fun() { cout << "A fun()" << endl; }
    };
    
    class B :public A
    {
    public:
    	void fun() { cout << "B fun()" << endl; }
    };
    
    void test1(A* a)
    {
    	a->fun();
    }
    
    void test2(A& a)
    {
    	a.fun();
    }
    
    void test3(A a)
    {
    	a.fun();
    }
    
    int main()
    {
    	B b;
    	test1(&b);
    	test2(b);
    	test3(b);
    	cout << "/***********************************/" << endl;
    	A a;
    	test1(&a);
    	test2(a);
    	test3(a);
    	
    	return 0;
    }
    

    运行结果:
    在这里插入图片描述
    对于test1和test2很好理解。参考指向子类对象的父类指针的使用
    对于test3而言,形参是值拷贝的临时对象,这个值拷贝不针对虚函数指针,也就是说用哪个类创建的对象,这个对象的虚函数指针就指向哪个类的虚函数表,所以对于test3不论是传A的对象还是B的对象,利用拷贝构造函数创建出来的形参是临时对象,且对象隐藏的虚函数指针指向的都是A类的虚函数表。所以调用的都是A类的虚函数中的fun函数,而不是B类的虚函数表中被重写的fun函数。

    三、静态联编和动态联编的效率问题

    既然动态联编这么好用,为什么还要存在静态联编呢?
    (1)效率方面
    静态联编是在编译期间就执行好的,而动态联编是运行期间才开始。不仅如此,动态联编还需要通过查虚函数表,找到虚函数地址,再去这个地址里找虚函数。而且动态联编需要生成虚函数指针(存在对象中),还需要生成虚函数表。所以说动态联编的步骤比静态联编的步骤复杂,而且还需要生成虚函数指针虚函数表。时间和空间都比静态联编消耗的多。
    (2)概念模型方面
    虚函数一般是为了子类涉设计的,预期子类需要重写这个函数,所以将这个函数写成虚函数。然而,有些函数并不需要被子类重新定义,那么父类就没必要将这个函数写成虚函数。效率提高了,还告诉子类我没有把这个设置成虚函数,就是为了告诉你不要重新定义这个函数。

    四、有关虚函数的注意事项

    1.编译器不允许将构造函数设置成虚函数

    构造函数不能是虚函数。因为如果构造函数放到虚函数表中,那么子类创建对象会调用父类的构造函数,然而事实上是子类先调用父类的构造函数,再用自己的构造函数。所以这个不符合继承构造函数调用的逻辑。

    2.有继承关系时,析构函数尽量设置成虚函数

    有继承关系时,特别是如果子类有指针成员变量,我们需要在子类的析构函数中判断指针是否指向堆空间,是的话,我们需要在子类的析构函数中对堆空间进行释放。那么如果不将父类析构函数设置成虚析构函数的话,那么如果用父类指针指向子类对象,父类指针将无法调用子类的析构函数,无法将申请的堆空间释放。(对于为什么父类析构函数写成虚函数,指向子类对象的父类指针就可以调用子类的析构函数,而不把父类析构函数写成虚函数,指向子类对象的父类指针不可以调用子类的析构函数,请点击此处查看指向父类对象的父类指针的使用)
    所以有继承关系时,尽量将析构函数写成虚函数,哪怕这个类不需要用析构函数做什么。

    3.友元不能是虚函数

    虚函数必须是对类的成员函数而言的,不可以将友元函数设置成虚函数。

    4.重新定义问题

    如果派生类重新定义父类的成员函数,那么父类的所用同名的成员函数都被隐藏。包括虚函数。如下代码:

    class A 
    {
    public:
    	virtual void fun(int a) {}
    	void fun() {}
    };
    
    class B :public A
    {
    public:
    	void fun() {}
    };
    
    int main()
    {
    	B b;
    
    	//b.fun(10);//error
    	b.fun();//调用的是子类的成员函数
    	return 0;
    }
    
    

    子类写了一个fun函数,父类的两个fun函数读背隐藏了(注意只要是函数同名,就被隐藏,和函数重载无关)。
    如果再调用父类的fun函数,需要显示调用。
    如下:
    b.A::fun();
    b.A::fun(10);
    如果重新定义继承的方法,应该确保与原来的原型完全相同(函数名,形参列表)。当然,可以将函数返回类型修改。
    (1)只要子类的函数名和形参列表与父类的虚函数相同,编译器就会认为这个是对父函数的虚函数的重写,这个时候如果返回类型和父类不一样,编译器会报错。(除去唯一一个例外:返回类型可以协变,比如父类虚函数返回类型是父类指针(引用)形式,子类重写父类的虚函数,允许将函数的返回类型改为子类指针(引用)形式)。
    (2)如果子类重写父类的虚函数,那么子类的其他重名函数(不论是不是虚函数)也会被隐藏。
    总的来说,只要父类的函数名和子类的函数名相同,都会被隐藏。
    如下示例:

    class A 
    {
    public:
    	virtual void fun() { cout << "A fun()" << endl; }
    	void fun(int a) { cout << "A fun(int)" << endl; }
    };
    
    class B :public A 
    {
    public:
    	int fun(int a, int b) { cout << "B fun(int)" << endl; return 0; }
    	
    	//下面语句错误,由于函数名和形参列表与父类的虚函数一样,
    	// 编译器会认为子类是对父类的虚函数重写
    	// 但是由于返回类型不同,编译器会报错:重写后的函数的返回类型与被重写的虚函数的返回类型不同,而且也不是协变
    	//int fun() {}
    };
    
    int main()
    {
    	B b;
    	b.fun(10,20);
    	//以下两种语句错误
    	// 本来想使用父类的fun函数,但是由于子类有同名函数,父类的所有同名函数都被隐藏,所以找不到,编译器报错
    	//b.fun();//想要调用父类的fun()函数
    	// b.fun(10);想要调用父类的fun(int)函数
    	//调用父类被隐藏的函数,需要告诉编译器这些函数在哪里,如下
    	b.A::fun();    //告诉编译器是调用A类的被隐藏fun()函数
    	b.A::fun(10);//告诉编译器是调用A类的被隐藏的fun(int)函数
    
    	A* aptr = &b; //父类指针指向子类对象,调用的函数都是父类的(还有子类对父类的虚函数重写的函数)
    	aptr->fun();    //A类的fun()
    	aptr->fun(10);//A类的fun(int)
    	return 0;
    }
    
    展开全文
  • 动态联编和静态联编、以及多态

    千次阅读 2022-03-12 14:42:04
    动态联编和静态联编 动态联编和静态联编的存在是为了支持C++的多态性。 静态联编:编译器在编译期就把对象与其在申明时采用的类型绑定起来,就确定函数的调用地址 class A { } class B:public A { } class D:...

    目录

    动态联编和静态联编

    默认参数

    类型转换问题

    多态

    什么是多态?

    多态有什么作用?

    多态的发生条件

    多态的实现原理

    纯虚函数和抽象类

    抽象类的作用

    虚析构函数和纯虚机构函数

    重写,重载,重定义的条件


    动态联编和静态联编

    动态联编和静态联编的存在是为了支持C++的多态性。

    静态联编:编译器在编译期就把对象与其在申明时采用的类型绑定起来,就确定函数的调用地址

    class A
    {
    }
    class B:public A
    {
    }
    class D:public A
    {
    }
    
    void test()
    {
        D* d=new D;//静态联编会把d绑定在D*类型上,因为D*是d的声明类型
        B* b=d;//静态联编会把b绑定在B*类型上
    }

    动态联编:编译器在运行期才把对象与其最后一个指向的类型绑定起来,也叫晚绑定

    class A
    {
    public:
        virtual void test1() {}//被virtual修饰的函数,被叫做虚函数,具有动态联编的特性
        void test2() {}
    };
    class B :public A
    {
    public:
        void test1() {}
        void test2() {}
    };
    
    void test()
    {
        B* b = new B();
        A* a = b;
        b->test2();
        a->test2();
        //提问1
        b->test1();
        a->test1();
        //提问2
    }

    问题:

            1.这里a->test2()和b->test2()调用的是不是同一个函数?

            答: 不是,因为test2()函数不是虚函数,所以发生了静态联编,即在对象声明的那一刻起,其就已经与声明类型绑定了,比如与b绑定的是B*,a绑定的是A*,所以他们调用的分别是B的test2()函数,A的test2()函数

            2.这里a->test1()和b->test1()调用的是不是同一个函数?

            答:是的,因为test1()是一个虚函数,发生了动态联编,即在运行期与最后一个对象的类型绑定起来,这里a是与A绑定的,b通过B* b=d;也与A绑定了起来,他们都指向了A,所以调用的是同一个函数A的test1()函数。

    默认参数

    当默认参数和虚函数一起出现的时候,则较为复杂些,因为虚函数是动态联编默认参数是静态联编(为了执行效率),当他们混合在一起,就要好好分析了。

    class A
    {
    public:
        virtual void test1(int i = 100) { cout << i << endl; }//被virtual修饰的函数,被叫做虚函数,具有动态联编的特性
    };
    class B :public A
    {
    public:
        void test1(int i = 200) { cout << i << endl; }
    };
    void test()
    {
        B* b = new B();
        A* a = b;
        b->test1();
        a->test1();
    }

    由上面可知,d和b调用的test1()函数是同一个函数,那么他们此时的默认参数是多少呢?

    解析:默认参数是静态绑定的,那么在你刚申明的时候就已经与各自的类型绑定了,D* d的时候,d的默认参数是100,B* b的时候,b的默认参数是200。

    类型转换问题

    class A
    {
    }
    class B:public A
    {
    }
    class D:public A
    {
    }

    子类转换为父类(向上转换):编译器认为指针的寻址范围缩小了,所以是安全的

    void test()
    {
        A* a=new B;
    }

     父类转换为子类(向下转换):编译器认为指针的寻址范围扩大了,是不安全的

    void test()
    {
        A* a=new A;
        B* b=a;//err
    }

    多态

    什么是多态?

    同一个操作作用于不同的对象,可以有不同的解释,会产生不同的效果,这就是多态。

    多态有什么作用?

    1. 可以解决项目中的紧耦合问题,提供程序的可扩展性
    2. 不必为每一个子类的功能去编写代码,只需要对抽象的父类进行处理

    多态的发生条件

    1. 有继承
    2. 有重写父类的虚函数
    3. 父类指针指向子类对象
    class People
    {
    public:
        //虚函数
        virtual void Mypro()
        {
    
        }
    };
    
    class xishi :public People
    {
    public:
        //重写父类的虚函数
        virtual void Mypro()
        {
            cout << "西施" << endl;
        }
    };
    
    class wangzhaojun :public People
    {
    public:
        //重写父类的虚函数
        virtual void Mypro()
        {
            cout << "王昭君" << endl;
        }
    };
    
    //同一个操作
    void doLogin(People *pro)
    {
        pro->Mypro();//产生不同的效果,这就是多态
    }
    
    void test()
    {
        People* pro = NULL;
        pro = new xishi;//不同的对象
        doLogin(pro);
        delete pro;
    
        pro = new wangzhaojun;//不同的对象
        doLogin(pro);
        delete pro;
    }
    

    多态的实现原理

    class Animal
    {
    public:
        virtual void speak()
        {
            cout << "Animal speak()" << endl;
        }
        virtual void speak1()
        {
            cout << "Animal speak1()" << endl;
        }
    };
    
    class Dog:public Animal
    {
    public:
        void speak()
        {
            cout << "Dog speak()" << endl;
        }
        void speak1()
        {
            cout << "Dog speak1()" << endl;
        }
    }
    
    void test()
    {
        cout << sizeof(Animal) << endl;
    }
    

    step1.编译器发现类中有虚函数,就会给类分配一个指针,这个指针也占类的内存,且指针指向一张表,这张表也由编译器创建, 表里放类中所有的虚函数的入口地址,这个指针的值就是表的地址,指针属于类,这个表不属于类。

    step2.当子类继承这个类时,不仅仅继承了父类的变量,父类的指针也继承了下来,就是拷贝了一份存在自己的内存中,占4个字节,并且,父类的虚函数表也会一同拷贝一份过来,作为子类自己的虚函数表,但是指针的值现在是没有变的仍然指向的是父类的虚函数表而不是自己的,所以,编译器还在所有子类的构造函数中添加了初始化虚函数指针的代码,让从父类继承下来的指针,指向子类自己的虚函数表,此时虚函数表的内容是没有变的,还是父类写的虚函数。

    step3.当编译器发现子类重写了父类的虚函数时,子类重写的虚函数就会覆盖掉虚函数表中父类的虚函数。

    step4.当父类指针指向子类对象时,发生动态联编,编译器会找到子类的存储空间,通过指针,找到虚函数表,再通过表找到要执行的函数。

    纯虚函数和抽象类

    纯虚函数

    能作为接口存在

    virtual int getnum(int a, int b) = 0;

    抽象类

    类中所有函数都是纯虚函数的类叫抽象类

    class rule
    {
    public:
        //纯虚函数
        virtual int getnum(int a, int b) = 0;
    };
    

     注意:

            1.抽象类不能实例化对象

            2.如果子类继承抽象类,子类必须实现抽象类的所有纯虚函数,不然子类也会变成抽象类

            3.子类重写父类的函数时,必须按父类申明的顺序一样的顺序来重写!

    抽象类的作用

    抽象类可以构成抽象层,抽象层的作用:

            1.依赖倒转:业务层依赖抽象层,实现层依赖抽象层
            2.开闭原则:对修改源代码关闭,对扩展新功能开放

    这样当你要修改业务层的代码时,不用去每个实现层都修改了,而只需要在抽象层修改相应的代码就可以了

    虚析构函数和纯虚机构函数

    问题:当基类指针指向派生类对象时,当基类指针释放派生类对象的时候,不会调用子类的析构函数,因为此时发生了静态联编。

    class Animal
    {
    public:
        Animal()
        {
            cout << "Animal的构造" << endl;
        }
        ~Animal()
        {
            cout << "Animal的析构" << endl;
        }
    
    
    };
    
    class Son :public Animal
    {
    public:
        Son()
        {
            cout << "Son的构造" << endl;
            pName = new char[64];
            memset(pName, 0, 64);
            strcpy_s(pName, strlen("如花")+1, "如花");
        }
        ~Son()
        {
            cout << "Son的析构" << endl;
            if (pName != NULL)
            {
                delete[] pName;
                pName = NULL;
            }
        }
    public:
        char* pName;
    };
    
    void test()
    {
        Animal* animal = new Son;
        delete animal;//我想要的是不仅仅释放掉Animal的空间,还有Son的空间,但发生了静态联编
                      //不会释放掉Son的空间,执行的是Animal的析构函数
    }
    

    解决:使用虚析构函数,使用虚析构函数时,就会调用子类的析构函数了,同时,如果你的类是抽象类,因为抽象类里必须都是纯虚函数,所以需析构函数也要改变为纯虚析构函数

    虚析构函数
    virtual ~Animal()
    {
        cout << "Animal的析构" << endl;
    }
    纯虚析构函数
    virtual ~Animal() = 0;

    纯虚析构函数需要初始化,只能且必须在类外初始化 ,要使用作用域

    Animal::~Animal()
    {
        cout << "Animal的析构" << endl;
    }

    重写,重载,重定义的条件

    重载
            1.同一个作用域
            2.参数个数,参数顺序,参数类型不同
            3.和函数返回值,没有关系
            4.const也可以作为重载条件  //do(const Teacher& t){}  do(Teacher& t)
    重定义(隐藏)
            1.有继承
            2.子类(派生类)重新定义父类(基类)的同名成员(非virtual函数)
    重写(覆盖)
            1.有继承
            2.子类(派生类)重写父类(基类)的virtual函数
            3.函数返回值,函数名字,函数参数,必须和基类中的虚函数一致

    展开全文
  • C++动态联编介绍

    2022-04-10 13:30:29
    更多python、PHP、JAVA、C、C++教程请到友情连接: 菜鸟教程https://www.piaodoo.com 茂名一技http://www.enechn.comppt制作教程步骤 http://www.tpyjn.cn ...目录一、静态联编和动态联编1.指针和引用类型的兼容性2.虚

    更多python、PHP、JAVA、C、C++教程请到友情连接: 菜鸟教程https://www.piaodoo.com

    茂名一技http://www.enechn.com

    ppt制作教程步骤 http://www.tpyjn.cn

    兴化论坛http://www.yimoge.cn

    电白论坛 http://www.fcdzs.com

    表格制作excel教程 http://www.tsgmyy.cn


    学习通 http://www.hssi.net/


    文章转自:公众号:Coder梁(ID:Coder_LT)

    一、静态联编和动态联编

    当我们使用程序调用函数的时候,究竟应该执行哪一个代码块呢?将源代码中的函数调用解释为执行特定的函数代码块这个过程被称为函数名联编(binding)。

    在C语言当中,这非常简单,因为每个函数名都对应一个不同的函数。而在C++当中,由于支持了函数重载,使得这个任务变得更加复杂。编译器必须要查看函数的参数以及函数名才能确定。好在函数的选择以及参数在编译的时候都是确定的,所以这部分联编在编译时就能完成,这种联编被称为静态联编。

    在有了虚函数之后, 这个工作变得更加复杂。因为使用哪一个函数不能在编译时确定了,因为编译器不知道用户将选择哪个类型的对象。所以,编译器必须能在程序运行的时候选择正确的虚函数,这被称为动态联编。

    1.指针和引用类型的兼容性

    在C++当中,动态联编与指针和引用调用方法相关,这是由继承控制的。前文当中说过,公有继承建立的is-a关系,使得我们可以用父类指针或引用指向子类的对象。而在C++当中,是不允许将一种类型的地址赋值给另外一种类型的指针的。

    下面两种操作都是非法的:

    double x = 2.5;
    int *pi = &x; // 非法
    long &r = x; // 非法

    将派生类引用或指针转换成基类的引用和指针称为向上强制转换(upcasting),这种规则是is-a关系的一部分。因为派生类继承了基类当中所有的数据成员和成员函数,因此基类成员能够进行的操作都适用于子类成员,所以向上强制转换是可传递的。

    如果反过来呢?将父类对象传递给子类指针呢?这种操作被称为向下强制转换(downcasting),在不使用强制转换的前提下是不允许的。因为is-a关系通常是不可逆的,派生类当中往往新增了一些数据成员或方法,不能保证在父类对象上一样还能兼容。

    2.虚函数的工作原理

    我们在使用虚函数的时候其实可以不需要知道当中的实现原理,但是了解了工作原理能够帮助我们更好地理解理念。另外在C++相关的开发面试当中经常会问起类似的实现细节。

    通常,编译器处理虚函数的方法是:给每一个对象添加一个隐藏成员,这个成员当中保存了一个指向函数地址数组的指针,这种数组称为虚函数表。

    这个虚函数表中存储了当前这个类对象的声明的虚函数的地址,我们来看一个例子:

    class Human {
    ? ? private:
    ? ? ...
    ? ? ?char name[40];
    ? ? public:
    ? ? ?virtual void show_name();
    ? ? ?virtual void show_all();
    ? ? ?...
    };
    

    class Hero : public Human{
    ? ? private:
    ? ? ?..
    ? ? ? ? char power[20];
    ? ? public:
    ? ? ?void show_all();
    ? ? ? ? virtual void show_power();
    ? ? ?..
    };

    对于Human类型的对象,它当中除了类中声明的内容之外,还会额外多一个指针,指向一个列表,比如是[1024,1222]。

    这里的1024和1222分别是show_nameshow_all两个函数代码块的地址。

    同样Hero子类当中也会有这样一个指针指向一个虚函数的列表,由于我们在Hero子类当中没有重载show_name方法,所以Hero类型的对象中的列表中的第一个元素仍然是1024。由于我们重载了show_all方法,以及我们新增了一个show_power的虚函数,因此它的虚函数列表可能是[1024,2333,3777]

    简单来说,当我们调用虚函数的时候, 编译器会先通过每个对象中的虚函数列表指针拿到虚函数列表。然后在找到对应位置的虚函数代码块的地址,最后进行执行。

    显然这个过程涉及到维护虚函数地址表,以及函数执行时有额外的查表操作,既带来了存储空间的消耗,也带来了性能的消耗。

    到此这篇关于C++动态联编介绍的文章就介绍到这了,更多相关C++动态联编内容请搜索菜鸟教程www.piaodoo.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持菜鸟教程www.piaodoo.com!

    展开全文
  • 静态联编、动态联编定义 将源代码中的函数调用解释为执行特定的函数代码块称为函数名联编。在C语言中,这非常简单,因为每个函数名都对应一个不同的函数。在C++中,由于函数重载的缘故,这项任务变得十分复杂。...
  • C++之动态联编

    千次阅读 2020-04-02 21:31:09
    成为一个可执行程序)的过程,在这个联编过程中,需要确定程序中的操作调用(函数调用)与执行该操作(函数)的代码段之间的映射关系,按照联编所进行的阶段不同,可分为静态联编和动态联编。 静态联编 静态联编又...
  • C++静态联编和动态联编 一、简介 程序调用函数时,将使用哪个可执行代码块呢?编译器负责回答这个问题。将源代码中的函数调用解释为执行特定的函数 代码块被称为函数名联编。 在C语言中,这非常简单,因为每个函数名...
  • C++-动态联编

    2021-02-24 16:48:40
    静态联编 束定:程序中的操作调用(如函数调用)与执行该操作代码间的关系(简单来说就是在函调用时能找到相应代码的位置) 静态联编就是联编工作在编译阶段完成 也就是在程序运行之前就已经确定好了调用这个函数要...
  • c++ 静态联编和动态联编 首先我们得明白一个概念:什么是联编? 联编(binding)又称绑定,就是将模块或者函数合并在一起生成 可执行代码的处理过程,同时对每个模块或函数分配内存地址,并且对外部访问也分配正确...
  • //Animal& animal = cat //静态联编:如果函数地址在运行钱早就绑定好了,地址早绑定,属于静态联编 //输出动物在说话 //动态联编:想要将函数的地址在运行阶段再绑定,地址晚绑定,属于动态联编 } void test02() {...
  • 先不论动态还是静态,首先需要明确的是关于“联编”的含义,书中有这样的说明: 将源代码中的函数调用解释为执行特定函数代码块被称为函数名联编(binding) 通俗些说,就是指明调用函数的语句调用的究竟是哪一个...
  • 动态联编与静态联编的区别

    千次阅读 多人点赞 2019-03-18 14:40:57
    摘要】:本文阐述了静态联编和动态联编的概念和区别,通过具体实例分析了实现动态联编的条件,指出了虚函数是实现动态联编的基础。 【关键词】:静态联编;动态联编;虚函数 在C++中,联编是指一个计算机程序的...
  • 动态联编

    千次阅读 多人点赞 2015-10-08 20:38:37
    关于 动态联编 和 静态联编 这个概念,自己听了老师上课讲的课仍然没有明白原理。 那么既然这样,只能自己去学习了。 首先我们知道的是,动态联编 和 静态联编 都是多态性的一种体现。 关于面向对象...
  • 静态联编与动态联编

    千次阅读 2020-03-08 16:02:43
    将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编。 在c语言中,因为每个函数名都有对应一个不同的函数。在C++中,由于重载的缘故,编译器必须查看函数参数以及函数名才能确定使用的是哪个函数,...
  • 动态联编说的是程序在运行时才确定调用和被调用者的关系.这个主要是虚函数实现的多态性. 1.静态联编 静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现...
  • 按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。 1. 静态联编        静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行...
  • 主要讲述,静态联编与动态联编,虚函数的声明与使用,虚函数应用实例,纯虚函数,虚析构函数等内容。
  • 动态联编 1.静态联编 联编(binding) 又称绑定, 就是将模块或者函数合并在一起生成可执行代码的处理过程, 同时对每个模块或者函数分配内存地址,并且对外部访问也分配正确的内存地址。 在编译阶段,就将函数...
  • C++虚函数与静态动态联编个人理解
  • 一、静态联编 定义:由于函数重载,编译器必须...所以,编译器必须生成能够在程序运行时选择正确的虚方法的代码,这就是动态联编。 1、虚函数 定义:定义非常简单,只需要在成员函数原型前加一个关键字virtual即可 特
  • 如何用C语言模拟实现C++的动态联编(运行时的多态)
  • C++笔记——虚函数与动态联编 动态联编?静态联编? 程序调用函数时,将使用哪个可执行代码块呢?编译器负责回答这个问题。将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编(binding)。在C语言中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,944
精华内容 9,977
关键字:

动态联编

友情链接: 维纳滤波.zip