推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

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

   2023-08-02 网络整理佚名2090
核心提示:在使用程序元素(如变量、函数、类等)之前,必须声明它们的名称。为了尽量减少错误的可能性,C++采用了使用头文件包含声明的约定。,cpp文件或其他需要该声明的头文件中使用#指令。不要把using语句放在头文件中!通常,头文件具有-guard或#-once指令,以确保它们不会多次插入到单个文件中.由于头文件可能包含在多个文件中,因此它不能包含可能产生相同名称的多个定义的定义。

0 和

、 、 、 等名称必须可以使用。 对于 ,您不能只在第一个 'x' 处写 x = 42。

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

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

告诉 是一个 int、a、a、类或其他东西。 ,每个名称在使用它的每个 .cpp 文件中都必须是 ( 或 )。 当您创建时,每个 .cpp 文件都被放入一个单元中。 没有其他单位的名字。 这意味着,如果您使用类 或 或 ,则必须在使用它的每个 .cpp 文件中使用该内容。 每一个东西都必须存在于所有文件中。 A 会导致 , 或 , 当 时 将所有单元合并为 a 。

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

对于而言,C++ 具有使用文件的功能。 您在一个文件中创建 ,然后在每个 .cpp 文件或其他文件中使用 # 。 # 将文件复制到.cpp 文件之前。

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

C++20 相当于文件的 and 。

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

诸如 、 、 类型和 之类的 C++。 其中每一个都必须可以使用。 A 的名称,以及有关其类型等信息。 在 C++ 中,名称所在的点就是它指向 . 您不能引用单元中稍后某个时刻的 或 类。 尽可能靠近它们的使用点。

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

一些 , , , 枚举和 , 必须和 一样。 包含稍后在 中使用时需要编码的所有内容。 A 必须是 ,换句话说,是一个值,与它的 相同。 内置类型(例如 int)的 A 知道它需要多少空间。

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

1 单位和

在 C++ 中, a 代表 a 或名称,可以是其范围的任意倍。 ,只能是一次。 这条规则就是“一条规则”(ODR)。 A(或)一个名字进入到,以及后来的名字加上一个。 一个名字和所有的一切。 如果名称为 a ,则 a 和 it 。 加上身体的A。 一个类的类名由一个块列出,列出了所有的类。 ( 的 可能在文件中。)

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

展示了一些:

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

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

展示了一些:

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

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

一个或多个单位的 A。 文件的一个单元以及它或 . 文件的文件名为 .cpp 或 .cxx。 文件的扩展名为 .h 或 .hpp。 每个单元均由 . 将单位转换为 。 ODR 规则的显示为 。 当同一名称出现在多个单元中时,就会发生这种情况。

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

在 中,制作文件的最佳方法是将其放在文件中。 然后在每个 .cpp 文件中添加一个 # 来表示 . 至此,您知道名称 a 对于每个单元只有一次。 名称仅存在于一个文件中。

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

在 C++20 中,作为文件。

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

在某些情况下,它可能是 .cpp 文件中的 a 或 class。 在这些情况下,您需要一种方法来区分名称的类型。 名称的类型只能在一个文件中,或者在所有文件中。 仅限于名称。 of 不适用于属于范围的名称。 范围由一组诸如 in 或 class 组成。

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

2 使用头文件和文件来并使用类

该图展示了一种获取类然后在文件中使用它的方法。 我们将从文件 .h 开始。 它是一个类,但请注意 is ; 这不是:

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

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

接下来是一个文件(带有 .cpp 或 )。 我们将文件命名为 .cpp,将 .cpp 文件命名为 a。 我们为“.h”文件添加一个#,以便在.cpp 文件中包含此时的内容,然后我们拉入for std::cout。 请注意, 用于表示与文件相同的文件,而角度用于表示 。 此外,许多没有 .h 或任何其他文件。

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

在文件中,我们可以使用 using 来避免每个“”或“cout”与“N::”或“std::”。 不要将 using 放入您的文件中!

在实现文件中,我们可以选择使用 using 语句,以避免每次提到“”或“cout”时都必须使用“N::”或“std::”进行限定。 不要将 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文件中使用。 我们 # 该文件以便拉入 . 所有需要知道的是这是一个带有 () 的类。

现在我们可以在另一个 .cpp 文件中使用 .cpp 文件。 我们# 文件,以便编译器提取声明。 编译器需要知道的是这是一个具有公共成员函数 ( ) 的类。

// my_program.cpp
#include "my_class.h"
using namespace N;
int main()
{
    my_class mc;
    mc.do_something();
    return 0;
}

将每个.cpp文件转换成.obj文件后,它会将.obj文件复制到.obj文件中。 当它找到一个文件时; 它位于 .cpp 的 .obj 文件和 build .

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

3

,文件有一个保护或一个 # 一次,表示它们不会多次进入 .cpp 文件。

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

4 哪些内容不宜放入文件中

一个文件可能由多个文件组成,并且可能具有相同的名称。 不是,或者说非常糟糕:

include指令元素和动作元素区别_三种指令元素_指令元素和动作元素的区别

由于一个头文件可能包含在多个文件中,因此它不能包含可能导致多个同名定义的定义。 以下行为是不允许的,或者被认为是非常不好的做法:

内置类型或范围

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

非内联函数定义

非常量

非常量变量定义

聚合定义

未命名的命名空间

使用

使用指令

使用 using 不会导致错误,但可能会导致它进入每个 .cpp 文件中的作用域。

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

5 由

免费是指处于或范围内的。 通过 have 实现非 const 和 free ; 他们来自 的任何单位。 没有其他人可以拥有这个名字。 a with 或 no 只是它所在的单位。 ,单元中可能存在同名。 类还是没有。

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

您可以强制名称为 。 这与它所在的单位相同。 在此,意味着比当地方。

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

其有:

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

常量

常量对象

目的

目的

在适用范围

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

给一个 const ,它作为它的值:

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

extern const int value = 42;

6 文件中要放入什么内容

文件中的 和 that 的种类:

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

命名空间

枚举定义

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

定义

类型别名定义

陈述

条件编译指令

全局函数声明

宏定义

类定义

结构定义

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

类模板声明

显示文件中 和 的种类:

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

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

参考

-结尾-

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON