玖叶教程网

前端编程开发入门

C++|头文件应该包含什么,不应该包含什么

0 declaration and definition

The names of program elements such as variables, functions, classes, and so on must be declared before they can be used. For example, you can't just write x = 42 without first declaring 'x'.

在使用程序元素(如变量、函数、类等)之前,必须声明它们的名称。例如,如果不先声明“x”,就不能只写x=42。

int x;      // declaration,not a pure declaration, also a definition
x = 42;  // use x

The declaration tells the compiler whether the element is an int, a double, a function, a class or some other thing. Furthermore, each name must be declared (directly or indirectly) in every .cpp file in which it is used. When you compile a program, each .cpp file is compiled independently into a compilation unit. The compiler has no knowledge of what names are declared in other compilation units. That means that if you define a class or function or global variable, you must provide a declaration of that thing in each additional .cpp file that uses it. Each declaration of that thing must be exactly identical in all files. A slight inconsistency will cause errors, or unintended behavior, when the linker attempts to merge all the compilation units into a single program.

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

To minimize the potential for errors, C++ has adopted the convention of using header files to contain declarations. You make the declarations in a header file, then use the #include directive in every .cpp file or other header file that requires that declaration. The #include directive inserts a copy of the header file directly into the .cpp file prior to compilation.

为了尽量减少错误的可能性,C++采用了使用头文件包含声明的约定。在头文件中进行声明,然后在每个 ,cpp文件或其他需要该声明的头文件中使用#include指令。#include指令将头文件的副本直接插入到编译前的cpp文件。

The C++20 modules feature is introduced as an improvement and eventual replacement for header files.

C++20引入模块功能是为了改进并最终替换头文件。

A C++ program consists of various entities such as variables, functions, types, and namespaces. Each of these entities must be declared before they can be used. A declaration specifies a unique name for the entity, along with information about its type and other characteristics. In C++ the point at which a name is declared is the point at which it becomes visible to the compiler. You can't refer to a function or class that is declared at some later point in the compilation unit. Variables should be declared as close as possible before the point at which they're used.

C++程序由各种实体组成,如变量、函数、类型和名称空间。在使用这些实体之前,必须声明它们中的每一个。声明指定实体的唯一名称,以及有关其类型和其他特征的信息。在C++中,声明名称的点是编译器可以看到名称的点。您不能引用编译单元中稍后某个点声明的函数或类。变量应在使用点之前声明为尽可能接近。

Some entities, including functions, classes, enums, and constant variables, must be defined as well as declared. A definition provides the compiler with all the information it needs to generate machine code when the entity is used later in the program. A constant variable must be defined, in other words assigned a value, in the same statement in which it's declared. A declaration of a built-in type such as int is automatically a definition because the compiler knows how much space to allocate for it.

一些实体,包括函数、类、枚举和常量变量,必须定义和声明。定义为编译器提供了在程序稍后使用实体时生成机器代码所需的所有信息。常量变量必须在声明它的同一语句中定义,换句话说,指定一个值。内置类型(如int)的声明自动成为定义,因为编译器知道要为其分配多少空间。

1 Translation units and linkage

In a C++ program, a symbol, for example a variable or function name, can be declared any number of times within its scope. However, it can only be defined once. This rule is the "One Definition Rule" (ODR). A declaration introduces (or reintroduces) a name into the program, along with enough information to later associate the name with a definition. A definition introduces a name and provides all the information needed to create it. If the name represents a variable, a definition explicitly creates storage and initializes it. A function definition consists of the signature plus the function body. A class definition consists of the class name followed by a block that lists all the class members. (The bodies of member functions may optionally be defined separately in another file.)

在C++程序中,可以在其作用域内多次声明符号,例如变量或函数名。然而,它只能定义一次。该规则是“一次定义规则”(ODR)。声明在程序中引入(或重新引入)名称,以及足够的信息,以便稍后将名称与定义相关联。定义引入名称并提供创建名称所需的所有信息。如果名称表示变量,则定义会显式创建存储并对其进行初始化。函数定义由签名和函数体组成。类定义由类名后跟列出所有类成员的块组成。(可以选择在另一个文件中单独定义成员函数体。)

