我该如何解决这种循环依赖?

How can I solve this circular dependency?

我有两个 类:DESEngine 和 UserEvents。 DESEngine 应该是 UserEvents 的 "owner",但 UserEvents 应该可以通过类似以下方式访问 DESEngines 变量和方法:Owner->Method();

但我收到多个错误,如图所示:Error List(错误包含在 post 末尾的文本格式中)

我几乎可以肯定几乎所有的错误都是由于循环依赖引起的,但我一直无法解决它们:/

DESEngine.h

#ifndef DESENGING
#define DESENGINE

#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include "boost/any.hpp"
#include "GlobalVariables.h"
#include "TextParser.h"
#include "UserEvents.h"

class GlobalVariables;
class TextParser;
class UserEvents;

class DESEngine
{
public:
// System Classes
    GlobalVariables GVar_User = GlobalVariables();
    GlobalVariables GVar_EventLabels = GlobalVariables();
    UserEvents UsrEvt = UserEvents(*this);

    DESEngine();
    ~DESEngine();

    // Irrelevant stuff omitted

private:
   // Irrelevant stuff omitted
    std::unordered_map<std::string, void(DESEngine::*)(const std::string&)> SystemFunctionPointerMap;
    void DESEngine::ExtractEventParameter(std::string &WordBlock, std::vector<boost::any> &EvtParams);
};

#endif

UserEvents.h

#ifndef USEREVENTS
#define USEREVENTS


#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <typeinfo>
#include <boost/any.hpp>
// #include "DESEngine.h" --> moved to DESEngine.cpp

class DESEngine;

class UserEvents
{

public:

    // Class constructor / destructor
    UserEvents(DESEngine &Engine);
    ~UserEvents();

    // Select which function(parameters) to call
    int UserEvents::Choose(const DESEngine::EventWithParams &Event);


private:
    DESEngine Owner;

    // Here we have an unordered map that assigns User Function (pointer) to each Key (string / Alias / Event Name)
    std::unordered_map<std::string, void(UserEvents::*)(const std::vector<boost::any>&)> UserFunctionPointerAliasMap;
// Irrelevant stuff omitted
    };

    #endif

C2079 'UserEvents::Owner' uses undefined class 'DESEngine'  33      
C2027 use of undefined type 'DESEngine' 29      
C4430 missing type specifier - int assumed. Note: C++ does not support default-int  29      
C2143 syntax error: missing ',' before '&'  29      
C2079 'UserEvents::Owner' uses undefined class 'DESEngine'  33      
C2440 '=': cannot convert from 'DESEngine' to 'int' 11      
C2511 'int UserEvents::Choose(const DESEngine::EventWithParams &)': overloaded member function not found in 'UserEvents'    24      
C2671 'UserEvents::Choose': static member functions do not have 'this' pointers 27      
C2228 left of '.at' must have class/struct/union    27      
C2027 use of undefined type 'DESEngine' 29      
C4430 missing type specifier - int assumed. Note: C++ does not support default-int  29      
C2143 syntax error: missing ',' before '&'  29      
C2079 'UserEvents::Owner' uses undefined class 'DESEngine'  33      
C2027 use of undefined type 'DESEngine' 29      
C4430 missing type specifier - int assumed. Note: C++ does not support default-int  29      
C2143 syntax error: missing ',' before '&'  29  

除了一些小的更正 - 你不能在 class B 中有 class A 的完整对象,在 class A 中不能有 class B 的完整对象;产生无限递归。

其中之一(至少)需要是指针或引用。

由于这是 DESEngineUserEvents 之间的 has a 关系,在其 class 结构中,您可以稍微重新设计您的 classes不会失去任何功能。这种设计上的细微变化应该会改善内存管理以及 class 之间的关系或联系。当一个 class Has a <T>Owns 一个 object 到另一个 classstructure 时,拥有 class 只需要包含 objects header 在它自己的 header 中,不需要 class prototype declaration。现在属于另一个的 class 不应该在它自己的 header 中包含拥有 class 的 header 但确实需要 class prototype declaration and needs to include the owning class'sheader in its实施`文件。

还有智能指针发挥作用的所有权关系。由于您的 DESEngine class object 拥有一个 UserEvents object,所以在 class 中拥有一个 std::unique_ptr<T> 是安全的。这将为您创建一个 T 的指针,并且还应该有助于管理该分配和 de-allocation 内存并重置指针以避免内存泄漏和悬垂指针问题。有了这个,只有 DESEngine 的这个实例才能访问和修改 UserEvents 的那个实例或副本。现在,如果除了引擎之外您还需要其他资源来修改并访问 UserEvents,您可以轻松地将其更改为 std::shared_ptr<T>

