服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++的静态联编和动态联编

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

2021-03-27 14:40C++教程网 C/C++

本文阐述了静态联编和动态联编的概念和区别,通过具体实例分析了实现动态联编的条件,指出了虚函数是实现动态联编的基础。

最近在看析构函数的内容,看到一些讲的比较好的文章,这里我也有了一些我自己的体会,在这里一并记录一下。

联编是指一个计算机程序自身彼此关联的过程,在这个联编过程中,需要确定程序中的 操作调用(函数调用) 与 执行该操作(函数) 的代码段之间的映射关系。

意思就是这个函数的实现有多种,联编就是把调用和对应的实现进行映射的操作。

按照联编进行的阶段不同,可分为静态联编动态联编

静态联编

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

动态联编

编译程序在编译阶段并不能确切地指导将要调用的函数,只有在程序执行时才能确定将要调用的函数,为此要确切地指导将要调用的函数,要求联编工作在程序运行时进行,这种在 程序运行时进行的 联编工作被称为动态联编。 C++中,动态联编是在虚函数的支持下实现的 。

静态联编和动态联编都是属于多态性的,他们在不同的阶段对不同的实现进行不同的选择。

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

虚函数这个概念是C++的精华之一。遇到虚函数时要注意:

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

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

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

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

虚析构函数

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
 
using namespace std;
 
class Base
{
  public:
    Base(){ data = new char[10];}
 
    ~Base(){ cout << "destroying Base data[]\n";delete []data;}
  private:
    char *data;
};
 
class Derive: public Base
{
  public:
    Derive(){ D_data = new char[10];}
 
    ~Derive(){ cout << "destroying Derive data[]\n";delete []D_data;}
  private:
    char *D_data;
 };
 
int main()
{
Base *basePtr = new Derive();
 
delete basePtr;
return 0;
}

输出结果:

?
1
2
$ ./a.out
destroying Base data[]

在这个例子中,派生类的析构函数并没有被调用,这在大的项目中就是一个灾难。究其原因是我们在main函数中定义了一个Base的指针,当我们delete一个动态分配的Base指针时,Base指针此时却指向了Derive类型的对象,但编译器还是按照Base类型调用了析构函数,没有执行Derive类型的虚析构函数。修改Base类的析构函数为虚析构函数即可以确保执行正确的析构函数版本。

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

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

延伸 · 阅读

精彩推荐
  • C/C++深入C++拷贝构造函数的总结详解

    深入C++拷贝构造函数的总结详解

    本篇文章是对C++中拷贝构造函数进行了总结与介绍。需要的朋友参考下...

    C++教程网5182020-11-30
  • C/C++使用C++制作简单的web服务器(续)

    使用C++制作简单的web服务器(续)

    本文承接上文《使用C++制作简单的web服务器》,把web服务器做的功能稍微强大些,主要增加的功能是从文件中读取网页并返回给客户端,而不是把网页代码...

    C++教程网5492021-02-22
  • C/C++c/c++实现获取域名的IP地址

    c/c++实现获取域名的IP地址

    本文给大家汇总介绍了使用c/c++实现获取域名的IP地址的几种方法以及这些方法的核心函数gethostbyname的详细用法,非常的实用,有需要的小伙伴可以参考下...

    C++教程网10262021-03-16
  • C/C++OpenCV实现拼接图像的简单方法

    OpenCV实现拼接图像的简单方法

    这篇文章主要为大家详细介绍了OpenCV实现拼接图像的简单方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    iteye_183805102021-07-29
  • C/C++关于C语言中E-R图的详解

    关于C语言中E-R图的详解

    今天小编就为大家分享一篇关于关于C语言中E-R图的详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看...

    Struggler095962021-07-12
  • C/C++C语言实现双人五子棋游戏

    C语言实现双人五子棋游戏

    这篇文章主要为大家详细介绍了C语言实现双人五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    两片空白7312021-11-12
  • C/C++c/c++内存分配大小实例讲解

    c/c++内存分配大小实例讲解

    在本篇文章里小编给大家整理了一篇关于c/c++内存分配大小实例讲解内容,有需要的朋友们可以跟着学习参考下。...

    jihite5172022-02-22
  • C/C++C语言main函数的三种形式实例详解

    C语言main函数的三种形式实例详解

    这篇文章主要介绍了 C语言main函数的三种形式实例详解的相关资料,需要的朋友可以参考下...

    ieearth6912021-05-16