The following example shows some declarations:

以下示例显示了一些声明:

int i;  // variable declaration, not pure, also definition
extern int j; // pure variable declaration
int f(int x); // function declaration
class C; // class declaration

The following example shows some definitions:

以下示例显示了一些定义:

int i{42};   // variable definition, and initialize
int f(int x){ return x * i; } // function definition
class C { // class definition
public:
   void DoSomething();
};

A program consists of one or more translation units. A translation unit consists of an implementation file and all the headers that it includes directly or indirectly. Implementation files typically have a file extension of .cpp or .cxx. Header files typically have an extension of .h or .hpp. Each translation unit is compiled independently by the compiler. After the compilation is complete, the linker merges the compiled translation units into a single program. Violations of the ODR rule typically show up as linker errors. Linker errors occur when the same name is defined in more than one translation unit.

程序由一个或多个翻译单元组成。翻译单元由一个实现文件和它直接或间接包含的所有头文件组成。实现文件的文件扩展名通常为.cpp或.cxx。头文件的扩展名通常为.h或.hpp。每个翻译单元由编译器独立编译。编译完成后,链接器将编译后的翻译单元合并到单个程序中。违反ODR规则通常表现为链接器错误。当在多个翻译单元中定义相同名称时,会发生链接器错误。(编译阶段对标识符做声明查找,链接阶段做定义匹配。)

In general, the best way to make a variable visible across multiple files is to declare it in a header file. Then add an #include directive in every .cpp file that requires the declaration. By adding include guards around the header contents, you ensure that the names a header declares are only declared once for each translation unit. Define the name in only one implementation file.

通常,使变量在多个文件中可见的最佳方法是在头文件中声明它。然后在每个需要声明的.cpp文件中添加一个#include指令。通过在添加include-guard,可以确保头文件中声明的名称对于每个翻译单元只声明一次。仅在一个实现文件中定义名称。

In C++20, modules are introduced as an improved alternative to header files.

在C++20中,引入模块作为头文件的改进替代方案。

In some cases, it may be necessary to declare a global variable or class in a .cpp file. In those cases, you need a way to tell the compiler and linker what kind of linkage the name has. The type of linkage specifies whether the name of the object is visible only in one file, or in all files. The concept of linkage applies only to global names. The concept of linkage doesn't apply to names that are declared within a scope. A scope is specified by a set of enclosing braces such as in function or class definitions.

在某些情况下,可能需要在cpp文件中声明全局变量或类。在这种情况下,您需要一种方法来告诉编译器和链接器名称具有何种链接。链接类型指定对象的名称是仅在一个文件中可见,还是在所有文件中可见。链接的概念仅适用于全局名称。链接的概念不适用于在作用域内声明的名称。作用域由一组括号指定,例如在函数或类定义中。

2 using a head file and source file to defina and use a class

The following example shows a common way to define a class and then use it in a different source file. We'll start with the header file, my_class.h. It contains a class definition, but note that the definition is incomplete; the member function do_something is not defined:

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

// my_class.h
namespace N
{
    class my_class
    {
    public:
        void do_something();
    };
}

Next, create an implementation file (typically with a .cpp or similar extension). We'll call the file my_class.cpp and provide a definition for the member declaration. We add an #include directive for "my_class.h" file in order to have the my_class declaration inserted at this point in the .cpp file, and we include <iostream> to pull in the declaration for std::cout. Note that quotes are used for header files in the same directory as the source file, and angle brackets are used for standard library headers. Also, many standard library headers do not have .h or any other file extension.

接下来,创建一个实现文件(通常具有.cpp或类似扩展名)。我们将该文件称为my_class.cpp并为成员声明提供定义。我们为“my_class.h”文件添加了一个#include指令,以便在.cpp文件中插入my_class声明(特别是有多个成员函数或自由函数需要实现且存在相互调用关系时)。并且我们包括<iostream>来拉入std::cout的声明。请注意,引号用于源文件所在目录中的头文件,尖括号用于标准库头文件。此外,许多标准库标头没有.h或任何其他文件扩展名。

