【我的C语言学习进阶之旅】什么是.hpp文件?

在这里插入图片描述

一、 .hpp文件是啥?

2.1 .hpp的疑问来源

最近在学习OpenGL ES的时候,学习了一个glm库(https://github.com/g-truc/glm),发现里面的代码包含大量的.hpp文件

在这里插入图片描述
当然也有一些是.h文件

在这里插入图片描述
好吧,我承认我刚刚学习C++,对于声明是.hpp文件不懂。

我的第一想法是: .hpp 文件 是不是 .h 文件 和 .cpp 文件的结合?

打开一个.hpp文件内容查看一些,发现有点像我理解的。
在这里插入图片描述
下面来正式介绍一下声明是.hpp文件?

2.2 什么是hpp文件?

2.2.1 以往套路

以往在编写 C++ 程序时,我们一般分成下面两个部分:

  • 编写一个 .h 文件,在里面放上函数的声明
  • 再编写一个 .cpp 文件,在里面添加函数的相关实现

这样在使用的时候包含头文件,再把 .cpp 加到当前工程下编译即可,但是如果此时 .cpp 忘记添加到当前工程下编译,就会出现函数找不到的情况。

如果你的 .h 是模板类的声明,.cpp 是模板类函数的实现,由于模板两次编译的机制,直接包含头文件就出现找不到函数的情况了,这种情况需要把实现和声明放在一起,才能找到函数的定义。

2.2.2 .hpp 新方式

hppHeader Plus Plus 的简写,顾名思义就是 .h 文件加上.cpp,与*.h类似,.hpp也是C++程序头文件。

本质上就是 .cpp 实现代码混入 .h 文件当中,定义和实现都包含在同一个文件里。

这样调用者只需要include.hpp文件即可,无需再 将.cpp加入到工程中进行编译。

而实现代码将直接编译到调用者的obj文件中,不再生成单独的obj

采用.hpp将大幅度减少调用工程中的.cpp文件数与编译次数,也不用再发布烦人的libdll

因此非常适合用来编写公用的开源库,比如开头说的glm库。

不过 *.h里面可以有using namespace std,而*.hpp里则无。

2.3 使用.hpp文件有什么好处?

  1. 一般来说,*.h里面只有声明,没有实现。而.hpp 文件将定义和实现放在同一个文件,这样可以减少了.cpp文件的数量。

  2. 无需再将. cpp 加入到项目中进行编译,将代码直接编译到调用者的 obj 文件,不再生成单独的 obj,大幅度减少编译次数,也不用再发布烦人的libdll,非常适合编写开源库,比如开头说的glm库。

  3. glm库大量使用模板,采样 .hpp 的形式可以保持与各个编译器更好的兼容性(C++ 模板不能把源文件和声明文件分开成两个文件)

三、思考一下为什么有了.h和.cpp还要.hpp呢?

下面内容摘自: C++中.cpp和.hpp的区别

3.1 说一说.cpp

我们可以将所有东西都放在一个.cpp文件内,编译器会将这个.cpp编译成.obj,即编译单元

一个程序可以由一个编译单元组成,也可以由多个编译单元组成。

一个.cpp对应一个.obj,然后将所有的.obj链接起来(通过一个叫链接器的程序),组成一个.exe,即程序

如果一个.cpp要用到另一个.cpp定义的函数怎么办?
只需在这个.cpp中写上它的函数声明,链接器将所有的obj链接起来。

但是如果碰巧有相同的函数或外部变量怎么办?
C++可以通过一种叫做链接属性的关键字来限定,某个函数是属于整个程序公用的,还是只在一个编译单元obj里面使用,这些关键字就是extern(外部链接)和static(内部链接)。

3.2 说一说.h

让我们说说.h。其实没有.h,程序也能很好的工作,
但是当你发现一个外部链接的函数或外部变量,需要许多分声明,
因为只要使用到该函数的单元,就必须写一份声明在那个.cpp里面,如果要修改会很麻烦!!!

.h就是为了解决这个问题而诞生的,它包含了这些公共的东西,然后所有需要使用该函数的.cpp,只需要用#include包含进去便可,以后需要修改,只是修改一份内容。#include并不是什么申请指令,只是将指定文件的内容,原封不动的拷贝进来

不是很严格的讲,.h文件做的是类的声明,包括类成员的定义和函数的声明,而.cpp文件做的类成员函数的具体实现(定义)。

一个*.h文件和*.cpp文件一般是配对的。

*.cpp文件的第一行一般也是#include "xxx.h"文件,其实也相当于把xxx.h文件里的东西复制到*.cpp文件的开头。
所以,你全部写在*.cpp文件其实也是一样的。

3.2 既然可以直接写cpp,为什么还要写hpp?

既然可以直接写cpp,为什么还要写hpp?

除了编程规范外,还涉及到类一个重要性质,就是封装性

比如现在我们公司和另一家软件公司合作,这样就必然要互相提供一些软件的信息(比如一些类,它到底是要做什么的),可是在提供这些信息的同时我们又不像让对方知道我们这些类的具体实现,毕竟这些是我们公司的算法核心和心血啊。

所以这个时候就可以把类的接口(这个类是要做什么的)放在*.h文件中,而具体类的实现放在 .cpp文件。这时候我们只要给对方公司.h文件就行了。这样既提供了必要的信息,又保护了我们的核心代码。

  1. 最表面的机制是:头文件是程序的界面(是代码界面),提供给程序员以 类、模版、函数等一系列的声明,让程序员知道应该怎么调用里面的“东西”。
  2. 从动态链接库的角度看:头文件提供界面,使得程序员在需要加载一个库函数的时候(这里也仅仅是举简单的例子)查看头文件就知道怎么加载这个动态库内部的函数。
  3. 从软件的扩展来说:将头文件作为界面,再去定义它的实现,这样只要保证界面不变(头文件不变),就可以只修改实现文件,而不必修改其他的实现代码。比如你有一个sort()函数来排序,在一个大程序中,你后来发现这个sort()有更好的算法,于是你只需要去修改函数的实现(修改.cpp文件的sort()函数的代码),其他使用这个函数的地方可以完全保持不变,这是分割技术的第一个好处
  4. 从编译的角度看:所有源文件都是被编译器分别划分单元来分别编译,在编译的过程中,头文件被嵌入到实现文件里面一起作为一个编译单元被编译(实现文件filename.cpp里的#include "filename.h"这一行被替换成filename.h里面的所有内容(实际上会把预处理指令去掉,这才是预处理最本质的作用))。

四、扩展阅读

  • 13
    点赞
  • 49
    收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:Age of Ai 设计师:meimeiellie 返回首页
评论 2

打赏作者

字节卷动

你的鼓励将是我创作的最大动力!

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值