如何对遗留 C++ 代码进行现代化改造?

在这里插入图片描述

C++ 在过去的十年中进步很大,以至于有些人把它看作是一种完全不同的语言,而不是“老旧的遗留 C++”。尽管现代 C++ 依然保留了与原来的准则和基本语法,但这些更新和进步对 C++ 语言和标准库意义重大。

不过,也不是每个人都在使用最新版本的你。诚然,根据 Jetbrains 2021生态系统调查,大多数项目和公司已经颇有进步,并不断向更新的语言规范迈进。不过,尽管 C++ 20 已经发布,但调查中仍有 12% 的开发者使用 C++98/03,40% 的开发者“卡在”C++11上,而绝大多数(42%)的开发者使用 C++17。
在这里插入图片描述
图片来源 : Jetbrains 2021生态系统调查

调查数据清楚地展现了,在大多数情况下,开发者和公司并不会迅速升级到最新语言,就算升级,也不会立马更新到最新的 C++ 20,而是选择 C++ 14 或 C++ 17。这样说来,目前使用 C++ 17 的群体还算是个例外,因为他们迫切地想要升级到 C++ 20。

在这里插入图片描述

图片来源 : Jetbrains 2021生态系统调查

为什么需要选择更新的版本呢?

使用现代C++版本的理由很多:

  • 语法更新,代码库功能更全,编码的可能性更丰富,也意味着代码可能更优质,性能更强大。
  • 使用成为标准库中部分的标准和功能,而不是使用外部库,让代码维护更简单,开发者之间进行代码转换更方便。
  • 跟上最新趋势,开发者喜欢不断进步。你当然也不希望团队落后,不然他们会寻找其他项目。
  • 尽管没有定论,但一般来说,老旧的语言版本,意味着语言无法跟上操作系统和编译器版本发展。因此,这反过来增加额外的兼容性和安全性风险。

为什么企业中的代码语言发展滞后?

在这里插入图片描述公司仍然使用旧版本语言(甚至还有 C++ 98/03 或 C++ 11)的主要原因,是编译器还支持这些语言。如果你的产品依赖于某一确切的操作系统,可能这个操作系统是你安装的一部分。但这个系统缺乏一个现代的 C++ 编译器,那么问题就很容易出现。

另一种情况是,升级代码库很麻烦,且不值得,因为产品是一个老旧的遗留产品。有时候,公司也没有人力能够去开展这类升级,而且,升级也有风险。

但不论原因为何,语言版本滞后迟早都是个问题。因为,你会慢慢积累技术债务,代码也变得过时。假设你要开发一个新的特性,或者修复一个缺陷,你会因为旧的遗留 C++ 版本代码“卡”住,进退两难。这些旧的版本让你无法使用许多现代 C++ 代码用例,你也无法使用现代 C++ 语法库和实用程序。

既然我们了解了升级到现代 C++ 的重要性,现在,我们来讨论迁移的路径。

迁移策略:从传统 C++ 升级为现代 C++,保持系统完好无损

第一个障碍可能是操作系统。旧版本的操作系统可能带有不支持现代 C++ 的C++ 编译器。例如,旧版 VxWorks 仅支持旧版 WindRiver Diab 编译器,这个编译器支持 C++03,但不支持 C++11。在当前的 VxWorks 版本中,Diab 编译器是以 LLVM 为基础的,支持现代 C++ 版本。此外,VxWorks 的较新版本还支持 GNU 和 ICC 编译器。要使用这两个编译器的任何一个,你都要先升级操作系统,如果你使用的是旧版的 VxWorks,这是项目的自身需求。这只是一个例子,其他遗留环境也会出现类似的操作系统障碍。

有时你也需要支持旧版的操作系统,可能客户有需求,或者因为你需要用旧的硬件设备。管理客户需求,宣布软件产品的终结,这些不在本文内容范围之内,但我想提醒的是,这也是一个必经的过程。

如果在迁移到新环境后,还必须支持旧操作系统和旧编译器,那么你可以将新旧环境共享的代码进行隔离,让这些代码保持原样,同时迁移与新环境相关的部分。

