联编的概念

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

意思就是这个函数的实现有多种,联编就是把调用和对应的实现进行映射的操作。按照联编进行的阶段不同,可分为静态联编和动态联编。

静态联编

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

动态联编

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

在 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++的静态联编和动态联编

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

  1. C++的静态联编和动态联编详解

    一.概述: 通常来说联编就是将模块或者函数合并在一起生成可执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存地址,它是计算机程序彼此关联的过程.按照联编所进行的 ...

  2. C++ 多态、虚函数(virtual 关键字)、静态联编、动态联编

    函数重写:(在子类中重写父类中的函数) 父类中被重写的函数  依然会继承  给子类. 子类中重写的函数将覆盖父类中的函数. 通过作用域分辨符  ::  可以访问到父类中的函数. 例如: #includ ...

  3. java动态联编

    JAVA中联编有两种,一种是动态联编,一种是静态联编. 动态联编:也叫多态联编或者是迟后联编,因为到底要调用哪一个函数,在编译时不能确定,而要推迟到运行中确定.也就是说,要等到程序运行时,确定了指针所 ...

  4. C++基础之多态性和动态联编

    (1)多态性是指相同的函数名对应不同的实现.多态性采用两种方式:重载方式和覆盖方式.重载方式表现在函数重载和运算符重载:覆盖方式表现在基类与派生类中相同说明的函数.(2)函数重载要求被重载的函数应该在 ...

  5. c++知识点总结--静态与动态联编

    静态联编是指在编译阶段就将函数实现和函数调用关联起来,因此静态联编也叫早绑定,在编译阶段就必须了解所有的函数或模块执行所需要检测的信息,它对函数的选择是基于指向对象的指针(或者引用)的类型   动态联 ...

  6. 初学c++动态联编

    先看一下什么是C++联编? 我觉得通俗的讲,用对象来访问类的成员函数就是静态联编. 那什么是动态联编: 一般是通过虚函数实现动态联编. 看一个动态联编的例子: 我比较懒,所以直接粘贴了MOOC视频的图 ...

  7. C++构造函数和析构函数调用虚函数时都不会使用动态联编

    先看一个例子: #include <iostream> using namespace std; class A{ public: A() { show(); } virtual void ...

  8. 使用Pods和自定义静态库实现多工程联编

    使用Pods和自定义静态库实现多工程联编 字数607 阅读112 评论0 喜欢0 近来随着公司项目开发的深入,项目的规范也就越来越高了,为了更加方便的管理自定义静态库与pods之间的联系,好好的研究了 ...

  9. 【转载】ASP.NET 内联代码、内联表达式、数据绑定表达式使用方法罗列(形式就是常说的尖括号 百分号 等于号 井号)

    ASP.NET 内联代码.内联表达式.数据绑定表达式使用方法罗列(形式就是常说的尖括号 百分号 等于号 井号) 今天在做渭南电脑维修网的一个小功能时遇到了一些问题,因此特别列出,以备他日之用. 首先对 ...

随机推荐

  1. python之模块调用

    1.以 atm_run 为入口,在atm中的main模块调用config中的settings from config import settings 2.以atm_run为入口,在main中调用log ...

  2. PB的一些记录

    断点设置在函数内,发现返回值没有,需要取消函数内断点才正常 加密--采用矩阵乘法 行列式取+_1的矩阵与逆矩阵其元素都是整数,, 可以使用matlab来找到这些矩阵 A* I   =E       A ...

  3. TabLoaout简单框架

    import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design. ...

  4. Axure RP Xmind

    官方网站下载地址:http://www.axure.com/download 下载地址:http://www.iaxure.com/2941.html 汉化安装:http://www.iaxure.c ...

  5. 完整的SOPC开发流程体验

    课程目标:学习并掌握完整的SOPC开发流程. 开发环境:Quartus15.1 学习内容:1.使用QSYS工具建立能够运行流水灯项目的NIOS II处理器系统 2.在quartus ii中添加NIOS ...

  6. WITH RECOMPILE 和 OPTION(RECOMPILE) 使用上的区别

    在考虑重编译T-SQL(或者存储过程)的时候,有两种方式可以实现强制重编译(前提是忽略导致重编译的其他因素的情况下,比如重建索引,更新统计信息等等), 一是基于WITH RECOMPILE的存储过程级 ...

  7. PHP基础架构

    PHP基础架构 一.PHP简介 PHP是一种非常流行的高级脚本语言,尤其适合Web开发,快速.灵活和实用是PHP最重要的特点.PHP自1995年由Lerdorf创建以来,在全球得到了非常广泛的应用. ...

  8. 用react + redux + router写一个todo

    概述 最近学习redux,打算用redux + router写了一个todo.记录下来,供以后开发时参考,相信对其他人也有用. 注意: 我只实现了Footer组件的router,其它组件的实现方法是类 ...

  9. Linux中vim文本编辑器的介绍和使用方法

    vim主要模式介绍,vim命令模式. 确保系统已经安装了VIM工具 [root@panda ~]# rpm -qf `which vim` [root@panda ~]# rpm -qf `which ...

  10. windows phone开发-windows azure mobile service使用入门

    在使用azure之前,我一直只能做本地app,或者使用第三方提供的api,尽管大多数情况下够用,但是仍不能随心所欲操纵数据,这种感觉不是特别好.于是在azure发布后,我就尝试使用azure来做为个人 ...