C++之头文件 (.h)

核心重点:头文件 (.h) 的作用

想象一下你在一家大公司工作,需要和其他部门协作。头文件(Header File,通常以 .h.hpp 结尾)就像是部门之间共享的**"接口文档"或"功能菜单"** 。

它的核心作用是**"声明" (Declaration)** ,而不是**"实现" (Implementation)** 。

  1. "声明"是什么?

    它告诉编译器(以及其他程序员):"嘿,存在这样一个东西"。比如:

    • 有一个类叫做 FunctionRenamer
    • 这个类有一个公开的函数叫 renameFromSymbols,它接收一个字符串,返回一个布尔值。
    • 有一个数据类型叫 Module
  2. "实现"是什么?

    它告诉编译器这个东西具体**"怎么做"** 。比如:

    • renameFromSymbols 函数内部具体是如何解析文件、如何重命名的。这部分代码通常放在 .cpp 文件中。

为什么要把"声明"和"实现"分开?

  • 解耦与协作 :你可以把头文件 (.h) 发给你的同事。他们只需要知道你的代码能做什么 (通过看头文件里的声明),而不需要关心你具体怎么实现 的。他们可以基于你的头文件继续写他们的代码,而你可以同时在 .cpp 文件里安心实现你的功能。
  • 提高编译效率 :当你在 .cpp 文件中修改了一个函数的具体实现时,只需要重新编译这一个 .cpp 文件。其他包含了对应头文件的代码文件,因为"声明"没有变,所以不需要重新编译,大大节省了项目大了之后的编译时间。
  • 代码清晰:头文件就像一个大纲或目录,让人可以快速了解一个模块提供了哪些功能,而不用陷入具体的实现细节中。

剖析 FunctionRenamer.h

现在我们来逐行分析这个头文件。

1. 头文件卫士 (Header Guards)

cpp 复制代码
#ifndef FUNCTION_RENAMER_H
#define FUNCTION_RENAMER_H
// ... 所有代码都在这里 ...
#endif
  • 作用:防止头文件被重复包含(include)。

  • 解释

    • #ifndef FUNCTION_RENAMER_H:意思是 "if not defined FUNCTION_RENAMER_H"(如果 FUNCTION_RENAMER_H 这个宏还没被定义过)。
    • #define FUNCTION_RENAMER_H:如果没被定义过,那就现在定义它。
    • #endif:结束这个条件判断。
  • 场景 :假设文件 A.hB.h 都包含了 FunctionRenamer.h。然后你的主文件 main.cpp 又同时包含了 A.hB.h。如果没有头文件卫士,FunctionRenamer.h 的内容就会被复制粘贴两次到 main.cpp 中,编译器会因为看到同一个类被定义了两次而报错。有了头文件卫士,第二次包含时,因为 FUNCTION_RENAMER_H 已经被定义了,所以 #ifndef#endif 之间的所有内容都会被忽略。

