c++ - 解决由于类之间的循环依赖导致的构建错误

标签 c++ compiler-errors circular-dependency c++-faq

我经常发现自己在 C++ 项目中面临多个编译/链接器错误的情况,由于一些糟糕的设计决策(由其他人:))导致不同头文件中的 C++ 类之间的循环依赖(也可能发生)在同一个文件中)。但幸运的是(?)这不会经常发生,让我记住下一次再次发生时解决这个问题的方法。

所以为了以后方便记忆,我将发布一个有代表性的问题和一个解决方案。更好的解决方案当然是受欢迎的。

  • A.h
    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;
        }
    };
    


  • B.h
    #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;
    }
    
  • 最佳答案

    思考这个问题的方法是“像编译器一样思考”。

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

    // 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 !哎呀。

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

    您可以通过允许编译器保留尽可能多的空间来打破它,例如,指针和引用将始终为 32 位或 64 位(取决于体系结构),因此如果您将(任一)替换为一个指针或引用,事情会很棒。假设我们在 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()仍然说:
    // file: main.cc
    #include "A.h"  // <-- Houston, we have a problem
    
    #include , 对于所有范围和目的(如果您将预处理器取出)只需将文件复制到 .cc .真的, .cc 好像:
    // file: partially_pre_processed_main.cc
    class A {
      B& _b_ref;
      B* _b_ptr;
    };
    #include "B.h"
    int main (...) {
      A a;
    }
    

    你可以看到为什么编译器不能处理这个 - 它不知道是什么 B是 - 它以前甚至从未见过这个符号。

    所以让我们告诉编译器关于 B .这被称为 forward declaration ,并在 this answer 中进一步讨论.
    // main.cc
    class B;
    #include "A.h"
    #include "B.h"
    int main (...) {
      A a;
    }
    

    这有效。这不是很好。但是此时您应该了解循环引用问题以及我们为“修复”它所做的工作,尽管修复很糟糕。

    这个修复不好的原因是因为下一个人 #include "A.h"必须申报 B在他们可以使用它之前会得到一个可怕的#include错误。所以让我们把声明移到 本身。
    // file: A.h
    class B;
    class A {
      B* _b; // or any of the other variants.
    };
    

    而在 B.h ,此时,您只需#include "A.h"直接地。
    // 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; 
    }
    

    哈。

    关于c++ - 解决由于类之间的循环依赖导致的构建错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70178855/

    相关文章:

    c++ - cocos2dx 当我将 setPhysicsBody 设置为 sprite 时,sprite 消失了

    c++ - 将函数应用于 2 个 STL vector

    ubuntu - Haskell - GHC 错误 : Could not find module ‘Prelude’/Perhaps you haven't installed the profiling libraries for package ‘base-4.12.0.0’ ?

    c++ - 如何保证后声明的类的变量在前一个类中可见?

    c++ - 循环依赖和 std::vector::insert

    java - 如何解决 Eclipse 中的循环项目依赖关系。有什么方法可以使依赖项之一仅运行吗?

    c++ - 读取一个bmp文件数据并写入另一个bmp

    c++ - 为什么std::unique调用std::sort?

    java - 处理 Java 自定义异常

    php - 找出有关错误写入变量或任何其他错误的问题,在php中?