In the implementation file, we can optionally use a using statement to avoid having to qualify every mention of "my_class" or "cout" with "N::" or "std::". Don't put using statements in your header files!

在实现文件中,我们可以选择使用using语句,以避免每次提到“my_class”或“cout”时都必须用“N::”或“std::”限定。不要把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;
}

Now we can use my_class in another .cpp file. We #include the header file so that the compiler pulls in the declaration. All the compiler needs to know is that my_class is a class that has a public member function called do_something().

现在我们可以在另一个.cpp文件中使用my_class.cpp文件。我们#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;
}

After the compiler finishes compiling each .cpp file into .obj files, it passes the .obj files to the linker. When the linker merges the object files it finds exactly one definition for my_class; it is in the .obj file produced for my_class.cpp, and the build succeeds.

在编译器编译完每个.cpp文件为.obj文件后,编译器传递.obj文件给链接器。当链接器合并.obj文件时,它正好找到my_class的一个定义;在.obj文件内为my_class.cpp生成的obj文件.cpp,构建成功。

3 Include guards

Typically, header files have an include guard or a #pragma once directive to ensure that they are not inserted multiple times into a single .cpp file.

通常,头文件具有include-guard或#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 */

4 What not to put in a header file

Because a header file might potentially be included by multiple files, it cannot contain definitions that might produce multiple definitions of the same name. The following are not allowed, or are considered very bad practice:

由于头文件可能包含在多个文件中,因此它不能包含可能产生相同名称的多个定义的定义。以下是不允许的,或被视为非常糟糕的做法:

built-in type definitions at namespace or global scope

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

non-inline function definitions

非内联函数定义

non-const variable definitions

非常量变量定义

aggregate definitions

聚合定义

unnamed namespaces

未命名命名空间

using directives

using指令

Use of the using directive will not necessarily cause an error, but can potentially cause a problem because it brings the namespace into scope in every .cpp file that directly or indirectly includes that header.

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

5 Internal linkage by default

A free function is a function that is defined at global or namespace scope. Non-const global variables and free functions by default have external linkage; they're visible from any translation unit in the program. No other global object can have that name. A symbol with internal linkage or no linkage is visible only within the translation unit in which it's declared. When a name has internal linkage, the same name may exist in another translation unit. Variables declared within class definitions or function bodies have no linkage.

自由函数是在全局或命名空间范围内定义的函数。默认情况下,非常量全局变量和自由函数具有外部链接;它们可以从程序中的任何翻译单元中看到。没有其他全局对象可以具有该名称。具有内部链接或无链接的符号仅在其声明的翻译单元内可见。当一个名称具有内部链接时,同一名称可能存在于另一个翻译单元中。在类定义或函数体中声明的变量没有链接。

You can force a global name to have internal linkage by explicitly declaring it as static. This keyword limits its visibility to the same translation unit in which it's declared. In this context, static means something different than when applied to local variables.

通过显式地将全局名称声明为静态,可以强制全局名称具有内部链接。该关键字将其可见性限制在声明它的同一翻译单元。在这种情况下,静态的含义与应用于局部变量时不同。

The following objects have internal linkage by default:

默认情况下,以下对象具有内部链接:

const objects

常量对象

constexpr objects

constexpr对象

typedef objects

typedef对象

static objects in namespace scope

命名空间范围中的静态对象

To give a const object external linkage, declare it as extern and assign it a value:

要赋予常量对象外部链接,请将其声明为extern并为其赋值:

extern const int value = 42;

6 What to put in a header file

The various kinds of declarations and definitions that are allowed in a header file:

头文件中允许的各种声明和定义:

命名空间

enum定义

const常量定义(默认为内部链接)

constexpr定义

类型别名定义

extern声明

条件编译指令

全局函数声明

宏定义

类定义

结构体定义

类模板定义及类模板成员函数实现

类模板声明

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;
}

ref

https://docs.microsoft.com/en-us/cpp/cpp/declarations-and-definitions-cpp

https://docs.microsoft.com/en-us/cpp/cpp/program-and-linkage-cpp

https://docs.microsoft.com/en-us/cpp/cpp/header-files-cpp

-End-

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言