如果你正在转换操作系统或编译器,你的第一步是在新的操作系统(如果需要的话)上用新的编译器编译代码,而不改变 C++ 版本。在用更新的 C++ 版本进行编译之前,编译器的简单更改可能会引发一些需要后续进行修复的问题。这些问题通常是因为不同编译器在各方面的要求不同。一些编译器可能对某些方面更宽松,但另一些编译器可能更严格,这些差异会引发编译错误,需要后续修复。你可以使用错误标志(例如删除,-pedantic 或 -Wall)来避免这类错误,同时在项目中添加一个注释,说明这些错误标志将在后面重新添加。

旁注:如果你的项目使用的是静态或动态代码库,无论是内部还是外部代码库,如果这些库是使用旧版编译器编译的(尤其是使用另一个编译器),那么就可能会出现兼容性问题。ABI(应用程序二进制接口)可能会中断,这可能需要你使用新的编译器重新编译相应的代码库。

如果你使用的是同一操作系统和编译器,但只需升级 C++ 版本,接下来要做的是是设置适当的标志,让构建使用更高级的C++版本(例如,-std=C++17)。因为 C++ 版本大多是向后兼容的(在少数情况下,语言本身可以进行错误修复,要求更改不是向后兼容),所以代码只能按原样编译。但是,如果编译器在某些方面要求更加严格,如有些代码之前不算错误,但现在却算作错误,那么可能会引发编译问题。同样,你可以删除一些错误标志来避免这些问题,这通常属于迁移项目管理的内容,帮你将所需的修复推迟到后面的阶段。

一旦你的项目编译并运行新的 C++ 版本,你就可以长舒一口气了。不过,也不能高兴得太早。接下来是全面认证的步骤,要确保所有产品都能正常工作。在另一个环境中重建代码,导致行为改变,其中可能的原因多种多样。所有这些都是源自隐藏在代码中的缺陷,而这些缺陷现在慢慢显现出来。可能与未定义行为代码有关,因为不能代码行为无法确定最终导致错误;或者因为一些未定义行为代码,导致编译器选择了特定的行为,但在后续更改编译器时,编译行为发生改变。如果你的应用程序是多线程的或基于计时的,则在重建后,一般需要更改计时。环境中的任何微小变动,都会引起新的竞争条件和数据竞争,这些竞争关系可能以前只是隐藏在代码中。

完整的认证之后,一切才真正开始:你的代码可以以更新的C++ 版本进行编译、运行和工作。

你可能已经享受到了升级的快乐。例如,即使代码没有任何更改,从 C++ 98/03 升级到 C++ 11 或更高版本,使用 Rvalue 和移动语义的标准库容器可能会更高效!为了充分利用升级的优势,你也希望使用高级 C++ 特性。不过,问题在于从哪里入手。

再小的增量改变,都需要一个理由

如果它能正常运行,那么没有理由去改变它,尽量别碰它。

  • 在将所有回路更换为以范围为基础的回路之前,请按兵不动。
  • 不要急于用 auto 定义所有变量声明。
  • 先将新算法添加到标准库,替换现有循环。
  • 没必要将所有旧分配转移到智能指针。

我的建议是,在添加新特性或修复缺陷时,在代码中首先找到最有利的更改,然后非常小心地更改其他地方。例如,如果是从 C++ 98/03 升级到 C++ 的任何更新版本, 其中最有利的更改,以及最需要搜索查看的地方包括:

  • 不在零规则下的类(如,它们具有用户定义的复制构造函数、赋值运算符或析构函数),并且可以通过移动构造函数和移动赋值运算符获得性能增益。
  • 在不使用 std::move 的情况下,移动数据结构的位置(当然,添加 std::move 时应非常小心,并且仅在相关的情况下)。
  • 将对象传递给数据结构,并将其放入数据结构中的位置。
  • 尽量将外部库替换为内部标准库,降低对外部库的依赖性。

不论什么时候,更改都需要代码审查。即使开发者觉得并没有做什么重大更改,但所有更改都有一定风险,需要进行审查。

慢慢地向现代 C++ 的新特性转移,在很长一段时间内,代码会处于一种很奇怪的状态,有时在完全相同的文件中,部分是新的代码,部分则看起来过时了。但没关系,罗马也不是一天建成的。

总结

