联编的概念

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

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

静态联编

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

动态联编

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

在 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. 20175316 盛茂淞 2018-2019-2 《Java程序设计》实验一 Java开发环境的熟悉 实验报告

    20175316 盛茂淞 2018-2019-2 <Java程序设计>实验一 Java开发环境的熟悉 实验报告 一.实验要求 1.使用JDK编译.运行简单的Java程序: 2.使用IDEA ...

  2. 2019.02.26 bzoj4311: 向量(线段树分治+凸包)

    传送门 题意: 支持插入一个向量,删去某一个现有的向量,查询现有的所有向量与给出的一个向量的点积的最大值. 思路: 考虑线段树分治. 先对于每个向量处理出其有效时间放到线段树上面,然后考虑查询:对于两 ...

  3. vue操作的填坑之旅

    1.渲染时图片存在,但损坏,会出现破损图,界面很丑呀 解决方案:调用vue显示的图片的时候会有一些图片404的问题,为了界面的美观,这里进行图片错误是显示默认图片的处理,下面贴出代码 data: { ...

  4. MySQL之二 yum安装及初识

      安装   yum install mysql-server chkconfig -list mysqld 查看mysqld服务是否为开机启动 chkconfig mysqld on 设为开机启动 ...

  5. js实现粒子特效,particles.js的使用

    今天偶然看到了一个比较炫酷的js网页.是粒子特效的,就试着用了用.一下是步骤,方便以后查看使用. 1.在网站下载源码https://github.com/VincentGarreau/particle ...

  6. navicat for mysql安装与破解

    Windows系统的电脑 navicat for mysql 的安装软件以及破解包 方法/步骤     从网上下载需要用到的navicat for mysql 的安装软件以及破解包.   双击navi ...

  7. SQL Server使用证书最小粒度授权

    最近在项目中某个功能遇到了阻塞造成的Time out错误.所以需要在执行该功能的时候开启一个线程检测SQL Server中阻塞会话.但是程序本身使用的sql帐号本身不具备VIEW Sever Stat ...

  8. 9.2 翻译系列:数据注解特性之---Column【EF 6 Code First系列】

    原文链接:http://www.entityframeworktutorial.net/code-first/column-dataannotations-attribute-in-code-firs ...

  9. H5+.Net Webapi集成微信分享前后端代码 微信JS-SDK wx.onMenuShareTimeline wx.onMenuShareAppMessage

    说明: 1/因为赚麻烦这里没有使用数据库或服务器缓存来存储access_token和jsapi_ticket,为了方便这里使用了本地的xml进行持久化这两个值以及这两个值的创建时间和有限期限. 2/每 ...

  10. PICE(6):集群环境里多异类端点gRPC Streaming - Heterogeneous multi-endpoints gRPC streaming

    gRPC Streaming的操作对象由服务端和客户端组成.在一个包含了多个不同服务的集群环境中可能需要从一个服务里调用另一个服务端提供的服务.这时调用服务端又成为了提供服务端的客户端了(服务消费端) ...