C++20 模块化(Modules)

news/2024/9/19 22:08:19 标签: c++

C++20 引入的模块化(Modules)是一个重大改进,旨在取代传统的头文件机制,提高编译速度、代码可维护性以及项目的可扩展性。模块化为 C++ 提供了一种更现代化的代码组织方式,避免了头文件中常见的宏污染、重复编译和复杂的依赖管理问题。

概念与背景

在 C++20 之前,C++ 项目是通过头文件.h.hpp)与源文件.cpp)的组合来组织代码的。头文件定义了类、函数、模板等声明,而源文件包含它们的实现。然而,头文件机制存在多个问题:

  • 重复编译:每次编译器遇到 #include 指令时,都会重新处理整个头文件。
  • 编译时间长:大型项目中,成千上万的头文件包含会导致严重的编译性能问题。
  • 符号冲突:头文件中的宏可能会与其他代码发生冲突,产生难以调试的错误。

模块化解决了这些问题,提供了更清晰的代码分隔、减少重复编译并减少符号冲突的风险。

模块的基本概念

1. 什么是模块?

模块是一个新的 C++ 编译单元,它由一个或多个模块文件组成,定义了导出(export)或隐藏的声明。模块打破了传统的头文件/源文件模式,允许开发者显式地控制哪些声明可以被外部使用。

模块的特点:

  • 明确的接口和实现:模块可以显式导出哪些部分是公共接口,哪些部分是私有实现。
  • 无宏污染:模块之间的依赖不会通过宏或预处理器泄漏。
  • 增量编译:模块化加快了增量编译的速度,减少了重复解析。
2. 模块文件结构

模块主要包括两种文件:

  • 模块接口文件(Module Interface File):包含导出给其他模块或翻译单元使用的声明。通常使用 .cppm 扩展名。
  • 模块实现文件(Module Implementation File):包含模块内部实现,通常以 .cpp 形式存在。

模块的语法和使用

1. 创建一个模块

模块通过 module 关键字定义模块名称。模块接口文件通常是 .cppm 文件,但这不是强制的。

// math.cppm - 模块接口文件
export module math;

export int add(int a, int b) {
    return a + b;
}
  • export module 定义了模块的名称 math
  • export 关键字用于导出 add 函数,使其可供其他模块或翻译单元使用。
2. 使用模块

模块通过 import 关键字来导入,而不是像传统头文件那样使用 #include

// main.cpp - 使用 math 模块
import math;

int main() {
    int result = add(2, 3);
    return 0;
}

与头文件不同,import 语句会将模块编译为二进制形式,避免了重复编译带来的性能损失。

3. 模块中的隐藏实现

模块允许将某些实现隐藏,而不导出给外部使用。只有 export 导出的内容才能被其他模块或翻译单元访问。

// math.cppm
export module math;

export int add(int a, int b);  // 导出的函数声明

int multiply(int a, int b) {   // 私有函数,不导出
    return a * b;
}

export int add(int a, int b) {
    return a + b;
}

在这个例子中,add 函数是导出的,而 multiply 函数则是私有的,只能在模块内部使用。

模块化的更多细节

1. 模块分区(Module Partitioning)

C++20 允许将模块分成多个子模块,称为模块分区(Module Partitions)。模块分区帮助大型模块进行更细粒度的代码组织。

// math.cppm - 模块接口文件
export module math;
export import :operations;  // 导入子模块

export int add(int a, int b);

// operations.cppm - 模块分区文件
module math:operations;

int multiply(int a, int b) {
    return a * b;
}

在这个例子中,模块 math 分为两个部分,主模块和 operations 子模块。主模块导入了子模块并公开了它的接口。

2. 模块与传统代码的混合使用

模块化的引入并不意味着完全抛弃传统的头文件机制。在模块化过渡期,模块与头文件可以共存。模块化代码仍然可以包含头文件,并与 #include 共用。

