gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 10:58:05 29 4
gpt4 key购买 nike

我经常发现自己处于这样的情况下,由于一些错误的设计决策(由其他人做出的:),导致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错误。因此,让我们将声明移到 A.h 本身。
    // 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;
    }

    HTH。

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

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com