解决由于类之间的循环依赖而导致的构建错误 [英] Resolve build errors due to circular dependency amongst classes

查看:286
本文介绍了解决由于类之间的循环依赖而导致的构建错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于一些错误的设计决策(由其他人做出的:)),我经常遇到处于C ++项目中的多个编译/链接器错误的情况,这导致不同头文件中C ++类之间的循环依赖(也可能在同一文件中发生)。但是幸运的是(?)这种情况发生得并不多,以至于我下次再次遇到该问题时仍记得该问题的解决方案。

I often find myself in a situation where I am facing multiple compilation/linker errors in a C++ project due to some bad design decisions (made by someone else :) ) which lead to circular dependencies between C++ classes in different header files (can happen also in the same file). But fortunately(?) this doesn't happen often enough for me to remember the solution to this problem for the next time it happens again.

因此,为了轻松起见回想一下,将来我将发布一个具有代表性的问题及其解决方案。当然,欢迎使用更好的解决方案。

So for the purposes of easy recall in the future I am going to post a representative problem and a solution along with it. Better solutions are of-course welcome.


class B;
class A
{
    int _val;
    B *_b;
public:

    A(int val)
        :_val(val)
    {
    }

    void SetB(B *b)
    {
        _b = b;
        _b->Print(); // COMPILER ERROR: C2027: use of undefined type 'B'
    }

    void Print()
    {
        cout<<"Type:A val="<<_val<<endl;
    }
};



  • Bh

#include "A.h"
class B
{
    double _val;
    A* _a;
public:

    B(double val)
        :_val(val)
    {
    }

    void SetA(A *a)
    {
        _a = a;
        _a->Print();
    }

    void Print()
    {
        cout<<"Type:B val="<<_val<<endl;
    }
};



  • main.cpp

#include "B.h"
#include <iostream>

int main(int argc, char* argv[])
{
    A a(10);
    B b(3.14);
    a.Print();
    a.SetB(&b);
    b.Print();
    b.SetA(&a);
    return 0;
}


推荐答案

对此进行思考的方法是像编译器一样思考。

The way to think about this is to "think like a compiler".

想象一下您正在编写一个编译器。您会看到这样的代码。

Imagine you are writing a compiler. And you see code like this.

// file: A.h
class A {
  B _b;
};

// file: B.h
class B {
  A _a;
};

// file main.cc
#include "A.h"
#include "B.h"
int main(...) {
  A a;
}

编译 .cc 文件时(请记住, .cc 而不是 .h 是编译单位),您需要为对象 A 。那么,那么多少空间呢?足以存储 B !那么 B 的大小是多少?足以存储 A !哎呀。

When you are compiling the .cc file (remember that the .cc and not the .h is the unit of compilation), you need to allocate space for object A. So, well, how much space then? Enough to store B! What's the size of B then? Enough to store A! Oops.

很显然,您必须破坏循环引用。

Clearly a circular reference that you must break.

您可以通过允许编译器执行以下操作来破坏它:相反,要保留尽可能多的空间,例如,指针和引用将始终为32位或64位(取决于体系结构),因此,如果用指针或引用替换(一个),那么事情就好了。 。假设我们替换 A

You can break it by allowing the compiler to instead reserve as much space as it knows about upfront - pointers and references, for example, will always be 32 or 64 bits (depending on the architecture) and so if you replaced (either one) by a pointer or reference, things would be great. Let's say we replace in A:

// file: A.h
class A {
  // both these are fine, so are various const versions of the same.
  B& _b_ref;
  B* _b_ptr;
};

现在情况变好了。有些。 main()仍显示:

Now things are better. Somewhat. main() still says:

// file: main.cc
#include "A.h"  // <-- Houston, we have a problem

#include ,在所有方面和用途(如果您将预处理器取出),只需将文件复制到 .cc 中即可。所以真的 .cc 看起来像:

#include, for all extents and purposes (if you take the preprocessor out) just copies the file into the .cc. So really, the .cc looks like:

// file: partially_pre_processed_main.cc
class A {
  B& _b_ref;
  B* _b_ptr;
};
#include "B.h"
int main (...) {
  A a;
}

您可以看到为什么编译器无法处理它-它没有知道 B 是什么-以前从未见过这个符号。

You can see why the compiler can't deal with this - it has no idea what B is - it has never even seen the symbol before.

所以让我们告诉编译器 B 。这称为转发声明,并在此答案

So let's tell the compiler about B. This is known as a forward declaration, and is discussed further in this answer.

// main.cc
class B;
#include "A.h"
#include "B.h"
int main (...) {
  A a;
}

可行。不是很棒。但是在这一点上,您应该已经了解了循环引用问题以及我们对其进行修复的操作,尽管修复效果很差。

This works. It is not great. But at this point you should have an understanding of the circular reference problem and what we did to "fix" it, albeit the fix is bad.

此修复的原因是不好是因为下一个 #include Ah 的人必须声明 B 才能使用它,并且会出现严重的 #include 错误。因此,让我们将声明移到 Ah 本身。

The reason this fix is bad is because the next person to #include "A.h" will have to declare B before they can use it and will get a terrible #include error. So let's move the declaration into A.h itself.

// file: A.h
class B;
class A {
  B* _b; // or any of the other variants.
};

Bh​​ 中,此时,您只需 #include Ah 直接。

And in B.h, at this point, you can just #include "A.h" directly.

// file: B.h
#include "A.h"
class B {
  // note that this is cool because the compiler knows by this time
  // how much space A will need.
  A _a; 
}

HTH。

这篇关于解决由于类之间的循环依赖而导致的构建错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