我正在学习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