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

news2024/12/24 11:29:27

        在C++20的模块引入之前,头文件作为一项技术用于提供给子系统或代码块的接口。头文件最通用的场景是声明要在别的地方定义的函数。声明告诉编译器有一个带有确定名字的实体(函数,变量等)存在。对于函数来说,声明指定了函数如何被调用,声明了参数的数量与类型以及函数的返回类型。定义告诉编译器带有确定名字的实体存在,并且定义了实体本身。对于函数来讲,定义包含了函数的实体代码。所有的定义都是声明,但是并不是所有的声明都是定义。声明,以及类定义,也是声明,通常位于头文件中,典型的是以.h为扩展名。定义,包括非内联类成员的定义,通常位于源文件中,典型的是以.cpp为扩展名。我们一般情况下都会使用模块,但是本节简要讨论使用头文件的令人头疼的方面,比如避免重复定义与循环依赖,因为会在历史遗留代码库中碰到。

1、单一定义规则(ODR)

        单个翻译单元可以只有一个变量、函数、类类型、枚举类型、概念或者模板的定义。对于有些类型,允许多重声明,但是不允许多重定义。更进一步,在整个程序中只能有一个非内联函数与非内联变量的定义。

        对于头文件,比较容易破坏单一定义规则,造成重复定义。下一节我们讨论如何通过头文件避免这样的重复定义。

        在模块之间,破坏单一定义原则是比较难的,因为每一个模块与其它模块进行了更好的隔离。这样做的一个主要原因是在模块中的实体不会从其它模块导出,其他模块拥有模块连接,因此从其它模块的代码无法访问。也就是说,多个模块可以定义自身本地的非导出的实体,可以拥有同样的名字而不会带来问题。另一方面,在非模块源文件中,本地实体缺省有外部连接。当然了,在模块本身之内,仍然需要确保不要破坏单一定义规则。

2、重复定义

        假定A.h包含了Logger.h,定义了Logger类,B.h也包含了Logger.h。如果有一个源文件叫做App.cpp,它包含了A.h与B.h,就会出现Logger类的重复定义,因为Logger.h头文件通过A.h与B.h都包含了。

        这个重复定义的问题可以通过叫做包含哨兵的技术来避免,也叫做头文件哨兵。下面的代码段展示了Logger.h头文件,使用了包含哨兵。在每个头文件的开始,#ifndef指令检查是否定义了特定的键。如果该键已经定义了,编译器就会略过直到匹配的#endif,通常会放置在文件的结尾。如果该键还没有定义,文件处理去定义该键,这样后续包含同样的文件就会被略过。

#ifndef LOGGER_H
    #define LOGGER_H
    class Logger { /* ... */ };
#endif // LOGGER_H

        替代解决方案,目前几乎所有的编译器都支持#pragma once指令,它替换了包含哨兵。在头文件的起始位置放置#pragma once确保它只会被包含一次,因此避免了多次包含头文件导致的重复定义。下面是例子:

#pragma once

class Logger { /* ... */ };

        注意:在一个单独的翻译单元中头文件被多次包含时,包含哨兵与#pragma once指令只是防止了单一定义规则被破坏,而不是多个翻译单元之间。

3、循环依赖

        另一个避免头文件问题的工具是声明传递。如果需要指向一个类,但是不能包含它的头文件(例如,因为它严重依赖你写的类),可以告诉编译器这样的类存在,只是没有通过#include技术提供正式的定义。当然了,在代码中还不能实际使用这个类,因为编译器对这个类一无所知,除非等所有的连接完成之后命名类存在。然而,你仍然可以在代码中使用指针与引用来传递声明类。也可以通过值来声明返回这样的传递声明类的函数,或者拥有传递声明类作为传值函数的参数。当然了,定义了函数的代码与调用该函数的代码都需要包含正确的头文件,它恰当地定义了传递声明类。

        例如,假定Logger类使用了另一个类叫做Preferences,它跟踪用户设置。Preferences类可能反过来使用Logger类,这样就有了一个循环依赖,无法通过包含哨兵解决。在这种情况下需要使用传递声明。在下面的代码中,Looger.h头文件对于Preferences类使用了传递声明,接下来指向Preferences类的就不会再包含它的头文件:

#pragma once
#include <string_view>
class Preferences;    // forward declaration

