C++——如何理解.h文件和.cpp文件

本文详细介绍了C++中的编译与链接过程,包括如何通过#include指令关联头文件和源文件,以及编译器在预处理阶段如何处理文件包含命令。此外,还探讨了声明与定义的区别,以及内部连接和外部连接的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇文章是之前摘的笔记放在电脑里,忘记在哪看到的了,就当是原创哈哈哈哈

  • 建立一个以类为名字的XX.h文件,声明类,再建立一个相同名字的XX.cpp文件(要#include “XX.h”),实现类中的函数, 在有main 函数的.cpp里声明函数,建一个与函数名一样的.cpp文件,实现该函数;并且要 #include “XX.h”

一、源文件如何根据#include来关联头文件

  1. 系统自带的头文件用尖括号括起来,这样编译器会在系统文件目录下查找。

    #include <xxx.h>

  2. 用户自定义的文件用双引号括起来,编译器首先会在用户目录下查找,然后在到C++安装目录(比如VC中可以指定和修改库文件查找路径,Unix和Linux中可以通过环境变量来设定)中查找,最后在系统文件中查找。

    #include “xxx.h”

二、头文件如何来关联源文件

  • 这个问题实际上是说,已知头文件“a.h”声明了一系列数,“b.cpp”中实现了这些函数,那么如果我想在“c.cpp”中使用“a.h”中声明的这些在“b.cpp”中实现的函数,通常都是在“c.cpp”中使用#include“a.h”,那么c.cpp是怎样找到b.cpp中的实现呢?
  • 其实.cpp和.h文件名称没有任何直接关系,很多编译器都可以接受其他扩展名。比如偶现在看到偶们公司的源代码,.cpp文件由.cc文件替代了。
  • 在Turbo C中,采用命令行方式进行编译,命令行参数为文件的名称,默认的是.cpp和.h,但是也可以自定义为.xxx等等。
  • 谭浩强老师的《C程序设计》一书中提到,编译器预处理时,要对#include命令进行“文件包含处理”:将file2.c的全部内容复制到#include“file2.c”处。这也正说明了,为什么很多编译器并不care到底这个文件的后缀名是什么—-因为#include预处理就是完成了一个“复制并插入代码”的工作。
  • 编译的时候,并不会去找b.cpp文件中的函数实现,只有在link的时候才进行这个工作。我们在b.cpp或c.cpp中用#include“a.h”实际上是引入相关声明,使得编译可以通过,程序并不关心实现是在哪里,是怎么实现的。源文件编译后成生了目标文件(.o或.obj文件),目标文件中,这些函数和变量就视作一个个符号。在link的时候,需要在makefile里面说明需要连接哪个.o或.obj文件(在这里是b.cpp生成的.o或.obj文件),此时,连接器会去这个.o或.obj文件中找在b.cpp中实现的函数,再把他们build到makefile中指定的那个可以执行文件中。
  • 在Unix下,甚至可以不在源文件中包括头文件,只需要在makefile中指名即可(不过这样大大降低了程序可读性,是个不好的习惯哦^_^)。在VC中,一帮情况下不需要自己写makefile,只需要将需要的文件都包括在project中,VC会自动帮你把makefile写好。
  • 通常,编译器会在每个.o或.obj文件中都去找一下所需要的符号,而不是只在某个文件中找或者说找到一个就不找了。因此,如果在几个不同文件中实现了同一个函数,或者定义了同一个全局变量,链接的时候就会提示
    示“redefined”。

声明与定义的区分:

一个声明就是一个定义,除非 :
声明:引入名称
定义:引入实体
1. 它声明了一个没有详细说明函数体的函数
2. 它包含一个extern定义符且没有初始化函数或函数体
3. 它是一个包含在一个类定义之内的静态类数据成员的声明
4. 它必须在最终程序的某处准确的定义一次
5. 它是一个类名声明,如class test;
6. 它是一个typedef声明。

言外之意

  1. 类的声明也就是定义
  2. 同时赋初值的声明也就是定义,如int a=2;
  3. 类非静态数据成员的声明也就是定义???
  4. 类的所有成员函数的声明也就是定义

一个定义就是一个声明,除非:

  1. 它定义的是一个静态数据成员
  2. 它定义了一个非内联成员函数

内部连接和外部连接:

  • 编译时每个文件会被编译成一个含有必要信息的源文件(又叫编译单元),然后编译单元会联结成一个和族文件同名的.o文件,.o文件把不同的编译单元中产生的符号联系起来,构成一个可执行文件。有两种截然不同的链接:内部的和外部的,将这些编译单元联系起来。

内部连接:对这个定义的访问被局限在当前编译单元,其他编译单元无法访问。
外部连接:可被其他单元访问,因此名称在整个执行文件中必须唯一。

  • 类的定义(同时也是声明),enum,struct,都是内部连接,内联函数,静态的非类成员数据也是typedef声明的类型也是内联结。
  • 非内联成员函数(包括静态成员)有外部连接,非内联函数,非静态自由函数(非类的成员函数)也是外连接。
    声明只对当前编译单元有用,他们不会影响到.o文件,
  • .h文件,由于该文件会被其他.cpp文件包含,但由于声明只是对当前编译单元有效,是不会将符号引入.o文件,所以该文件不能含有任何外部连接的符号(数据成员和函数)的定义。一般情况下也不要包含内连接符号的定义。

综上所诉

.h文件中能包含:

  1. 类成员数据的声明,但不能赋值
  2. 类静态数据成员的定义和赋值,但不建议,只是个声明就好。
  3. 类的成员函数的声明
  4. 非类成员函数的声明
  5. 常数的定义:如:const int a=5;
  6. 静态函数的定义
  7. 类的内联函数的定义

不能包含:

  1. 所有非静态变量(不是类的数据成员)的声明
  2. 默认命名空间声明不要放在头文件,using namespace std;等应放在.cpp中,在.h文件中使用std::string
### C/C++ 中 `.h` 文件的作用 `.h` 文件被称为头文件,在 C C++ 编程语言中用于定义函数原型、宏定义以及变量声明。这些文件的主要目的是提供一种机制来共享代码片段,使得多个源文件能够访问相同的接口定义。 #### 定义预处理指令防止重复包含 为了避免同一个头文件被多次引入而导致的多重定义错误,通常会在头文件顶部使用条件编译语句 `#ifndef`, `#define` 底部放置对应的 `#endif` 来创建所谓的“保护性宏”。这确保即使同一头文件在一个程序的不同部分被导入数次也只会实际加载一次[^1]。 ```cpp #ifndef MYHEADER_H #define MYHEADER_H // ...文件内容 ... #endif // MYHEADER_H ``` #### 包含其他必要的头文件 在自定义头文件内部还可以通过 `#include` 指令进一步引用其他的系统级或第三方库提供的头文件。按照惯例,应该先列出标准库头文件,接着是外部依赖库,最后才是项目自身的私有头文件: ```cpp #include <iostream> // 系统头文件 #include <openssl/ssl.h> // 第三方库头文件 #include "my_class.h" // 项目内头文件 #include "my_struct.h" #include "helper_functions.h" // 当前文件相关头文件 ``` #### 声明全局符号 除了上述功能外,头文件还负责对外暴露本模块内的公共成员——无论是数据类型还是方法签名。例如,可以在此处声明类及其成员函数,或是简单地给出一些辅助性的自由函数原型以便于使用者调用: ```cpp void foo(); int bar(int x, int y); class MyClass { public: void method(); private: int data; }; ``` 综上所述,合理设计并维护好项目的各个 `.h` 文件对于构建清晰有序且易于扩展的应用至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值