2. 包含其他头文件 (#include)

cpp 复制代码
#include "wasm.h"
#include "wasm-traversal.h"
#include <string>
#include <unordered_map>
#include <vector>
  • 作用:告诉编译器,我接下来要用到的某些功能、类或类型是在这些文件里声明的,请把它们的内容也"复制粘贴"到这里来。

  • <...> vs "... " 的区别

    • #include <string>:尖括号 <> 通常用于包含标准库 或者系统库 的头文件。编译器会在系统指定的目录中去查找。这里的 <string>, <unordered_map>, <vector> 都是 C++ 标准库提供的强大工具。

      • string: 用于处理文本字符串。
      • unordered_map: 用于存储键值对(像字典或哈希表)。
      • vector: 一个动态数组,可以随时添加或删除元素。
    • #include "wasm.h":双引号 "" 通常用于包含项目内部 的其他头文件。编译器会先在当前文件所在的目录查找,然后再去系统目录查找。这里的 "wasm.h""wasm-traversal.h" 显然是这个项目自定义的头文件,它们可能声明了 Module, Name, PostWalker 这些类型。

3. 命名空间 (Namespace)

cpp 复制代码
namespace wasm {
// ...
} // namespace wasm
  • 作用:避免命名冲突。就像给你的类和函数起一个"姓"。
  • 解释 :这个项目中可能有很多叫 FunctionRenamer 的类,但这个是属于 wasm 这个"家族"的。当其他地方要使用它时,需要写 wasm::FunctionRenamer,明确指出是 wasm 空间下的那个,避免混淆。

4. 类声明 (class)

cpp 复制代码
class FunctionRenamer {
public:
  // ... 公开接口 ...
private:
  // ... 内部实现细节 ...
};
  • class 是 C++ 中面向对象编程的核心,它是一个蓝图,用来创建对象 (Object) 。这个蓝图把数据(成员变量)操作这些数据的函数(成员函数/方法) 打包在一起。
  • public::公共区域。这里声明的成员(变量或函数)是这个类的"对外接口",任何地方都可以访问。就像电视遥控器上的按钮,是设计给用户按的。
  • private::私有区域。这里声明的成员只能被这个类内部的函数访问。这是类的"内部实现细节",对外部是隐藏的。就像遥控器的内部电路,用户不应该也无法直接操作。

5. 成员函数和成员变量

让我们看几个例子:

  • 构造函数 (Constructor)

    cpp 复制代码
    explicit FunctionRenamer(Module& wasm) : wasm_(wasm) {}
    • 这是一个特殊的函数,名字和类名相同。当创建一个 FunctionRenamer 对象时,它会自动被调用。
    • Module& wasm: 参数列表。& 符号表示引用 (Reference) ,意味着它传递的不是 Module 对象的一个拷贝,而是它本身。这样做更高效,并且可以在函数内部修改原始对象。
    • : wasm_(wasm): 这是成员初始化列表 。它是在函数体 {} 执行之前,初始化成员变量 wasm_ 的最高效方式。这里的意思是"用参数 wasm 来初始化成员变量 wasm_"。
    • explicit: 这个关键字可以防止一些不期望的隐式类型转换,是一个好的编程习惯。
  • 函数声明

    cpp 复制代码
    bool renameFromSymbols(const std::string& symbolsPath);
    • 这就是一个典型的"声明"。它只说明了函数签名:返回 bool 类型,函数名叫 renameFromSymbols,接受一个 const std::string& 类型的参数。具体实现(函数体 {...})会在对应的 .cpp 文件里。
    • const: 表示这个函数不会修改传入的 symbolsPath 字符串。
  • 成员变量

    cpp 复制代码
    Module& wasm_;
    std::unordered_map<Name, Name> renameMap_;
    • 这些是存储在 FunctionRenamer 对象内部的数据。
    • 变量名后面的下划线 _ (如 wasm_) 是一种常见的编码风格,用来表示它们是类的私有成员变量。
  • 嵌套的结构体 (struct)

    cpp 复制代码
    struct CallRenamer : public PostWalker<CallRenamer> {
      // ...
    };
    • structclass 非常相似,唯一的默认区别是 struct 的成员默认是 public,而 class 的默认是 private
    • 这里在 FunctionRenamer 内部定义了一个辅助性的 struct CallRenamer,这表明 CallRenamerFunctionRenamer 关系紧密,主要是为了辅助它完成工作的。
    • : public PostWalker<CallRenamer> 这部分是继承 (Inheritance) ,表示 CallRenamer 继承了 PostWalker 的所有功能,并可以在此基础上进行扩展。

总结与展望

  • 头文件 (.h) :是蓝图的"接口声明"部分,告诉别人"我能提供什么",并包含其他需要的"接口文档"。
  • 源文件 (.cpp) :是蓝图的"实现细节"部分,告诉编译器"我该怎么做"。这个例子中没有展示 .cpp 文件,但你可以想象一个 FunctionRenamer.cpp 文件,里面包含了 renameFromSymbols 等函数的具体代码。

FunctionRenamer.cpp 可能会是这样(伪代码):

cpp 复制代码
#include "FunctionRenamer.h" // 包含自己的头文件,以获取声明

namespace wasm {

bool FunctionRenamer::renameFromSymbols(const std::string& symbolsPath) {
  // 1. 调用私有方法 parseSymbolsFile 来解析文件
  if (!parseSymbolsFile(symbolsPath)) {
    return false; // 解析失败
  }

  // 2. 调用私有方法 buildRenameMapping 来建立映射关系
  buildRenameMapping();

  // 3. 调用私有方法 performRename 来执行重命名
  performRename();

  return true; // 成功
}

// ... 其他函数的具体实现 ...

} // namespace wasm

通过这个例子,你已经接触到了 C++ 最核心的几个概念:头文件管理、命名空间、类与对象、公有/私有访问控制、构造函数、引用以及标准库的使用。

相关推荐
linux开发之路7 小时前
C++精选面试题集合(100份大厂面经提取的200+道真题)
linux·c++·网络编程·数据结构与算法·c++面试题
重启的码农7 小时前
云游戏技术之高速截屏和GPU硬编码 (1) 捕获-预处理-编码流水线
c++·云计算·音视频开发
重启的码农7 小时前
云游戏技术之高速截屏和GPU硬编码 (2) 应用程序主控
c++·云计算·音视频开发
沐怡旸8 小时前
【C++基础知识】深入剖析C和C++在内存分配上的区别
c++
studytosky8 小时前
C语言数据结构之双向链表
c语言·数据结构·c++·算法·链表·c
沐怡旸8 小时前
【底层机制】malloc 在实现时为什么要对大小内存采取不同策略?
c++
HABuo9 小时前
【C++进阶篇】学习C++就看这篇--->多态超详解
c语言·开发语言·c++·后端·学习
1白天的黑夜19 小时前
哈希表-1.两数之和-力扣(LeetCode)
c++·leetcode·哈希表
哼?~10 小时前
list模拟实现
开发语言·c++