默认情况下,编译指示应位于每个标头的顶部

问题描述

我正在学习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 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”的标头(一次在main.cpp:1处,一次在Bh:1期间,在main.cpp:2中包含一次)。实际上,编译器两次“看到” class A的定义,并认为我们两次定义了A。

我无法理解的是包含防护:

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

一次用#pragma修复的A.h

#pragma once
#include <iostream>

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

允许程序很好地编译。

对我来说,这意味着我应该以#pragma开始每个标题一次!哪个不对?这是惯例吗?如果是这样,有没有办法在编译时执行此操作(例如,作为标志)?

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

我想念什么?有办法完全避免这个问题吗?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)