C++ 中到底是应该include .h文件还是应该include .cpp文件

在阅读一个较大的解决方案中,对于其他文件夹下的.h和.cpp文件,有时候#include“XXX.h”文件,有时候是#include“XXX.cpp”文件,而且二者还不能更换。下面就好好分析一下他们二者的区别。
 

测试

测试:XXX.h和XXX.cpp有没有在解决方案里的差别

       如果 .h 文件和 .cpp 文件都已经添加在解决方案里,只要在 main 的头文件中 include 对应的 .h 文件即可。
  如果 .h 文件和 .cpp 文件不在解决方案里,可能在其他文件夹里,单独 include.h 文件就会报错“error LNK2019: 无法解析的外部符号”。

        但是单独 include.cpp 就能运行了,而此时当前 main.cpp 里面有没有对应的 .h 头文件都可以正确运行(因为 .h 文件里有 #pragma once 防止多次定义),

        这个很好解释,include 就是把对应的代码拷贝一份进去,拷贝之后,自定义函数的定义本来就在使用前,有没有头文件的声明都可以。为了验证 include 是不是就是将程序拷贝,做了实验验证。

        在自定义函数使用前 #include“XXX.h” 用于函数的声明(意思是在 main.cpp 最前面 #include“XXX.h),在 main 函数结束之后#include“XXX.cpp”,运行果然正确。
  初步结论:说明当.h文件和.cpp文件不在解决方案里的时候,#include就是纯粹的拷贝复制工作。但是当 .h 和 .cpp 文件在解决方案的情况需要进一步验证。

        同时也说明,.cpp 文件有没有包含进工程文件的情况是不同的。

探究

按照所有查到的信息都说明一点内容:凡是#include的头文件,都是把对应的文件信息拷贝复制一份进来。显然这个笼统的概念是不对的。

  源程序->预处理->编译和优化->生成目标文件->链接->可执行文件
  参考之前我转载的3篇博文C++编译与链接的三部曲(强烈推荐看一看)。大致总结如下:
 

预处理(简单替换)

  预处理做如下工作:

预处理器主要负责以下的几处:
1.宏的替换
2.删除注释
3.处理预处理指令,如#include,#ifdef

编译和优化(高级语言->汇编语言)

词法分析 – 识别单词,确认词类;比如int i;知道int是一个类型,是一个关键字以及判断i的名字是否合法。
语法分析 – 识别短语和句型的语法属性;
语义分析 – 确认单词、短语和句型的语义特征;
代码优化 – 修辞、文本编辑;
代码生成 – 生成译文。
内联函数的替换就发生在这一阶段。

编译的另一个重要方面就是编译单元
  什么是编译单元呢?简单来说一个cpp文件就是一个编译单元。
  在集成式的IDE中,我们往往点击一下运行便可以了,编译的所有工作都交给了IDE去处理,往往忽略了其中的内部流程。
  事实上编译每个编译单元(.cpp)时是相互独立的,即每个cpp文件之间是不知道对方的存在的(不考虑#include “xxx.cpp" 这种奇葩的写法)。
  编译器会分别将每个编译单元(.cpp)进行编译,生成相应的obj文件,然后链接器会将所有的obj文件进行链接,生成最终可执行文件内部链接与外部链接。
  这里能作为单独编译单元的是添加进工程的cpp文件,外部文件夹的cpp文件并不单独成为编译单元。
 

生成目标文件(汇编语言->二进制机器语言)

  汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。
  在最终的目标文件中,编译器把一个cpp编译为目标文件的时候,除了要在目标文件里写入cpp里包含的数据和代码,还要至少提供3个表:

未解决符号表;
导出符号表;
地址重定向表

未解决符号表提供了所有在该编译单元里引用但是定义并不在本编译单元里的符号及其出现的地址;
  导出符号表提供了本编译单元具有定义,并且愿意提供给其他编译单元使用的符号及其地址。
  地址重定向表提供了本编译单元所有对自身地址的引用的记录。
  这就是不同cpp之间的通讯方式。

从这个定义方式上看,主cpp文件和其他所有cpp文件都是平等的关系,只不过主cpp里面包含了main函数而已,而main函数和其他所有函数也是平等的,只不过只有main函数可以作为工程的入口函数而已。

链接(汇总成一个目标文件)

由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
  由此,我们可以理解了经常报错的一些情况,就是未解决符号表和导出符号表之间没有定义或者重复定义的情况的。
 

外部文件夹的h和cpp文件

外部文件夹的cpp文件并不能生成单独的编译文件,所以,不参与未解决符号表和导出符号表的生成,而#include外部的h头文件也只是复制进来头文件中的函数声明而已,没有定义。所以当需要使用外部文件夹的cpp文件的时候,直接在头部加上#include cpp文件,或者在头部#include h文件,尾部#include cpp文件,就相当于我们平时写的自定义函数一样。
 

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

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

相关文章

Linux内核(十四)Input 子系统详解 IV —— 配对的input设备与input事件处理器 input_register_handle

文章目录 input_handle结构体详解配对的input设备与input事件处理器实例input核心层对驱动层和事件层之间的框架建立流程图 本文章中与input子系统相关的结构体可参考input子系统结构体解析 input函数路径:drivers/input/input.c input_handle结构体详解 input_ha…

(转)雪花算法(SnowFlake)

简介 现在的服务基本是分布式、微服务形式的,而且大数据量也导致分库分表的产生,对于水平分表就需要保证表中 id 的全局唯一性。 对于 MySQL 而言,一个表中的主键 id 一般使用自增的方式,但是如果进行水平分表之后,多…

第八章结构型模式—装饰者模式

文章目录 装饰者模式解决的问题概念结构 案例使用装配者进行改进 使用场景JDK源码分析 静态代理和装饰者的区别 结构型模式描述如何将类或对象按某种布局组成更大的结构,有以下两种: 类结构型模式:采用继承机制来组织接口和类。对象结构型模式…

【Wi-Fi】802.11/802.11b/802.11g/802.11n/802.11a/802.11ac/802.11ax/802.11be

WiFi发展历史 IEEE 802.11 Protocol Release Date Frequency Band Bandwidth Max Throughput 802.11-1997 1997 2.4GHz 22MHz 2Mbps 802.11b 1999 2.4GHz 22MHz 11Mbps 802.11a 1999 5GHz 20MHz 54Mbps 802.11g 2003 2.4GHz 20MHz 54Mbps 802.11n (W…

计算机组成原理基础练习题第一章

有些计算机将一部分软件永恒地存于只读存储器中,称之为() A.硬件    B.软件C.固件    D.辅助存储器输入、输出装置以及外界的辅助存储器称为() A.操作系统    B.存储器 C.主机      D.外围设备完整的计算机系…

OpenCL编程指南-4.1OpenCL C编程

使用OpenCL C编写数据并行内核 OpenCL中的数据并行性表述为一个N维计算域,其中N1、2或3。N-D域定义了可以并行执行的工作项的总数。下面通过一个简单的例子来了解如何用OpenCL C编写一个数据并行内核,将两个浮点数数组相加。这个代码的串行版本求和时需…

力扣19删除链表的倒数第 N 个结点:思路分析+图文全解+方法总结(快慢指针法递归法)+深入思考

文章目录 第一部分:题目描述第二部分:代码实现2.1 快慢指针法2.2 递归 第一部分:题目描述 🏠 链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode) ⭐ 难度:中等 第二部分&#…

lol由于找不到vcruntine140_1.dll文件,vcruntime140_1.dll修复方法

家人们有没有遇到过打开游戏或者软件提示由于找不到vcruntime140_1.dll,无法继续执行此代码的情况,是不是不知道怎么修复呢?Vcruntime140_1.dll是一个Windows系统文件,它是Microsoft Visual C Redistributable for Visual Studio …

【嵌入式环境下linux内核及驱动学习笔记-(11-设备树)】

目录 1、设备树体系1.1 DTS /DTSI / DTC / DTB 2、基础语法2.1 节点语法2.1.1 通用名称建议 2.2 属性语法2.2.1 属性值 2.3 关于label2.4 节点的[unit-address] 与reg属性2.5 根节点 /2.6 标准属性compatible2.6.1 of_machine_is_compatible函数 2.7 地址编码2.7.1 标准属性#ad…

RabbitMQ养成记 (3.MQ的简单工作模式 和 Pub/sub 订阅模式)

上一篇是一个简单的helloworld。 我们直接发直接收 这种是最简单的。 下面我们再来接触更加复杂一点: 简单工作模式 work queues 工作队列模式: 这里注意 这里的消息 对两个消费者 c1 c2来说是竞争关系 而不是等份分发关系, 就像两个线程…

JVM 方法区

栈、堆、方法区的交互关系 线程共享角度: 新建对象分配: 方法区的理解 方法区(Method Area) 与 Java 堆一样,是各个线程共享的内存区域方法区在 JVM 启动的时候被创建,并且它的实际物理内存空间中和 Java 堆区一样都可以不连续的方法区的大小&#xf…

HNU-操作系统OS-实验Lab5

OS_Lab5_Experimental report 湖南大学信息科学与工程学院 计科 210X wolf (学号 202108010XXX) 实验目的 了解第一个用户进程创建过程了解系统调用框架的实现机制了解ucore如何实现系统调用sys_fork/sys_exec/sys_exit/sys_wait来进行进程管理 实验…

1099 Build A Binary Search Tree(超详细注解+38行代码)

分数 30 全屏浏览题目 作者 CHEN, Yue 单位 浙江大学 A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties: The left subtree of a node contains only nodes with keys less than the nodes key.The right subtree…

使用云服务器可以做什么?十大使用场景举例说明

使用阿里云服务器可以做什么?阿里云百科分享使用阿里云服务器常用的十大使用场景,说是十大场景实际上用途有很多,阿里云百科分享常见的云服务器使用场景,如本地搭建ChatGPT、个人网站或博客、运维测试、学习Linux、跑Python、小程…

详解c++STL—string组件

目录 一、string基本概念 1、本质 2、string和char * 区别: 3、特点: 二、string构造函数 1、构造函数原型 2、示例 三、string赋值操作 1、赋值的函数原型 2、示例 四、string字符串拼接 1、函数原型 2、示例 五、string查找和替换 1、功…

2023系统分析师---软件工程、系统规划高频错题

系统规划---成本效益分析 评价信息系统经济效益常用的方法主要有成本效益分析法,投入产出分析法和价值工程方法。盈亏平衡法常用于销售定价; 可行性分析 系统规划是信息系统生命周期的第一个阶段,其任务是对企业的环境、目标以及现有系统的状况进行初步调查,根据企业目标…

【利用AI让知识体系化】万字深入浅出Nginx

思维导图 文章目录 思维导图 第一部分:入门篇1.1 起步下载和安装Nginx启动NginxNginx配置文件Nginx命令行总结 1.2 Nginx的基本架构1.3 安装和配置Nginx1.4 Nginx的基本操作 第二部分:核心篇2.1 Nginx的请求处理2.2 Nginx的缓存机制2.3 Nginx的负载均衡机…

题解校验码—CRC循环校验码与海明校验码

码距 一个编码系统的码距是任意两个码字的最小距离。 例如个编码系统采用三位长度的二进制编码,若该系统有四种编码分别为:000,011,100,111,此编码系统中000与111的码距为3;011与000的码距为2…

Hard Patches Mining for Masked Image Modeling

摘要 蒙面图像建模(MIM)因其在学习可伸缩视觉表示方面的潜力而引起了广泛的研究关注。在典型的方法中,模型通常侧重于预测掩码补丁的特定内容,并且它们的性能与预定义的掩码策略高度相关。直观地说,这个过程可以被看作…

WiFi(Wireless Fidelity)基础(九)

目录 一、基本介绍(Introduction) 二、进化发展(Evolution) 三、PHY帧((PHY Frame ) 四、MAC帧(MAC Frame ) 五、协议(Protocol) 六、安全&#x…