【问题标题】:How to manage #include of header?如何管理标头的#include?
【发布时间】:2012-06-02 20:01:39
【问题描述】:

这可能被回答了很多次,但我似乎找不到合适的帖子来讨论这个问题。

假设我们有类 A、B 和 C,它们是彼此的容器。这意味着他们必须包含彼此的头文件。但是当我在 Visual Studio 2010 中执行此操作时,我收到错误消息“包含文件过多:深度 = 1024”。

在 Java 中,我可以拥有相互导入的类,但 C++ 似乎无法做到这一点(为什么编译器不处理这个问题,真的)。

无论如何,我该如何让它工作?

【问题讨论】:

  • 如果您真的需要这样,请转发声明该类,并存储指向该类的指针或引用。
  • 由于我计划在单独的文件中定义类,我如何使前向声明起作用?

标签: c++ header include


【解决方案1】:

为避免循环引用,您可以将每个包含文件“包装”到预处理器#ifdef 中。

文件 A.h:

#ifndef SOMEREALLYUNIQUEIDFileAincluded
#define SOMEREALLYUNIQUEIDFileAincluded

#include "B.h"

class B;

/// Here you can use pointers to B
class A
{
  // something about B*
};

#endif // SOMEREALLYUNIQUEIDFileAincluded

文件 B.h:

#ifndef SOMEREALLYUNIQUEIDFileBincluded
#define SOMEREALLYUNIQUEIDFileBincluded

#include "A.h"

class A;

/// Here you can use pointer to A
class B
{
  // something about A*
};

#endif // SOMEREALLYUNIQUEIDFileBincluded

#ifdef 被称为“包含守卫”

对于现代编译器,你只能写而不是写“ifdefs”

#pragma once

在每个文件的开头。

编辑:

那么你必须使用 C.cpp 中的所有头文件:

#include "A.h"

#include "B.h"

void test() {}

用“gcc -c C.cpp”测试它(仅编译)。

EDIT2:

某种样本。具有可渲染对象的场景。

文件 Scene.h:

#ifndef SceneHIncluded
#define SceneHIncluded

class SceneObject;

class Scene {
public:
   void Add(SceneObject* Obj);
   void Render();
private:
   std::vector<SceneObject*> Objects;
};

#endif // SceneHIncluded

文件 Scene.cpp:

#include "Scene.h"
#include "SceneObject.h"

void Scene::Add() { this->Objects.pusj_back(Obj); Obj->SceneRef = this; }

void Scene::Render() {
   for(size_t j = 0 ; j < Objects.size() ; j++) { Objects[j]->Render(); }
}

文件 SceneObject.h:

#ifndef SceneObjHIncluded
#define SceneObjHIncluded

class Scene;

class SceneObject {
public:
   /// This is not the sample of "good" OOP, I do not suppose that
   /// SceneObject needs this reference to the scene
   Scene* SceneRef;
public:
   // No implementation here
   virtual void Render() = 0;
 };

#endif // SceneObjHIncluded

SceneObject 的实现可能是一些带有变换的网格,即

 class Mesh: public SceneObject {...}

在 Mesh.h 和 Mesh.cpp 文件中。

【讨论】:

  • 您是否包含其他类定义向前声明该类?前向声明足以使用指针。
  • 好吧,当然,前向定义就足够了,但是如果 OP 也询问“递归包含”,那为什么不提这个呢?
  • 最后我检查了这两个都搞砸了编译器。
  • 检查并添加到帖子的末尾。对我来说很好,应该不是问题
  • 也许,一个有用的建议是移动所有“A类;B类;”像许多大型项目一样,将东西放到一个单独的文件中。
【解决方案2】:

尽可能通过在头文件中对类和结构使用前向声明来避免循环引用。在非循环依赖中,使用前向声明的额外好处是避免#includeing 额外的文件,从而缩短编译时间。

在A、B、C这3个类的例子中,A包含B,B包含C,C包含A;您可以执行以下操作:

a.hpp:

#ifndef A_HPP
#define A_HPP

#include <memory>
#include <vector>

// Forward-declare B
struct B;

struct A
{
    std::vector<std::shared_ptr<B>> bs;
};

#endif

b.hpp:

#ifndef B_HPP
#define B_HPP

#include <memory>
#include <vector>

// Forward-declare C
struct C;

struct B
{
    std::vector<std::shared_ptr<C>> cs;
};

#endif

c.hpp:

#ifndef C_HPP
#define C_HPP

#include <memory>
#include <vector>

// Forward-declare A
struct A;

struct C
{
    std::vector<std::shared_ptr<A>> as;
};

#endif

ma​​in.cpp:

#include "a.hpp"
#include "b.hpp"
#include "c.hpp"

int main()
{
    std::shared_ptr<A> a(new A);
    std::shared_ptr<B> b(new B);
    std::shared_ptr<C> c(new C);

    a->bs.push_back(b);
    b->cs.push_back(c);
    c->as.push_back(a);
}

示例在 g++-4.7 中编译(这个概念同样适用于 c++98,你不能使用shared_ptr&lt;&gt;):

g++ -std=c++11 -pedantic main.cpp

在这种情况下,如果您需要使用某个依赖类中的功能,请务必在其对应的 .cpp 文件中使用该功能。所以除了类本身的名称之外的任何东西。当 a.cpp #includes 标头 a.hpp & b.hpp, b.cpp @ 987654329@s b.hpp & c.hppc.cpp #includes c.hpp & a.hpp 循环包含不会有问题,因为每个 .cpp 文件都被编译成自己的翻译单元,链接器将能够整理出多个类定义没有问题,只要它们相同(它们将是相同的,因为它们来自相同的文件)。

【讨论】:

  • 现在,如果我想让 A、B 和 C 都成为彼此的容器,该怎么办?如果我在多个头文件中转发同一个类,程序会崩溃吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-29
  • 2013-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-31
相关资源
最近更新 更多