news 2026/4/16 17:57:45

C++23中的模块应用说明之三深入分析和混合编程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++23中的模块应用说明之三深入分析和混合编程

一、模块的深入分析

在模块的学习中,英文文档中反复出现translation unit,它其实就是模块单元(module unit)的组成者。可以直译为“翻译单元”,也可以根据模块的特点理解为“迁移单元”。
C++中对模块的应用来说,是一个革命的性的变化。所以影响最大的有两种场景:

  1. 编译处理
    为了理解编译的流程,需要先了解模块的整体划分,它主要由模块的接口单元(moudle interface unit)、模块实现单元(module implementation unit)、模块分区 (module partitions)和BMI (binary module interface)编译生成的二进制接口文件组成。
    也就是说,模块分为模块的定义和实现两部分,有点类似头文件和cpp文件。其同样也存在着模块的声明和定义一体的情况(在一个文件中)。特别是在某些小规模的应用场景下,开发者不愿意组织成不同的文件形式进行处理(即主分区接口和实现单元),所以又提供了私有模块分区这一情况。这都对编译器提出出了较强的要求。
    下面以一个主程序依赖模块B,而B又依赖A的情况进行说明:
    首先,编译器将编译模块的接口单元(B.ixx,包括主模块接口单元,先进行分区接口的编译,然后再依次聚合成主模块接口),生成BMI文件(B.gcm)。即通过解析接口单元代码,处理export的声明有效性来生成对应的二进制接口文件(前面的例程中的std.gcm)。在这个文件中,包含着模块定义中对外声明的所有接口信息,可以理解为一种头文件件的声明(没有实现内容);
    其次,编译器开始处理实现单元,首先验证gcm文件接口的声明与实现单元中是否保持一致(比如函数的签名等),然后编译实现单元(如B.cpp)生成目标文件(.o文件),但此过程不会有新的gcm文件生成
    再次,编译开始处理模块的依赖模块的编译,比如B依赖A模块,则开始检查A的BMI是否成生,如未生成则开始编译A模块。然后解析B模块中import A中的接口信息与A.gcm中的是否保持一致。编译生成目标文件(A.o)且不生成新的BMI。即其方式与主编译单元方式一致(如果有更深层的编译则依次进行即可)。
    最后,编译主程序并链接模块B,先从B.gcm导入接口信息并验证,如果通过,则编译主程序生成目标文件(main.o),然后在链接过程中,将三个目标文件即A.o,B.o,main.o链接成可执行文件。
    编译器在处理模块间的依赖时,是通过显式的依赖方式来构建依赖图的,从而保证编译的依赖顺序(大家是不是想到了传统的动态库中的手动控制库的依赖顺序的问题以及头文件顺序的问题),防止出现类似库依赖时顺序颠倒就会导致链接错误的情况。另外,模块的接口变化仅处理模块A及其依赖相关模块的编译,而内部的变化则只影响模块本身的编译(想想头文件的污染)。
    由于模块提供了增量编译的功能,所以其避免了重复编译和解析,提高了编译的效率。
  2. 与头文件混合处理
    模块的引入与头文件的使用并不是一种二选一的结果,而一种可以混合存在的形式,毕竟庞大的C++代码库和框架不会允许出现一种推翻性的新技术点的应用。在前面的例程中已经提供了对头文件和模块共同使用的方式。那么如何才能更好的解决新老代码的整合呢?
    首先,在新工程中引入模块封装为模块接口单元;其次,对强使用、相对简单的旧代码优先引入模块机制;利用命名模块来处理与传统宏定义的冲突;最后,对一些很少应用,特别复杂的不进行模块的引入。

二、模块的结构组织

在前面学习的基础上,很容易在实际的工程中进行模块的组织架构处理:

  1. 抽象对外导出接口及其相关功能描述说明
  2. 以模块分区和分片段为基础划分出最底层的实现单元
  3. 以层级(树结构)来处理功能实现中模块(子模块间)的依赖(防止循环依赖和重复导出)
  4. 划分命名和匿名模块来处理一些特殊场景的处理

通过上述的组织,就可以实现编译的解耦,保证了功能实现上的隔离。并可以为导出控制提供更好的实现方式。

三、混合编程

新技术的引进往往是渐进性的,模块机制亦是如此。混合编程既可以兼顾模块机制的各种优势(如解决依赖顺序、提高编译效率等)又可以兼容老的头文件库和框架,让开发者不至于陷入重写老代码和编写新代码的混合状态。
下面给出一个具体的例程:

