默认情况下,pragma once 应该位于每个 header 之上

should pragma once be on top of every header as default

我正在学习 C++,我遇到(并修复)了一个看起来非常 classic 的问题:

g++ main.cpp A.cpp B.cpp -o out
In file included from B.h:1,
                 from main.cpp:3:
A.h:1:7: error: redefinition of ‘class A’
    1 | class A {
      |       ^
In file included from main.cpp:2:

通过快速研究(假设我理解正确),发生这种情况是因为 #include operation is not "idempotent"(我在这个问题中发现的一个术语)。

为了说明我的问题,我提出了一个最小的工作示例。

main.cpp

#include "A.h"
#include "B.h"
#include <iostream>

int main () {
  std::cout << "Hello world" << std::endl;
  A a;
  B b(a);
  return 0;
}

A.h

#include <iostream>

class A {
  public:
      void test();
};

A.cpp

#include "A.h"

void A::test () {
  std::cout << "test" << std::endl;
}

B.h

#include "A.h"

class B {
  public:
      B(A);
};

B.cpp

#include "B.h"
#include <iostream>

B::B(A a){
  a.test();
}

使用 g++ main.cpp A.cpp B.cpp 或更具体地说 g++ -c main.cpp 编译程序将失败并出现上述错误。

我了解到编译器 transcludes 在编译 main.cpp 时“A”的 header 两次(一次在 main.cpp:1 和一次在 B.h:1 在其自身包含在 main.cpp:2) 期间。实际上,编译器 'sees' 定义了 class A 两次,并认为我们定义了 A 两次。

我不明白的是包含守卫:

要解决此问题,可以在多次包含的文件顶部使用关键字:pragma once

A.h fixed with #pragma once

#pragma once
#include <iostream>

class A {
  public:
      void test();
};

允许程序顺利编译。

对我来说,这表明我应该在每个 header 开始时使用 #pragma once !!! 哪个不对?这是常见的做法吗?如果是这样,有没有办法在编译时执行此操作(例如作为标志)?

如果我不这样做,我可能不会使用 object A 作为 class A 的成员,也不会将其作为参数传递给 B(如在我的示例中的 B 的构造函数中),如果这样的话A 和 B 可以在另一个文件中单独使用;除非每次出现问题时我都反应性地添加 #pragma once ,这对我来说似乎是“肮脏的”。此外,我无法与任何人分享我的资源,因为担心他们会遇到我的两个 object 的情况,而不必自己在我的文件中添加 pragma once

我错过了什么?有没有办法完全避免这个问题?

您可以使用 #pragma once or the more "traditional" include guard.

如您所料,它应该存在于每个头文件中。

在此示例中,您提供了:

#pragma once
#include <iostream>

class A {
  public:
      void test();
};

不保证适用于所有环境。 (就像其他评论提到的那样。)以及 Circular Dependices.

但是使用两个预处理器指令:#ifndef#endif(包括守卫) 防止头文件被意外包含多次。 #ifndef A_H 告诉预处理器查找名为 A_H 的常量,该常量尚未使用 #define 指令创建。

现在,如果 A_H 常量尚未定义,则程序中将包含以下行:

  class A {
public:
    void test();
};

例如使用 ifndef#endif:

#ifndef A_H
#define A_H

class A {
public:
    void test();
};
#endif

To me this suggest that I should start every header with #pragma once !!! Wich can't be right is it?

可能是对的。虽然理论上可能有点夸张。

Is it common practice?

是的,是的。如果我们 包括 使用宏 header 守卫的另一个选项到相同的实践中,这是相当普遍的。

If so, Is there a way to do that at compile time instead (as a flag for instance)?

如果你的意思是,有没有办法让 pre-processor 将每个包含的文件都视为包含 pragma,无论它们是否有任何形式的 header 守卫那么没有,有在 C++ 中无法做到这一点。

理论上,您可以编写自己的 pre-processor 来执行此操作。然而,虽然这样pre-processor会相对简单,但我仍然认为在利益方面是一个不必要的复杂解决方案。

unless I add #pragma once reactively every time the problem pops up which seems "dirty" to me.

Is there a way to avoid the problem altogether?

有一个简单的方法可以pre-emptively 解决您已经提到的这个问题:在每个 header 文件的顶部添加 pragma 或传统宏 header 守卫。无需等待问题出现。这样做,你的烦恼就没有了。