默认情况下,pragma应该在每个标头的顶部出现一次



我正在学习C++,我遇到了(并修复了(一个似乎非常经典的问题:

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操作不是"包含";幂等";(我在这个问题上发现了一个术语(。

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

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编译程序将失败,并出现如上所示的错误。

我理解编译器将";A";编译main.cpp时两次(一次在main.cpp:1,另一次在B.h:1,在其自身包含main.cpp:2期间(。实际上,编译器"看到"了class A的定义两次,并认为我们定义了A两次。

我不明白的是包含防护:

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

A.h用#pragmaonce 修复

#pragma once
#include <iostream>
class A {
public:
void test();
};

允许程序很好地编译。

对我来说,这建议我应该用#pragma开头每个标头一次哪个不可能是对的,是吗?这是常见的做法吗?如果是这样的话,有没有办法在编译时这样做(例如作为标志(?

如果我不这样做,我可能不会使用对象A作为类A的成员,也不会将其作为参数传递给B(如在我的例子中的B的构造函数中(,如果这样的A和B可以在另一个文件中单独使用的话;除非每次问题弹出时我反应性地添加CCD_ 5;脏的";此外,如果不在我的文件中添加pragma once,我将无法与任何人共享我的来源,因为我担心他们会遇到我的两个对象的情况。

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

对我来说,这建议我应该用#pragma开头每个标头一次!!!维奇不会是对的吧?

这可能是对的。尽管理论上可能有点夸张。

这是常见的做法吗?

是的。如果我们在相同的实践中包括使用宏标头保护的另一个选项,这是相当普遍的。

如果是,有没有办法在编译时这样做(例如作为标志(?

如果你的意思是,有没有一种方法可以让预处理器将每个包含的文件视为包含杂注,无论它们是否具有任何形式的头保护,那么没有,在C++中没有办法做到这一点。

理论上,你可以编写自己的预处理器来完成这项工作。然而,尽管这样的预处理器相对简单,但我仍然认为这是一个不必要的复杂解决方案。

除非每次问题弹出时我都会反应性地添加#pragma一次,这似乎是";脏的";

有没有办法完全避免这个问题?

有一种简单的方法可以先发制人地解决这个问题,您已经提到过:在每个头文件的顶部添加pragma或传统的宏头保护。没有必要等待问题突然出现。只要这样做,你的担忧就消失了。

您可以使用#pragma一次或多次"传统的";包括警卫。

正如您所猜测的,它应该出现在每个头文件中。

在此示例中,您提供了:

#pragma once
#include <iostream>
class A {
public:
void test();
};

不能保证在所有环境中都能工作。(就像其他评论提到的一样。(以及循环家属。

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

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

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

例如,使用ifndef#endif:

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

最新更新