// legacy.h#pragmaonce#include<string>#include<iostream>class LegacyDemo{public:voiddisplay(conststd::string&msg);};intadd(inta,intb);// legacy.cpp#include"legacy.h"voidLegacyDemo::display(conststd::string&msg){std::cout<<"old "<<msg<<std::endl;}intadd(inta,intb){returna+b;}// new_module.cppmexport module new_module;// 全局模块片段:包含传统头文件module;// 开始全局模块片段#include"legacy.h"export module new_module;#include<iostream>#include<string>export namespace ModuleDemo{exportintadd(inta,intb);export class DemoClass{private:intret_=0;public:DemoClass();intsub(inta,intb);};}// new_module.cppmodule new_module;//#include <cmath>namespace ModuleDemo{intadd(inta,intb){returna+b;}DemoClass::DemoClass(){}intDemoClass::sub(inta,intb){ret_=a-b;returnret_;}}// main.cpp#include<iostream>#include"legacy.h"import new_module;intmain(){// 使用头文件机制std::cout<<"legacy add : "<<add(10,20)<<std::endl;LegacyDemo ld;ld.display("Hello include!");// 使用模块机制std::cout<<"moudle add : "<<ModuleDemo::add(30,40)<<std::endl;ModuleDemo::DemoClass calc;std::cout<<"计算器结果: "<<calc.sub(5,6)<<std::endl;return0;}

编译的方法如同前文一致,不再重复。大家可以在前面的环境中进行验证即可。
说明:在本次编译中遇到了新问题,如果在模块的实现文件中增加了未使用的头文件,在直接用命令编译时没有问题但使用CMake编译时则会报编译错误。

四、总结

在学习了模块的相关开发处理后,就出现了一个必须面对的问题即如何在实际的工程中组织相关的模块结构。正如反复说明的,正规化和系统化才是一切工程开发的最终的目的。特别是目前软件的规模已经达到了空前巨大,并且C++还有庞大的历史遗产的情况下,引入模块编程是一个解决传统问题的好的方式。
模块本身是一个相对较完整的组织体系(但为了更方便,可以在其内部继续分区,正如在进程时可以细分线程一样)。不同的粒度可以让模块的组织结构看上去更清晰。这样,在不同的层次和不同的角度来控制模块的开发和应用,会让其在设计和实现上更加灵活。通过整合模块机制与传统的头文件包含机制,可以更好的提高代码整体的编译效率和安全性。大家可以在实际的工程中借鉴这种思想,通过混合编程对相关工程在整体上进行思考和设计。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 9:19:40

终极指南:Semgrep Docker容器化代码安全扫描方案

在当今快速迭代的开发环境中&#xff0c;如何高效实施代码安全检测已成为技术团队面临的核心挑战。面对多语言混合技术栈和复杂部署环境&#xff0c;传统安全工具往往力不从心。本文将为你揭秘基于Docker的Semgrep部署方案&#xff0c;帮助你在5分钟内搭建完整的静态分析环境。…

作者头像 李华
网站建设 2026/4/16 9:18:50

VoxCPM-1.5-TTS-WEB-UI支持长文本输入,突破语音合成长度限制

VoxCPM-1.5-TTS-WEB-UI&#xff1a;如何让长文本语音合成变得高效又自然 在有声书平台动辄需要处理数万字文稿、在线教育机构批量生成课程音频的今天&#xff0c;传统的文本转语音&#xff08;TTS&#xff09;系统越来越显得力不从心。你有没有遇到过这样的情况&#xff1a;输入…

作者头像 李华
网站建设 2026/4/16 9:21:01

系统学习Multisim14.3模拟电路仿真中的探针功能

探针不只是“看”电压——深度玩转Multisim14.3的实时监测艺术你有没有过这样的经历&#xff1a;在仿真一个放大电路时&#xff0c;明明参数都设好了&#xff0c;结果输出却异常&#xff1b;想查问题&#xff0c;只能反复运行瞬态分析、导出波形、放大细节……一圈操作下来&…

作者头像 李华
网站建设 2026/4/16 10:07:28

VoxCPM-1.5-TTS-WEB-UI支持语音合成任务依赖库管理

VoxCPM-1.5-TTS-WEB-UI&#xff1a;让高保真语音合成触手可及 在智能语音助手、有声书平台和虚拟主播日益普及的今天&#xff0c;用户对“像人一样说话”的语音系统提出了更高要求。机械感强、语调生硬的传统TTS已难以满足需求&#xff0c;而真正自然流畅的声音生成又往往伴随…

作者头像 李华
网站建设 2026/4/15 10:03:58

终极指南:用MacBook凹槽打造个性化音乐控制中心

终极指南&#xff1a;用MacBook凹槽打造个性化音乐控制中心 【免费下载链接】boring.notch TheBoringNotch: Not so boring notch That Rocks &#x1f3b8;&#x1f3b6; 项目地址: https://gitcode.com/gh_mirrors/bor/boring.notch 你是否想过MacBook屏幕顶部的凹槽除…

作者头像 李华
网站建设 2026/4/16 11:01:53

Pico TTS轻量级引擎?Android系统内置

Pico TTS轻量级引擎&#xff1f;Android系统内置“&#xff1a;CosyVoice3开源语音克隆技术解析 在智能手机、智能音箱乃至儿童手表中&#xff0c;语音播报早已无处不在。然而你是否注意到——同样是“你好&#xff0c;我是小助手”&#xff0c;有的声音机械生硬&#xff0c;有…

作者头像 李华