class Logger
{
public:
    void setPreferences(const Preferences& preferences);
    void logError(std::string_view error);
};

        推荐在头文件中尽可能地使用传递声明而不是包含其它头文件。这样可以降低编译与重编译时间,因为它打破了头文件之间的依赖。当然了,实现文件需要包含正确的头文件,其类型是你要传递声明的;否则的话,编译不会成功。

3、查询头文件是否存在

        查询特定头文件是否存在,使用__has_include(“filename”)或__has_include(<filename>)预处理常量表达式。如果头文件存在结果为1,不存在结果为0.例如,在c++17中<optional>头文件被批准前,有些编译器已经在<experimental/optional>中有了初始版本。可以使用__has_include()来检查下面两个头文件中的哪一个在你的系统中可用:

#if __has_include(<optional>)
    #include <optional>
#elif __has_include(<experimental/optional>)
    #include <experimental/optional>
#endif

4、模块导入声明

头文件不应包含任何模块导入声明。标准严格要求模块import声明必须在文件的开头,在任何其它声明之前,必须不能来自于头文件包含或预处理宏扩展。这使得易于构建系统来发现模块依赖,然后用于决定模块需要构建的顺序。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI笔筒操作说明及应用场景

AI笔筒由来&#xff1a; 在快节奏的现代办公环境中&#xff0c;我们一直在寻找既能提升效率、增添便利&#xff0c;又能融入企业文化、展现个人品味的桌面伙伴。为此&#xff0c;我们特推出专为追求卓越、注重细节的您设计的AI笔筒礼品版&#xff0c;它集高科技与实用性于一身…

【C++】内存管理(二):operator new/delete

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解C的operator new/delete&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 1 new/delete的底层2 new/delete的底层调用顺序3 delete[ ]调用析构函数的次数…

【工具变量】中国制造2025试点城市数据集(2000-2023年)

数据简介&#xff1a;《中国制造2025》是中国ZF于2015年5月8日印发的一项战略规划&#xff0c;旨在加快制造业的转型升级&#xff0c;提升制造业的质量和效益&#xff0c;实现从制造大国向制造强国的转变。该规划是中国实施制造强国战略的第一个十年行动纲领&#xff0c;明确提…

小菜家教平台(一):基于SpringBoot+Vue打造一站式学习管理系统

前言 现在已经学习了很多与Java相关的知识&#xff0c;但是迟迟没有进行一个完整的实践&#xff08;之前这个项目开发到一半&#xff0c;很多东西没学搁置了&#xff0c;同时原先的项目中也有很多的问题&#xff09;&#xff0c;所以现在准备从零开始做一个基于SpringBootVue的…

算法专题:字符串

目录 1. 最长公共前缀 1.1 算法原理 1.2 算法代码 2. 最长回文子串 2.1 算法原理 2.2 算法代码 3. 二进制求和 3.1 算法原理 3.2 算法代码 4. 字符串相乘 4.1 算法原理 4.2 算法代码 1. 最长公共前缀 . - 力扣&#xff08;LeetCode&#xff09; 1.1 算法原理 有以…

非线性数据结构之图

一、有向图&#xff08;Directed Graph&#xff09; 1. 定义 有向图是一个由顶点&#xff08;节点&#xff09;和有方向的边&#xff08;弧&#xff09;组成的图。在有向图中&#xff0c;每条边都有一个起点和一个终点&#xff0c;表示从一个顶点到另一个顶点的关系。 2. 特…

虚拟现实技术课程开发思路

文章目录 组队选题立项分工建模说明&#xff1a;场景说明&#xff1a;交互说明&#xff1a; 结语&#xff1a; 前言&#xff1a;最近学弟学妹们反馈水水老师课程开始上强度了。不仅有翻转课堂&#xff0c;还有理论课实验课都要做东西出来。听说理论课是做什么博物馆什么的&…

FPGA视频GTH 8b/10b编解码转PCIE3.0传输,基于XDMA中断架构,提供工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的PCIE方案我已有的 GT 高速接口解决方案 3、PCIE基础知识扫描4、工程详细设计方案工程设计原理框图输入Sensor之-->芯片解码的HDMI视频数据组包基于GTH高速接口的视频传输架构GTH IP 简介GTH 基本结构GTH 发送和接收处理…

CSS中常见的两列布局、三列布局、百分比和多行多列布局!

目录 一、两列布局 1、前言&#xff1a; 2. 两列布局的常见用法 两列布局的元素示例&#xff1a; 代码运行后如下&#xff1a; 二、三列布局 1.前言 2. 三列布局的常见用法 三列布局的元素示例&#xff1a; 代码运行后如下&#xff1a; 三、多行多列 1.前言 2&…