// math.cppm
export module math;
#include <iostream>  // 模块中仍可以使用头文件

export void print_message() {
    std::cout << "Hello from module" << std::endl;
}
3. 模块的依赖管理

模块通过 import 显式管理依赖关系,而不是隐式地通过 #include 传播依赖。这种显式依赖管理使得模块的依赖关系更加清晰,并且减少了不必要的编译。

// math.cppm
export module math;
import <iostream>;  // 导入标准库模块

export void print_message() {
    std::cout << "Message from math module" << std::endl;
}
4. 标准库模块化

C++20 模块化支持标准库模块。例如,标准库可以通过 import <iostream> 的形式来使用。

import <iostream>;

int main() {
    std::cout << "Hello, Modules!" << std::endl;
}

 


http://www.niftyadmin.cn/n/5666202.html

相关文章

SpringSecurity原理解析(五):HttpSecurity 类处理流程

1、SpringSecurity 在spring boot中与SSM项目中基于配置文件的区别 通过前边的笔记我们可以知道&#xff0c;在传统的SSM项目中 SpringSecurity的使用是基于配置文件 的&#xff0c;然后spring 容器初始化的时候将 SpringSecurity 中的各种标签解析成对应的Bean对象&#xff0c…

linux 操作系统下的dhclient命令介绍和案例使用

linux 操作系统下的dhclient命令介绍和案例使用 dhclient 是 Linux 系统中用于动态主机配置协议&#xff08;DHCP&#xff09;客户端的命令。它的主要功能是从 DHCP 服务器获取网络配置&#xff0c;包括 IP 地址、子网掩码、默认网关和 DNS 服务器等信息 dhclient 命令概述 …

MutationObserver详解+案例——深入理解 JavaScript 中的 MutationObserver:原理与实战案例

目录 深入理解 JavaScript 中的 MutationObserver&#xff1a;原理与实战案例 一、MutationObserver 简介 二、MutationObserver 的工作原理 1、基本用法 2、observe 方法的配置项 三、实战案例 案例 1&#xff1a;监控动态内容加载 案例 2&#xff1a;监控属性变化 案…

缓存穿透 问题(缓存空对象)

文章目录 1、缓存穿透2、缓存空对象3、AlbumInfoApiController --》getAlbumInfo()4、AlbumInfoServiceImpl --》getAlbumInfo()5、RedisConstant6、请求缓存不存在的数据 1、缓存穿透 2、缓存空对象 3、AlbumInfoApiController --》getAlbumInfo() GetMapping("getAlbumI…

佩戴舒适且适合学生党的蓝牙耳机?分享开放式耳机排行榜前十名

对于追求佩戴舒适的学生党来说&#xff0c;高性价比的开放式耳机是一个不错的选择。因为这类耳机不仅提供了良好的通风性&#xff0c;还减少了长时间佩戴带来的闷热感。而且开放式耳机也通常具有轻巧的设计&#xff0c;能够减轻了耳朵的压力。而且&#xff0c;还需要考虑到学生…

【多线程】深入剖析线程池的应用

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;多线程 / javaEE初阶 还记得我们一开始引入线程的概念&#xff0c;就是因为进程太“重”了&#xff0c;频繁创建销毁进程的开销是非常大的。而随着计算机的发展&#xff0c;业务上对性能的要求越来越…

IDEA Project不显示/缺失文件

问题&#xff1a;侧边栏project 模式下缺少部分文件 先点close project 打开项目所在目录&#xff0c;删除目录下的.idea文件夹 重新open project打开这个项目即可解决

C++:动态内存分配(new、delete 相比 malloc、free的优势)与运算符重载

动态内存分配与运算符重载 一、动态内存分配&#xff08;一&#xff09;内存的分类&#xff08;二&#xff09;动态内存分配函数(1)new 和delete 的使用&#xff08;1&#xff09;new 的原理&#xff08;2&#xff09;delete 的原理 2、 operator new与operator delete&#xf…