这是一组 classes...

的示例

例子

Owner.h

#ifndef OWNER_H
#define OWNER_H

#include "SomeObject.h" // This class is owned by Owner since Owner has a SomeObject class object

/*No Need To Have Class Prototype Declaration*/ // #include "SomeClass.h"
// This does however need to be included in "Owner.cpp" 

class Owner {
    std::unique_ptr<SomeObject> myRestrictedObject;
    std::shared_ptr<SomeObject> mySharedResourceObject;
};
#endif // OWNER_H 

Owner.cpp

#include "stdafx.h" // if used  
#include "SomeObject.h"
#include "SomeOtherClasses.h"

// Class Body Or Implementation Definitions

SomeObject.h

#ifndef SOME_OBJECT_H
#define SOME_OBJECT_H

// Since Owner.h already has #include `THIS.h` we do not want to include it here but we will need this:
class Owner; 

class SomeObject {
private:
     // Google Search For Using Friend relationship.
};

#endif // SOME_OBJECT_H

SomeObject.cpp

// We need to include Owner.h here
#include "Owner.h"

现在,当使用另一个人拥有的 SomeObject 时,您应该能够通过将这两个 class 彼此成为朋友,要么是完全朋友 class,要么是通过每个 class 中的特定方法。有了这个,你可以控制两个 class 如何相互作用的内部关系。通过精心设计,您的产品代码的用户不必担心您的功能、class 及其方法和模板的内部实现细节,如果它结构良好且功能符合预期的话做并相应地命名以提高可读性。

现在,如果你想让这个 object 包含它拥有的指针 object 那么你可能想要远离使用朋友,但也尽量避免他们中的任何一个接受一个指针彼此的指针或 object。换句话说,两个 classes 都应该有默认构造函数,如果一个依赖另一个进行构造,它总是可以首先使用默认构造函数,然后你可以使用初始化或更新方法来更改 class 的通过该方法指向另一个 class 的内部变量指针。在某些情况下或需要,您可以让一个 class 成为静态的 class 而不是实际的 object。然后你需要一个静态获取函数 return 它的 *this 作为静态指针返回给你然后其他依赖 classes 也可以使用静态指针,特别是如果你只会有class object.

的 1 个实例

为了使用可以很容易地用谷歌搜索来查看它是如何完成的朋友。至于后一个选项,如果需要,我可以给出一个小例子。

因为您的应用程序似乎只应该有一个 DESEngineUserEvents 的实例,因为不需要有多个实例,除非您正在汇集一堆的 UserEvents object 然后你可以从一个基本的 Singleton 接口派生这两个 class 。然后确保这两个 object 都声明了一个指向自身的静态指针以及该指针的 get 方法。然后其他使用此 object 的 classes 可以使用该静态指针,或者类似地但不是静态单例,您可以使用智能指针甚至两者的组合。这完全取决于您的特定需求。

包含的位置和 class 原型声明是循环依赖的最重要决定因素。现在您还显示了其他 classes,这两个 classes 与包含的其他 header 文件一起使用,并且其中之一可能也是罪魁祸首但没有直到现在才出现这两个 classes。

我还想补充 WhozCraig 的评论,您正在将 DESEngine 的引用传递给 UserEvents 构造函数。因此,让我们看看编译器试图从您那里了解什么...

在源代码中的某个其他函数中,您正在创建 DESEngine 的实例,例如;

main.cpp

#include "DESEngine.h"

int main() {
    DESEngine engine; // This is okay because you declared it with a default constructor.

}

所以编译器进入 DESEngine.h 并在定义之前查看此 class 的声明,它对自己说好吧,我需要一个 UserEvents class 让我们转到它的构造函数,好的,这个构造需要一个 Reference 到一个 DESEngine object 并且它来自 DESEngine 变量声明到当前的 UserEvents 我还没有'尚未完成声明正在创建或定义 DESEngine object。所以它绕着圈子试图根据声明完成定义。

简而言之,你不应该让DESEngine成为UserEvents的成员。这也是不合理的,因为您想要对其所有者的 reference 而不是 copy.

所以它应该看起来像:

<< deseingine.h >>
class DESEngine {
private:
  UserEvents events;

public:
  DESEngine() : events(*this) { }
};

<< userevents.h >>
class DESEngine; // forward declaration

class UserEvents {
private:
  DESEngine& owner;  // reference
public:
  UserEvents(DESEngine& e) : owner(e) { }
};