头文件 (C++)

头文件 (C++)

必须在使用变量、函数、类等程序元素的名称之前对其进行声明。 例如,不能在没有声明“x”之前编写 x = 42。

int x; // declaration

x = 42; // use x

声明告知编译器,元素是 int、double、函数、 还是其他内容。class 此外,必须在使用每个名称时所在的每个 .cpp 文件中(直接或间接)声明每个名称。 编译程序时,每个 .cpp 文件都会独立编译为一个编译单元。 编译器不知道在其他编译单元中声明了哪些名称。 这意味着,如果你定义类、函数或全局变量,则必须在使用它的每个附加 .cpp 文件中提供对它的声明。 在所有文件中,对它的每个声明必须完全相同。 当链接器尝试将所有编译单元合并成单个程序时,出现轻微的不一致会导致错误或意外行为。

为了最大程度地减少出错的可能性,C++ 采用了使用头文件来包含声明的约定。 在一个头文件中进行声明,然后在每个 .cpp 文件或其他需要该声明的头文件中使用 #include 指令。 #include 指令在编译之前将头文件的副本直接插入 .cpp 文件中。

注意

在 Visual Studio 2019 中,C++20 模块功能作为头文件的改进和最终替代引入。 有关详细信息,请参阅 C++ 中的模块概述。

示例

以下示例演示了一种声明类的常见方法,然后在另一源文件中使用它。 我们将从头文件 my_class.h 开始。 它包含类定义,但请注意,定义不完整;未定义成员函数 do_something:

// my_class.h

namespace N

{

class my_class

{

public:

void do_something();

};

}

接下来,创建一个实现文件(通常使用 .cpp 或类似的扩展名)。 我们将调用文件 my_class.cpp,并为成员声明提供定义。 我们为“my_class.h”文件添加一个 #include 指令,以便立刻将 my_class 声明插入到 .cpp 文件中。我们包括 ,用于拉入 std::cout 的声明。 请注意,引号用于源文件所在目录中的头文件,尖括号用于标准库标头。 此外,许多标准库标头没有 .h 或任何其他文件扩展名。

在实现文件中,可以选择使用 using 语句来避免使用“N::”或“std::”限定每个提及的“my_class”或“cout”。 不要在头文件中放置 using 语句!

// my_class.cpp

#include "my_class.h" // header in local directory

#include // header in standard library

using namespace N;

using namespace std;

void my_class::do_something()

{

cout << "Doing something!" << endl;

}

现在,我们可以在另一个 .cpp 文件中使用 my_class。 我们 #include 头文件,以便编译器拉入声明。 所有编译器都需要知道的是,my_class 是一个类,它有一个名为 do_something() 的公共成员函数。

// my_program.cpp

#include "my_class.h"

using namespace N;

int main()

{

my_class mc;

mc.do_something();

return 0;

}

编译器完成将每个 .cpp 文件编译为 .obj 文件的操作后,会将 .obj 文件传递给链接器。 链接器合并对象文件时,会发现 my_class 的一个定义;它位于为 my_class.cpp 生成的 .obj 文件中,生成成功。

Include 防范

通常,头文件有一个 include 防范或 #pragma once 指令,用于确保它们不会多次插入到单个 .cpp 文件中。

// my_class.h

#ifndef MY_CLASS_H // include guard

#define MY_CLASS_H

namespace N

{

class my_class

{

public:

void do_something();

};

}

#endif /* MY_CLASS_H */

头文件中应避免的内容

由于一个头文件可能会被多个文件执行 include 操作,因此它不能包含可能生成多个同名定义的定义。 不允许以下操作,否则会被视为非常糟糕的做法:

命名空间或全局范围内的内置类型定义

非内联函数定义

非常量变量定义

聚合定义

未命名的命名空间

using 指令

使用 using 指令不一定会导致错误,但可能会导致问题,因为它将命名空间引入每个直接或间接包含该标头的 .cpp 文件中的范围。

示例头文件

以下示例显示了头文件中允许的各种声明和定义:

// sample.h

#pragma once

#include // #include directive

#include

namespace N // namespace declaration

{

inline namespace P

{

//...

}

enum class colors : short { red, blue, purple, azure };

const double PI = 3.14; // const and constexpr definitions

constexpr int MeaningOfLife{ 42 };

constexpr int get_meaning()

{

static_assert(MeaningOfLife == 42, "unexpected!"); // static_assert

return MeaningOfLife;

}

using vstr = std::vector; // type alias

extern double d; // extern variable

#define LOG // macro definition

#ifdef LOG // conditional compilation directive

void print_to_log();

#endif

class my_class // regular class definition,

{ // but no non-inline function definitions

friend class other_class;

public:

void do_something(); // definition in my_class.cpp

inline void put_value(int i) { vals.push_back(i); } // inline OK

private:

vstr vals;

int i;

};

struct RGB

{

short r{ 0 }; // member initialization

short g{ 0 };

short b{ 0 };

};

template // template definition

class value_store

{

public:

value_store() = default;

void write_value(T val)

{

//... function definition OK in template

}

private:

std::vector vals;

};

template // template declaration

class value_widget;

}

相关推荐

我找到了完形填空的答案规律
常见问题
口袋妖怪日月孵蛋攻略 口袋妖怪日月孵蛋地点教程
肯德基kfc尚选晚餐套餐菜单价格表及供应该时间