只要你有一个活跃的 C++ 项目,那么向现代 C++ 版本迁移就是必经之路。落后的版本将不断产生技术债务,你不能使用新的 C++ 语法,也无法利用现代版本的外部代码和库。另外,你也会发现,你的开发者流失率会很高,因为开发者总是希望开展新的项目,使用更新的语法和工具。

我们讨论了解决迁移项目的策略。你应该记住,这是一个需要身体力行的项目,不是一个副业,也不是一个下午就完成的事情。你需要周密的计划和明确的目标。如果做得好,则所有的担心都不会实现。

需要记住的一点是,仅仅迁移到一个更新的语言版本是不够的。如果团队不知道如何正确使用现代 C++ 的特性和能力,那么语言的错误使用也将引发更多问题,或者因各种琐碎的使用细节导致工作效率低下。

在迁移项目期间,需要进行很多构建。你需要开展大量编译,大部分情况下,我们需要先修复再重新编译。然而,所有这些构建和编译都需要时间,因此项目进展可能会有所延迟。关键时刻,就需要 Incredibuild 发挥功效。使用Incredibuild 构建加速服务,你可以更快、更好地完成这样的版本迁移。当然,Incredibuild 还有更多优秀的功能,如果你还没有使用过,那它肯定能为你提供一个好的用例。

最后,如果你决定迁移到现代 C++,那就勇敢点。直接跳转到 C++20,即使你的编译器仅部分支持 C++ 20,也是值得的。所有新的 C++ 版本都有很多新的惊喜,C++ 20 带来了四个重大的改变:概念、模块、范围和协程。

点击了解 Incredibuild 的 C++构建加速方案,并获取试用 License!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/339687.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity 工厂方法模式(实例详解)

文章目录 在Unity中,工厂方法模式是一种创建对象的常用设计模式,它提供了一个接口用于创建对象,而具体的产品类是由子类决定的。这样可以将对象的创建过程与使用过程解耦,使得代码更加灵活和可扩展。 工厂模式的主要优点如下&…

快速排序(三)——hoare法

目录 ​一.前言 二.快速排序 hoare排法​ 三.结语 一.前言 本文给大家带来的是快速排序,快速排序是一种很强大的排序方法,相信大家在学习完后一定会有所收获。 码字不易,希望大家多多支持我呀!(三连+关…

PADS自动导出Gerber文件 —— 双面板

视频地址:PADS_2层PCB板(双面板) 快速出GERBER光绘文件实战视频教程_哔哩哔哩_bilibili 像pads做封装不用做阻焊层,因为在出GERBER文件的时候调用了焊盘,并在焊盘的基础上增加了几个mil来做阻焊层。 出Gerber文件之前一定要先铺铜并且检查无错…

双指针算法专题

前言 双指针算法入门,干就完了 下面的题目都是来自灵神的基础算法精讲,有思路不清晰的地方,可以去看讲解。 灵茶山艾府的个人空间-灵茶山艾府个人主页-哔哩哔哩视频 (bilibili.com) 相向双指针 1.两数之和 题目链接:167. 两数之…

清华大模型Chatglm2-6B的微调方法和微调模型使用方式(非常仔细,值得借鉴)

一、下载chatglm2-6b的项目代码和模型 1、下载chatglm2-6b的项目 方法一、chatglm2-6b的项目下载地址: https://github.com/THUDM/ChatGLM2-6B方法二、百度网盘提取chatglm2-6b的项目: 链接:https://pan.baidu.com/s/1BEwUhiIJlB4SJrGw7N…

力扣:474. 一和零(动态规划)(01背包)

题目: 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。 示例 1: 输入&#…

MacOS受欢迎的数据库开发工具 Navicat Premium 15 中文版

Navicat Premium 15 Mac是一款数据库管理工具,提供了一个全面的解决方案,用于连接、管理和维护各种数据库系统。以下是Navicat Premium 15 Mac的一些主要功能和特点: 软件下载:Navicat Premium 15 中文版下载 多平台支持&#xff…

【UE5】第一次尝试项目转插件(Plugin)的时候,无法编译

VS显示100条左右的错误,UE热编译也不能通过。原因可能是[名字.Build.cs]文件的错误,缺少一些内容,比如说如果要写UserWidget类,那么就要在 ]名字.Build.cs] 中加入如下内容: public class beibaoxitong : ModuleRules …