jmeter结合ansible分布式压测--1数据准备

一、搭建ansible环境 ansible是基于python开发&#xff0c;通过ssh连接客户机执行任务。ansible可以批量系统配置、批量程序部署、批量运行命令等。 1、安装yum install ansible 2、检查ansible的版本:ansible --version 二、利用ansible在其他机器上准备压测数据 1、本地准…

蓬勃发展:移动开发——关于软件开发你需要知道些什么

一、前言 移动开发一直都是软件开发领域中最有趣的领域之一&#xff0c;这是因为&#xff1a; 1、移动开发为“只有一个人”的开发团队提供了一个非常独特的机会&#xff0c;让他可以在相对较短的时间内建立一个实际的、可用的、有意义的应用程序&#xff1b; 2、移动开发也代…

gitmakegdb

git git reset 命令 | 菜鸟教程 (runoob.com) 像嫁接一样 make Makefile | 爱编程的大丙 (subingwen.cn) # 举例: 有源文件 a.c b.c c.c head.h, 需要生成可执行程序 app ################# 例1 ################# app:a.c b.c c.cgcc a.c b.c c.c -o app################# 例…

网络安全认证的证书有哪些?

在网络安全领域&#xff0c;专业认证不仅是个人技术能力的象征&#xff0c;也是职业发展的重要推动力。随着网络安全威胁的日益严峻&#xff0c;对网络安全专业人才的需求也在不断增长。本文将介绍一些网络安全认证的证书&#xff0c;帮助有志于从事网络安全行业的人士了解并选…

初阶数据结构的各种排序方法——冒泡,直接插入,希尔,快排,选择,归并(C语言)

1.交换排序 交换排序基本思想&#xff1a; 所谓交换&#xff0c;就是根据序列中两个记录键值的⽐较结果来对换这两个记录在序列中的位置 交换排序的特点是&#xff1a;将键值较⼤的记录向序列的尾部移动&#xff0c;键值较⼩的记录向序列的前部移动。 1.1冒泡排序 例子&…

qt QFileInfo详解

1、概述 QFileInfo是Qt框架中用于获取文件信息的工具类。它提供了与操作系统无关的文件属性&#xff0c;如文件的名称、位置&#xff08;路径&#xff09;、访问权限、类型&#xff08;是否为目录或符号链接&#xff09;等。此外&#xff0c;QFileInfo还可以获取文件的大小、创…

Charles抓包_Android

1.下载地址 2.破解方法 3.安卓调试办法 查看官方文档&#xff0c;Android N之后抓包要声明App可用User目录下的CA证书 3.1.在Proxy下进行以下设置&#xff08;路径Proxy->Proxy Settings&#xff09; 3.1.1.不抓包Windows&#xff0c;即不勾选此项&#xff0c;免得打输出不…

软件压力测试有多重要?北京软件测试公司有哪些?

软件压力测试是一种基本的质量保证行为&#xff0c;它是每个重要软件测试工作的一部分。压力测试是给软件不断加压&#xff0c;强制其在极限的情况下运行&#xff0c;观察它可以运行到何种程度&#xff0c;从而发现性能缺陷。 在数字化时代&#xff0c;用户对软件性能的要求越…

【Python】【数据可视化】【商务智能方法与应用】课程 作业一 飞桨AI Studio

作业说明 程序运行和题目图形相同可得90分&#xff0c;图形显示有所变化&#xff0c;美观清晰可适当加分。 import matplotlib.pyplot as plt import numpy as npx np.linspace(0, 1, 100) y1 x**2 y2 x**4plt.figure(figsize(8, 6))# yx^2 plt.plot(x, y1, -., labelyx^2,…

进程的调度(超详细解读)

在特别老的操作系统中&#xff0c;进程的调度是根据FIFO调度算法进行调度&#xff0c;先进先出式的调度&#xff0c;其实就是队列&#xff0c;但是不能很好的体现进程的优先级&#xff0c;在上节讲解的进程优先级&#xff0c;知道nice值范围是[-20&#xff0c;19]&#xff0c;然…

【初阶数据结构篇】链式结构二叉树(续)

文章目录 须知 &#x1f4ac; 欢迎讨论&#xff1a;如果你在学习过程中有任何问题或想法&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习。你的支持是我继续创作的动力&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;觉得这篇文章对你有帮助吗&#xff1…