当前位置: 首页 > article >正文

C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(1)

        在使用模块之前,头文件用于提供代码重用的接口。头文件确实有许多问题,比如避免同一头文件的多重包含以及确保头文件的包含顺序正确。还有,简单的#include,例如,<iostream>就添加了几千行代码,编译器不得不编译。如果几个源文件#include <iostream>,所有这些翻译单元变得更大了。这还只是包含了一个简单的头文件。想像一下,如果需要<iostream>,<vector>,<format>等等。

        模块解决了所有这些问题,甚至更多。模块被import的顺序并不重要。模块一旦被编译为进制格式,每当该模块被import进行另一个源文件时,编译器就可以使用。这与头文件是一个明显的对比,头文件是每当编译器碰到#include那个头文件时都要进行一而再再而三的编译。因此,模块可以极大节省编译时间。当在模块中修改了内容时,也节省了增加的编译时间,例如,修改一个模块接口文件中的导出函数实现,不需要触发使用那个模块的用户的重新编译。模块不受外部定义宏的影响,任何在模块中定义的宏对模块之外的任何代码不可见,也就是说,模块是自我隔离的。因此,推荐如下:

        注意:有了以上讨论的好处,如果编译器支持模块,新写的代码应该使用模块来结构化代码成为逻辑上分隔的构造块。

        如果可能的话,历史遗留代码也可以慢慢转换为模块。然而,遗留代码太多了,许多第三方库还不支持模块,现在也不是所有的编译器完全支持模块。基于这些原因了解传统的头文件是如何工作的依然重要。这就是本章仍然要包含头文件的讨论的原因。

        注意:如何编译模块与编译器相关。咨询编译器的文档来学习特定编译器的模块工作方式。

        注意:如果你的编译器不支持模块,那就只能将我们的例子转化为非模块的代码了,但还是建议你与时俱进,跟上时代的脚步吧,找一个支持模块的编译器,使你的工作事半功倍;而不是因循守旧,事倍功半。

1、将代码非模块化

        如果想要使用还不完全支持模块的编译器来编译本博客中的代码,可以按如下步骤将代码非模块化:

  • 重命名.cppm模块接口文件为.h头文件。
  • 在每个.h头文件的顶部添加#pragma once。
  • 去除export module xyz声明
  • 替换module xyz声明为#include来包含相应的头文件。
  • 替换import与export import声明为合适的#include指令。如果代码使用了import std;那就需要用#include指令来包含所有需要的单个头文件了。
  • 去除任何export关键字。
  • 去除所有的module;,它指出了全局模块部分的开始。
  • 如果函数定义或变量定义出现在一个.h头文件中,在前面添加inline关键字。

2、标准命名的模块(c++23)

        通过导入标准命名模块std可以访问c++标准库的任何东东。该命名模块使得整个标准库可用,包括所有的定义在<cstddef>中的C功能。然而,只要通过std命名空间就可以使用所有的C功能。对于遗留代码,可以考虑导入std.compat命名模块,它导入了所有的std import的,同时使得std命名空间与全局空间的C功能可用。在新的代码中不推荐使用std.compat。

3、模块接口文件

        模块接口文件定义了由模块提供的功能的接口,通常以.cppm作为文件扩展名。模块接口文件以声明标示文件是由一个特定的名字定义的模块开头。这叫做模块声明。模块名字可以是任何合法的c++标识符。名字可以包含.但是不能以.开始或结束,并且不能一行中包含多个.。合法的名字的例子是datamodel,mycompany.datamodel,mycompany.datamodel.core,datamodel_core,等等。

        注意:目前,还没有模块接口文件的标准扩展名。然而,大多数编译器支持.cppm(C++模块)扩展名,所以我们也这么用。可以检查一下你的编译器使用的是什么样的扩展名。

模块需要显式声明要把什么导出去,也就是说,当客户端代码导入模块时什么是可见的。模块可以导出任何声明,例如变量声明,函数声明,类型声明,using指令,以及using声明。还有,导入声明也可以被导出。从模块中导出实体通过export关键字来完成。从模块中没有导出的东东只在模块自身内可见。所有导出的实体的集合叫做模块接口。

        下面是一个模块接口文件的例子,名字叫做Person.cppm,定义了一个person模块,导出了一个Personal类。注意,它导入了由std提供的功能。

export module person;    // Named module declaration

import std;                // Import declaration

export class Person        // Export declaration
{
public:
	explicit Person(std::string firstName, std::string lastName)
		: m_firstName{ std::move(firstName) }
        , m_lastName{ std::move(lastName) } { }

	const std::string& getFirstName() const { return m_firstName; }
	const std::string& getLastName() const { return m_lastName; }

private:
	std::string m_firstName;
	std::string m_lastName;
};

        在标准术语中,所有东东以命名模块声明开始(上面代码段中的第一行),直到文件结尾,叫做模块范围。

        Person类可以通过导入person模块来使用,如下(test.cpp):

import person;
import std;

using namespace std;

int main()
{
	Person person{ "Kole", "Webb" };
	println("{}, {}", person.getLastName(), person.getFirstName());
}

        任何东东都可以从模块导出,只要它有名字。例子为类定义,函数原型,类枚举类型,using声明与指令,命名空间,等等。如果命名空间使用export关键字显式导出,该命名空间中的所有东东也自动导出。例如,下面的代码段导出了整个DataModel命名空间;因此,没有必要再显式导出单个类与类型别名:

export module datamodel;

import std;

export namespace DataModel
{
	class Person { /* ... */ };

	class Address { /* ... */ };

	using Persons = std::vector<Person>;
}

        也可以使用export block导出一个声明的整块,下面是一个例子:

export
{
    namespace DataModel
    {
        class Person { /* ... */ };
        class Address { /* ... */ };
        using Persons = std::vector<Person>;
    }
}


http://www.kler.cn/a/378995.html

相关文章:

  • 大数据之Hadoop集群
  • HTML 框架
  • django的models使用介绍。
  • IMU应用于监测进食
  • Php实现钉钉OA一级审批,二级审批
  • ChatGPT 和 RAG(检索增强生成)的区别;ChatGPT 和 RAG 的联系
  • 网络安全法详细介绍——爬虫教程
  • 华为设备配置端口隔离实验
  • FreeRTOS 中的列表与列表项详解
  • java基础day08
  • 数据结构,问题 B: 反向输出
  • 探索智能投顾:正大金融数据分析如何优化市场策略
  • DICOM标准:CR图像模块属性详解——计算放射线照相术(CR)及其在DICOM中的表示
  • 《数字图像处理基础》学习05-数字图像的灰度直方图
  • VMware workstation的3种网络类型
  • 【Hive sql 面试题】统计Top3歌单以及每个Top3歌单下的Top3歌曲(难)
  • 【青牛科技】GC4931P替代A4931/Allegro在水泵、筋膜枪、吸尘器和电动工具中的应用
  • 多角色AI代理的一次尝试- AI代码助手
  • Redis 数据备份与恢复
  • GOF的C++软件设计模式的分类和模式名称
  • 系统架构设计师(软考高级)一站式通关课程
  • JSON交互处理
  • 网络规划设计师-(4)数据调制与编码
  • 基于matlab的人脸识别系统设计与仿真
  • 【反射率】-- Lab 转换(excel)
  • 力扣排序242题 有效的子母异位词