gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-04 12:37:57 26 4
gpt4 key购买 nike

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

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


  • 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/55546018/

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