翻译自:https://docs.microsoft.com/en-us/cpp/cpp/header-files-cpp?view=vs-2019
程序中各元素在使用前必须被声明,例如:变量,函数,类等。元素的声明告诉编译器该元素的类型,是int,double,函数,还是类。进一步地说,在每个.cpp文件中使用地元素必须被声明(直接或间接)。
当你编译一个程序时,每个.cpp文件被独立地编译未一个compilation unit编译单元。编译器并不知道其他编译单元里有哪些名称被声明过滤。这也就意味着,如果你定义一个类或函数,或全局变量,你必须在每个使用该对象的.cpp文件中提供该对象地声明。在所有文件中该对象的声明必须是一致的。当linker链接器尝试着将所有编译单元合成为一个单独的程序时,稍微的不一致就会导致错误,或预料外的行为。
为了使得错误的可能性最小化,C++采用了header file头文件的行驶来包含声明。你在头文件中声明变量,然后在需要该声明的每个.cpp文件中使用#include包含该头文件。在编译时,#include标识符将头文件的一个拷贝直接插入到.cpp文件中。
在Visual Studio 2019,C++20的module特性用于提升并最终替代头文件。
下面的例子展示了声明一个类并在另外一个源文件中使用这个类的典型方式。我们首先创建一个header file,它包含类的声明,但是要注意该类的定义并不完整,因为do_something函数并没有定义:
// my_class.h namespace N { class my_class { public: void do_something(); }; }
下一步,创建一个实现文件(典型的是一个.cpp文件,或类似的文件扩展名)。我们将调用my_class.cpp并为成员函数提供定义。在.cpp文件中通过#include标识符包含my_class.h来获取my_class的声明。注意,头文件与.cpp在同一目录下的,使用#inlcude "",而使用标准库的头文件则使用<>。
在.cpp实现文件中,我们通过使用using来简化各个类型的书写,如果不使用using namespace std的话,string就必须写成:std::string。但是注意:不要在头文件中使用using!
// my_class.cpp #include "my_class.h" // header in local directory #include <iostream> // 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文件传递给链接器linker。当链接器合并各个.obj文件时,它会找到my_class的一个声明,在my_class.cpp生成的.obj中,然后程序可编译成功。
典型情况下,头文件使用include guard(即下面的#ifndef---#define---#endif)或#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 */
一个头文件可能被多个文件引用,因而在头文件中不能针对同一个name有多个定义。下列行为不建议在头文件中声明:
在头文件中使用using不会产生错误,但是可能引入问题。因为直接或间接引用该头文件的.cpp文件都可以访问该namespace,这就可能导致不同命名空间内变量名的冲突。
The following example shows the various kinds of declarations and definitions that are allowed in a header file:
// sample.h #pragma once #include <vector> // #include directive #include <string> 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<int>; // 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 <typename T> // template definition class value_store { public: value_store<T>() = default; void write_value(T val) { //... function definition OK in template } private: std::vector<T> vals; }; template <typename T> // template declaration class value_widget; }
原文:https://www.cnblogs.com/zyk1113/p/13219753.html