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

联编的概念

联编是指一个计算机程序自身彼此关联的过程,在这个联编过程中,需要确定程序中的操作调用(函数调用)与执行该操作(函数)的代码段之间的映射关系。
意思就是这个函数的实现有多种,联编就是把调用和对应的实现进行映射的操作。按照联编进行的阶段不同,可分为静态联编和动态联编。

静态联编

静态联编工作是在程序编译连接阶段进行的,这种联编又称为早期联编,因为这种联编实在程序开始运行之前完成的。在程序编译阶段进行的这种联编在编译时就解决了程序的操作调用与执行该操作代码间的关系。

动态联编

编译程序在编译阶段并不能确切地指导将要调用的函数,只有在程序执行时才能确定将要调用的函数,为此要确切地指导将要调用的函数,要求联编工作在程序运行时进行,这种在程序运行时进行的联编工作被称为动态联编,或称动态束定,又叫晚期联编。

在 C++ 中动态联编需要虚函数的支持,这是因为虚函数的工作原理决定的,而正是因为使用了虚函数来实现动态联编,也让动态联编的效率略低于静态联编。通常,编译器处理虚函数的方法是: 给每个对象添加一个隐藏成员,隐藏成员保存了一个指向函数地址数组的指针 ,这个数组就是虚函数表(virtual function table, vtbl)。虚函数表中存储了为类对象进行声明的虚函数的地址,调用虚函数时,程序将查看存储在对象中的 vtbl 地址,然后转向相应的函数地址表,如果使用类声明中定义的第一个虚函数,则程序将使用数组中的第一个函数地址,并执行具有该地址的函数。

虚函数这个概念是 C++ 的精华之一,遇到虚函数时要注意以下几点:

1.定义一个函数为虚函数,不代表函数为不被实现的函数(可以有自己的实现)

2.定义它为虚函数是为了允许用基类的指针来调用子类的这个函数(提供了基类调用子类函数的方式)

3.定义一个函数为纯虚函数,代表函数没有被实现(声明后面接=0,例如:virtual func() = 0 此时派生类必须要实现此虚函数)

4.具有纯虚函数的类是抽象类,不能用于生成对象(即不能实例化),只能派生,它派生的类如果没有实现纯虚函数,那么他的派生类还是抽象类。

虚析构函数

虚析构函数顾名思义就是将析构函数定义为虚函数。如果我们在派生中分配了内存空间,但是基类的析构函数不是虚析构函数,就会发生内存泄漏。看下面的例子:

#include <iostream>

using namespace std;

class Base{
public:
    virtual void print(){
        cout << "This is Base's print function" << endl;
    }

    /* 对比加与不加 virtual 析构函数的调用情况 */
    ~Base(){
//  virtual ~Base(){
        cout << "The destructor of Base" << endl;
    }
};

class Derived : public Base{
public:
    void print(){
        cout << "This is Derived's print function" << endl;
    }

    ~Derived(){
        cout << "The destructor of Derived" << endl;
    }
};

int main()
{
    Base *p = new Derived();
    p->print();

    delete p;

    return 0;
}

不加 virtual 的运行结果:

加上 virtual 的运行结果:

在上面程序示加上 virtual 时编译器还是按照 Base 类型调用了析构函数,没有执行 Derived 类的虚析构函数,就造成了内存泄露。修改 Base 类的析构函数为虚析构函数后实现了多态,就可以确保执行正确的析构函数,完成资源的释放。

总结一下关于虚函数的一些常见问题:

1.虚函数是动态绑定的,也就是说,使用虚函数的指针和引用能够正确找到实际类的对应函数,而不是执行定义类的函数,这就是虚函数的基本功能。
2.构造函数不能是虚函数。而且,在构造函数中调用虚函数,实际执行的是父类的对应函数,因为自己还么有构造好,多态此时是被 disable 的。
3.析构函数可以是虚函数,而且,在一个复杂类结构中,这往往是必须的。
4.将基类中的一个函数定义为纯虚函数,实际上是将这个类定义位抽象类,不能实例化对象。
5.纯虚函数通常没有定义体,但也可以拥有。(如果 Base 的析构函数为纯虚函数,那么也可以在类外定义 Base::~Base(){…} 的方式来定义其定义体)
6.析构函数可以是纯虚的,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。
7.非纯的虚函数必须有定义体,不然是一个错误。
8.派生类的 override 虚函数定义必须和父类完全一致,除了一个特例,如果父类返回值是一个指针或引用,子类 override 时可以返回这个指针(或引用)的派生。如在 Base 中定义了 virtual Base* fun(){ return this; },在 Derive 中可以定义 virtual Derive* fun(){ return this; }。

