如何管理标头的#include?

时间:2012-06-02 20:01:39

标签: c++ header include

这可能回答了很多次,但我似乎无法找到正确的帖子来讨论这个问题。

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

在Java中,我可以拥有相互导入的类,但看起来使用C ++无法实现(为什么编译器不能真正处理它)。

无论如何,我该如何让它发挥作用?

2 个答案:

答案 0 :(得分:3)

为避免循环引用,您可以将每个包含文件“包装”到预处理器#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:

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

File Scene.h:

#ifndef SceneHIncluded
#define SceneHIncluded

class SceneObject;

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

#endif // SceneHIncluded

File 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文件中。

答案 1 :(得分:0)

通过尽可能在头文件中使用类和结构的前向声明,可以最好地避免循环引用。在非循环依赖中,使用前向声明还有一个额外的好处,即避免#include其他文件,从而缩短编译时间。

在3个A,B和C类的例子中,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

<强> main.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<>):

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

在这种情况下,如果您需要使用其中一个依赖类的功能,并在相应的 .cpp 文件中使用该功能,则这一点非常重要。所以除了类本身的名称之外的任何东西。当 a.cpp #include时,标题 a.hpp &amp; b.hpp b.cpp #include s b.hpp &amp; c.hpp c.cpp #include s c.hpp &amp; a.hpp 循环包含不会有问题,因为每个 .cpp 文件都被编译成自己的翻译单元,链接器将能够排序多个类定义没有问题,只要它们是相同的(它们将是相同的,因为它们来自相同的文件)。