C++ 包含守卫

2024年8月28日 | 阅读 7 分钟

在本文中,您将通过示例了解 C++ 中的包含保护

包含保护常用于 C++ 中,以防止在单个翻译单元中多次包含同一个 头文件。这通常称为头文件保护宏保护。这样可以避免重复的符号定义,从而可能导致链接器错误

#ifndef 检查名为 MY_HEADER_FILE_H 的预处理器宏是否未定义。如果未定义,则 #ifndef#endif 之间的代码将被包含在翻译单元中。

#define MY_HEADER_FILE_H 定义了宏 MY_HEADER_FILE_H。如果已定义该宏,则可以防止包含 #ifndef 块内的代码。

您希望包含 #ifndef 和 #endif 指令之间的头文件内容。这是头文件的实际内容。

#endif 标记包含保护块的结束。

使用此技术,第一次将头文件包含到翻译单元中时,MY_HEADER_FILE_H 宏将未定义,并且将包含头文件的内容。在同一或其他翻译单元中后续包含时,MY_HEADER_FILE_H 宏将已定义,从而防止内容再次被包含。

这有助于避免头文件被多次包含时可能出现的重复定义声明等问题。通常的做法是根据头文件名来命名,以确保唯一性。例如,如果您的头文件名为 my_header_file.h,请使用 MY_HEADER_FILE_H 作为包含保护宏的名称。

现代 C++ 和像C++11 标准这样的工具引入了#pragma once 指令,它的功能与包含保护相同。但是,#pragma once 不是 C++ 标准的一部分,所以如果您想确保最大的可移植性和兼容性,请使用传统的包含保护。

程序

让我们举一个例子来理解 C++ 中包含保护的用法。

输出

Dog is of White

说明

  • 在此示例中,#ifndef ANIMAL_H#define ANIMAL_H 创建了包含保护,以防止此头文件的内容在同一翻译单元中被包含多次。
  • #include#include<string> 包含输入/输出字符串操作所必需的头文件。
  • Animal 类有三个私有成员变量:name、colortype
  • 类中定义了两个公有成员函数
  • Void input(): 它将namecolor 属性设置为特定值,在本例中为"Dog""White"
  • Void display(): 它使用 std::cout 打印 namecolor 的值。
  • #endif // ANIMAL_H 关闭了包含保护

在 main 函数中

  • 创建了一个名为 animalAnimal 类的实例。
  • 调用 Animal 类的 input() 函数,设置 namecolor
  • 之后,调用 Animal 类display() 函数,该函数将 "Dog is of White" 打印到标准输出。
  • Return 0 表示程序成功执行。
  • display() 函数中的打印语句在 main 函数内生成此输出

复杂度分析

时间复杂度

  • Animal 类input()display() 成员函数都具有恒定的时间复杂度。它们执行固定数量的操作,与输入大小无关。
  • main() 函数调用 input()display() 函数,每个函数都具有恒定的时间复杂度。
  • 总的来说,此代码的时间复杂度O(1),这意味着它是恒定的时间复杂度。

空间复杂度

  • Animal 类有三个私有成员变量:name、colortype。这些成员变量占用恒定的内存。
  • main() 函数中创建了 Animal 对象 animal,并使用了恒定的内存。
  • 此代码的空间复杂度也为 O(1),这意味着它使用的内存量是恒定的,与输入大小无关。
  • 简单来说,提供的代码不涉及任何循环、递归或随输入大小缩放的数据结构,因此时间和空间复杂度都为常量。

程序

让我们举另一个例子来理解 C++ 中包含保护的用法。

输出

Sum: 15
Difference: 5

说明

  • 在此示例中,#ifndef MATH_UTILS_H#define MATH_UTILS_H 行是包含保护,它们可以防止头文件在同一翻译单元中被包含多次。
  • 提供了两个函数声明:int add(int a, int b)int subtract(int a, int b),但没有实现。
  • 它包含了h头文件,其中包含函数声明。
  • 这里提供了 addsubtract 函数的实现。它们返回输入整数的
  • 它包含 iostream 标准头文件,用于输入/输出操作
  • 它还包含 h 头文件,允许使用 addsubtract 函数。

在 main 函数中

  • main() 函数中,声明并赋值了两个整数变量:xy
  • 使用 xy 调用 add 函数,并将结果存储在 sum 变量中。
  • 使用 xy 调用 subtract 函数,并将结果存储在 difference 变量中。
  • 然后,使用 std::cout 将结果打印到控制台。
  • 此代码演示了代码组织的模块化方法。头文件 h 包含函数声明,实现文件 math_utils.cpp 提供了实际的函数实现。

复杂度分析

时间复杂度

  • cpp 中的 addsubtract 函数具有恒定的时间复杂度 O(1),因为它们执行固定数量的操作(加法减法)。
  • 这些函数不涉及循环、递归或任何随输入大小缩放的操作。
  • cpp 中的 main function 包括变量声明、函数调用打印等操作。
  • 这些操作具有恒定的时间复杂度 O(1),因为它们不随输入大小缩放。
  • addsubtract 函数的函数调用是恒定时间操作。
  • 整个代码库中的所有函数和文件都包含具有恒定时间复杂度 (O(1)) 的操作。因此,整个程序的总体时间复杂度保持为 O(1),即恒定时间复杂度。

空间复杂度

  • addsubtract 函数不使用随输入大小缩放的额外内存。
  • 它们只为局部变量参数使用恒定的内存。
  • 因此,这些函数的空间复杂度O(1)
  • main 函数使用几个整数变量(x、y、sum、difference)来存储结果和临时值。
  • 这些变量占用的内存量是恒定的,与输入大小无关。
  • 内存使用量不会随输入值而增长。
  • 因此,main 函数的空间复杂度为 O(1)
  • 整个代码库的内存使用量保持恒定,并且不随输入大小而缩放。因此,整个程序的总体空间复杂度O(1),即恒定空间复杂度。

性质

C++ 中的包含保护是一种用于防止同一个头文件在同一翻译单元中被包含多次的技术。它们有助于确保头文件的内容只被包含一次,从而可以防止重复符号定义等问题,并改进代码组织。以下是包含保护的关键属性:

防止多次包含:包含保护使用条件预处理指令(#ifndef, #define, #endif),确保只有在关联的宏未定义时才包含头文件的内容。它防止头文件在同一个文件中被包含多次。

头文件隔离:您可以通过包含保护来隔离头文件中的声明定义。它有助于封装代码,并防止因多次包含而引起的问题,例如重定义错误

宏命名约定:用于包含保护的宏应唯一于头文件。一种常见的约定是使用头文件的名称(大写),并用下划线替换非字母数字字符。

可移植性包含保护是一种被广泛接受的实践,得到了大多数 C++ 编译器的支持。它们是可移植的,并有助于确保在不同平台和编译器之间行为的一致性。

头文件依赖:包含保护还有助于管理头文件之间的依赖关系。如果包含多个头文件,而其中一个依赖于其他头文件的定义,包含保护可以确保这些依赖关系得到正确处理。


下一个主题C++ 中的弱指针