140:vue+leaflet加载here地图(v2软件多种形式)

第140个 点击查看专栏目录 本示例介绍如何在vue+leaflet中添加HERE地图(v2版本的软件),并且含多种的表现形式。包括地图类型,文字标记的设置、语言的选择、PPI的设定。 v3版本和v2版本有很大的区别,关键是引用方法上,请参考文章尾部的API链接。 直接复制下面的 vue+leaf…

哈尔滨游“出圈”,上市公司谁在冰雪经济掘金?

今年冬季,冰雪游热度不减,哈尔滨成为最大赢家。 仅在元旦假期,根据携程数据显示,黑龙江元旦假期订单量同比增长490%,其中,哈尔滨元旦旅游订单同比增长631%。 受哈尔滨旅游市场出圈影响的出圈,…

仿真机器人-深度学习CV和激光雷达感知(项目2)day5【作业1与答案1】

文章目录 前言作业1答案1 前言 💫你好,我是辰chen,本文旨在准备考研复试或就业 💫本文内容是我为复试准备的第二个项目 💫欢迎大家的关注,我的博客主要关注于考研408以及AIoT的内容 🌟 预置知识…

Science Robotics: 意大利IIT仿生软体机器人实验室研制具有自适应行为的软体生长机器人

FiloBot通过模仿攀爬植物的生长方式——通过在顶端增加材料来构建身体,实现在难以预测和复杂的环境中的导航。这种设计理念的核心在于能够适应多种地形并克服障碍,特别适用于密集森林或杂乱区域这样的非结构化环境。机器人使用添加制造技术(特…

【 CSS 】基础 2

“生活就像骑自行车,想要保持平衡,就得不断前行。” - 阿尔伯特爱因斯坦 CSS 基础 2 1. emmet 语法 1.1 简介 Emmet语法的前身是 Zen coding,它使用缩写,来提高 HTML / CSS 的编写速度, VSCode 内部已经集成该语法。…

GIS项目实战06:超详细Node.js安装及系统环境配置

简单的说 Node.js 就是运行在服务端的 JavaScript。 Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台。 Node.js 是一个事件驱动 I/O 服务端 JavaScript 环境,基于 Google 的 V8 引擎,V8 引擎执行 Javascript 的速度非常快,性能…

超全的测试类型详解,再也不怕面试答不出来了!

在软件测试工作过程中或者在面试过程中经常会被问到一些看起来简单但是总是有些回答不上的问题,比如你说说“黑盒测试和白盒测试的区别?”,“你们公司做灰度测试么?", ”α测试和β测试有什么不一样?“&#xff0…

python+PyQt5 网口功能测试

UI界面: 源代码: # -*- coding: utf-8 -*-# Form implementation generated from reading ui file NetOpeningWinFrm.ui # # Created by: PyQt5 UI code generator 5.15.2 # # WARNING: Any manual changes made to this file will be lost when pyuic5…

k8s集群加入一个master2--kubeadm方式

已经有一个集群: 192.168.206.138 master 192.168.206.136 k8s-node1 192.168.206.137 k8s-node2 kubectl get nodes -o wide 新加入一个master2节点 192.168.206.139 master2 一、初始化系统参数 139 master2 上 #在136、137、138上添加hosts“” echo "…

Nat Med | 特定密码子的KRAS突变预测

导语 今天给同学们分享一篇实验文章“Codon-specific KRAS mutations predict survival benefit of trifluridine/tipiracil in metastatic colorectal cancer”,这篇文章发表在Nat Med期刊上,影响因子为82.9。 结果解读: KRAS突变作为FTD/T…

【Java网络编程02】套接字编程

【Java网络编程02】套接字编程 1. Socket套接字 概念:Socket套接字,就是系统提供用于实现网络通信的技术,是基于TCP/IP协议的网络通信基本操作单元。基于Socket套接字的网络程序开发就是网络编程。 分类: 我们可以把Socket套接字…

GO 的那些 IDE

“程序员为什么要使用 IDE”,在一些社区论坛,经常可以看到这样的提问。关于是否应该使用IDE,每个人都有着自己的看法。 早期,程序的开发并不需要 IDE,那是以机器码编程为主的时代。后来随着计算机行业发展&#xff0c…