Header Guards和LNK4006

Ste*_*eve 10 c++ linker-warning

我在标题中定义了一个字符数组

//header.h
const char* temp[] = {"JeffSter"};
Run Code Online (Sandbox Code Playgroud)

如果#defined受到保护,则标题在顶部有一次#pragma.如果这个头包含在多个地方,我得到一个LNK4006 - 已经在blahblah.obj中定义的char const**temp.所以,我有几个问题

  1. 如果我有防护装置,为什么会发生这种情况?我认为他们阻止了首次访问后读取标头.
  2. 为什么此标题中的众多枚举还没有给出LNK4006警告?
  3. 如果我在签名前添加静态,我不会收到警告.这样做有什么意义呢.
  4. 有没有更好的方法来避免错误,但让我在标题中声明数组.我真的很讨厌只有一个cpp文件用于数组定义.

Alo*_*hal 14

如果我有防护装置,为什么会发生这种情况?我认为他们阻止了首次访问后读取标头.

包含警卫确保标题仅包含在一个文件(翻译单元)中一次.对于包含标题的多个文件,您希望标题包含在每个文件中.

通过定义,而不是在头文件中使用外部链接(全局变量)声明变量,您只能在一次源文件中包含标头.如果在多个源文件中包含标头,则会有多个变量定义,这在C++中是不允许的.

因此,正如您所知,在头文件中定义变量是一个坏主意,正是出于上述原因.

为什么此标题中的众多枚举还没有给出LNK4006警告?

因为,它们没有定义"全局变量",它们只是关于类型等的声明.它们不保留任何存储空间.

如果我在签名前添加静态,我不会收到警告.这样做有什么意义呢.

创建变量时static,它具有静态范围.该对象在定义它的转换单元(文件)之外是不可见的.所以,简单来说,如果你有:

static int i;
Run Code Online (Sandbox Code Playgroud)

在标题中,包含标题的每个源文件都将获得一个单独的 int变量i,该变量在源文件之外是不可见的.这被称为内部联系.

有没有更好的方法来避免错误,但让我在标题中声明数组.我真的很讨厌只有一个cpp文件用于数组定义.

如果您希望数组是所有C++文件中可见的一个对象,您应该:

extern int array[SIZE];
Run Code Online (Sandbox Code Playgroud)

在头文件中,然后在所有需要该变量的C++源文件中包含头文件array.在其中一个 source(.cpp)文件中,您需要定义array:

int array[SIZE];
Run Code Online (Sandbox Code Playgroud)

您还应该在上面的源文件中包含标题,以允许捕获由于标头和源文件的差异而导致的错误.

基本上,extern告诉编译器" array在某处定义,并具有类型int和大小SIZE".然后,您实际上只定义 array一次.在链接阶段,一切都很好地解决了.


Geo*_*che 5

包含防护可以防止您将相同的标头重复包含到同一个文件中,但不能将其包含到不同的文件中。
发生的情况是链接器看到temp多个目标文件 - 您可以通过将其设置为temp静态或将其放入未命名的命名空间来解决此问题:

static const char* temp1[] = {"JeffSter"};
// or
namespace {
    const char* temp2[] = {"JeffSter"};
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用一个源文件来定义temp并在标头中将其声明为 extern:

// temp.cpp:
const char* temp[] = {"JeffSter"};

// header.h:
extern const char* temp[];
Run Code Online (Sandbox Code Playgroud)


AnT*_*AnT 5

  1. 标题保护与防止整个程序中的多个定义完全无关。头保护的目的是防止将同一头文件多次包含到同一翻译单元(.cpp 文件)中。换句话说,它们的存在是为了防止在同一个源文件中有多个定义。在您的情况下,它们确实按预期工作。

  2. 在 C++ 中管理多重定义问题的规则称为单一定义规则 (ODR)。对于不同类型的实体,ODR 的定义不同。例如,允许类型在程序中具有多个相同的定义。它们可以(并且大多数情况下必须)在使用它们的每个翻译单元中定义。这就是为什么您的枚举定义不会导致错误的原因。

    具有外部链接的对象是一个完全不同的故事。它们必须在一个且仅一个翻译单元中定义。这就是temp当您将头文件包含到多个翻译单元时,您的定义会导致错误的原因。包含警卫无法防止此错误。只是不要在头文件中定义具有外部链接的对象。

  3. 通过添加static你给你的对象内部链接。这将使错误消失,因为现在从 ODR 的角度来看完全没问题。但这将temp在包含您的头文件的每个翻译单元中定义一个独立的对象。为了达到同样的效果,你也可以这样做

    const char* const temp[] = { "JeffSter" }; 
    
    Run Code Online (Sandbox Code Playgroud)

    因为constC++ 中的对象默认具有内部链接。

  4. 这取决于您是需要一个带有外部链接的对象(即一个用于整个程序的对象)还是一个带有内部链接的对象(每个翻译单元唯一的)。如果您需要后者,请使用static和/或额外const(如果这对您有用),如上所示。

    如果需要前者(外部链接),则应在头文件中放入一个非定义声明

    extern const char* temp[];
    
    Run Code Online (Sandbox Code Playgroud)

    并将定义移动到一个且仅一个 .cpp 文件中

    char* const temp[] = { "JeffSter" }; 
    
    Run Code Online (Sandbox Code Playgroud)

    头文件中的上述声明适用于大多数目的。然而,它声明temp为一个未知大小的数组——一个不完整的类型。如果您希望将其声明为已知大小的数组,则必须手动指定大小

    extern const char* temp[1];
    
    Run Code Online (Sandbox Code Playgroud)

    并记住在声明和定义之间保持同步。