补充一个具有定义体的纯虚函数的例子

#include <iostream>

using namespace std;

class Base{
public:
    virtual void print() = 0;
};

void Base::print(){
        cout << "This is Base's fun" << endl;
}

class Derived : public Base{
public:
    void print(){
        /* 调用基类的纯虚函数 */
        Base::print();
        cout << "This is Derived fun" << endl;
    }
};

int main()
{
    Base *T = new Derived();

    T->print();

    delete T;

    return 0;
}

程序输出:

本篇文章参考:阿里云 - C++的静态联编和动态联编

原文地址:https://www.cnblogs.com/GyForever1004/p/8443241.html

时间: 2024-11-09 13:39:40

C++的静态联编和动态联编的相关文章

C++ 虚函数、静态联编和动态联编、抽象类

 //C++ 虚函数.静态联编和动态联编.抽象类 #include<iostream> #include<string> using namespace std; class People { private: string name; int age; public: People(string name_, int age_):name(name_), age(age_){} People(){cout << "基类默认构造函数" <&

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

联编是指一个程序自身彼此关联的一个过程.按照联编所进行的阶段不同,可分为静态联编和动态联编两种. 静态联编 静态联编是指在程序编译连接阶段进行联编.这种联编又称为早期联编,这是因为这种联编工作是在程序运行之前完成的. 编译时所进行的联编又称为静态束定.束定是指确定所调用的函数与执行该函数代码之间的关系. 下面来看一个静态联编的程序例题: <span style="font-size:18px;">#include <iostream> using namespa

【转】C++多态篇1一静态联编,动态联编、虚函数与虚函数表vtable

首先,说起多态就必须要讲静态联编,动态联编.这俩也叫静态绑定和动态绑定.有些书比如C++ Primer也叫静态类型和动态类型.谭浩强写的C++程序设计直接叫静态多态性和动态多态性. 文章转载自:原文链接

静态联编和动态联编初认识。

多态性: 静态联编多态性(编译时的多态性)通过函数,运算符的重载实现的(系统根据形参的个数来实现编译的多态性) 动态联编多态性(运行时的多态性)通过继承,虚函数(当运行时才能实现对象与函数的联编)C++规定:动态联编是在虚函数的支持下实现的 条件:指向派生类对象的基类指针或者引用派生类对象的基类引用(调用虚函数) 动态联编: 1 #include <iostream> 2 using namespace std; 3 class shape{ 4 public: 5 void virtual

静态联编和动态联编

1.联编是指对于相同名字的若干个函数的选择问题 2.实例 #include <iostream> using namespace std; const double PI = 3.1415926; class Point { private: double x,y; public: Point(double i,double j) { x = i; y = j; } double Area() { return 0; } }; class Rectangle:public Point//矩形类

静态联编,动态联编,类指针之间的关系,虚函数与多态性,纯虚函数,虚析构函数

1.静态联编,是程序的匹配,连接在编译阶段实现,也称为早期匹配.重载函数使用静态联编. 2.动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编.switch语句和if语句是动态联编的例子. #include<iostream> void go(int num) { } void go(char *str) { } //class //::在一个类中 class A { public: void go(int num) { } void go(char *str) { } }; void

静态联编与动态联编

联编是指一个程序模块.代码之间相互关联的过程. 静态联编,是程序的匹配.链接在编译阶段实现,也称早期匹配.重载函数就使用静态联编.编译的阶段 动态联编是指程序联编推迟到运行时候进行,又称晚期匹配.switch.if语句就是动态联编的例子.执行的阶段 原文地址:https://www.cnblogs.com/xiangtingshen/p/11616105.html

java动态联编

JAVA中联编有两种,一种是动态联编,一种是静态联编. 动态联编:也叫多态联编或者是迟后联编,因为到底要调用哪一个函数,在编译时不能确定,而要推迟到运行中确定.也就是说,要等到程序运行时,确定了指针所指向对象的类型时,才能够确定. 静态联编:静态联编是指联编工作出现在编译连接阶段,这种联编又称作早期联编,因为这种联编工作是程序开始运行之前完成的. 静态联编的条件:类方法(静态方法),类变量(静态类型变量). EXM: public class Par_Test{  //基类 public sta

【CPP-NOTE】动态联编

多态中动态联编特性 联编是指一个计算机程序的不同部分彼此关联的过程.按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编. 源代码中的函数调用解释为执行特定的函数代码被称为函数名联编(binding). 在编译过程中进行联编被称为静态联编(static binding),又称早期联编(early binding).  编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为动态联编(dynamic binding), 又称为晚期联编(late binding).