如何解决此循环依赖关系

How can I solve this circular dependency?

本文关键字:循环 依赖 关系 解决 何解决      更新时间:2023-10-16

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

但是根据图像,我收到多个错误: 错误列表(错误包含在帖子末尾的文本形式)

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

德森吉内·

#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  

由于这是DESEngineUserEvents类结构之间的has a关系,因此您可以稍微重新设计不应丢失任何功能的类。这种微小的设计变化应该会改善内存管理以及类之间的关系或纽带。当一个类Has a <T>Owns另一个classstructureobject时,拥有类只需要包含header的对象在其自己的header中,而不需要class prototype declaration。现在,属于另一个类的类不应在其自己的标头中包含拥有类的header,但确实需要class prototype declaration and needs to include the owning class's标头in its实现文件。

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

下面是一组类的示例...

所有者.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 

所有者.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

某对象.cpp

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

现在,当使用另一个拥有的SomeObject时,您应该能够减轻包含指针或对拥有对象的引用的需求,只需使这两个类彼此成为完全友元类或每个类中的特定方法即可。有了这个,您可以控制两个类如何与其他类交互的内部关系。通过精心设计,产品代码的用户不必担心函数、类及其方法和模板的内部实现细节,如果它结构良好,并且函数执行它们应该做的事情,并且相应地命名以提高可读性。

现在,如果您希望此对象包含其所属对象的指针,那么您可能希望放弃使用朋友,但也要尝试避免它们中的任何一个接受指针或彼此的对象。换句话说,如果一个类依赖于另一个类进行构造,则两个类都应该具有默认构造函数,它始终可以首先使用默认构造函数,然后您可以使用初始化或更新方法通过该方法将类的内部变量指针更改为另一个类。在某些情况下或需要,你可以让一个类是静态类,而不是实际对象。然后你需要一个静态 get 函数将其*this作为静态指针返回给你,然后其他依赖类也可以使用静态指针,特别是如果你只有该类对象的 1 个实例。

对于使用朋友,可以很容易地用谷歌搜索它是如何完成的。至于后面的选项,如果需要,我可以给出一个小插图。

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

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

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

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

主.cpp

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

因此,编译器进入DESEngine.h并在定义之前查看此类的声明,它对自己说好吧,我需要一个UserEvents类,让我们转到它的构造函数,好的,这个构造需要对DESEngine对象的Reference,并且从DESEngine变量声明到当前UserEvents,我还没有完成声明或定义DESEngine对象。因此,它绕了一圈,试图根据声明完成定义。

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

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

简而言之,您不应该将DESEngine作为UserEvents的成员。 这也是不合理的,因为您希望引用其所有者而不是副本

所以它应该看起来像:

<< 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) { }
};