如何解决这种循环依赖?

时间:2017-05-02 02:08:43

标签: c++ class pointers circular-dependency

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

但是我根据图片得到了多个错误:Error List(错误在帖子末尾包含在文本中)

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

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  

3 个答案:

答案 0 :(得分:1)

由于这是has aDESEngine之间的UserEvents关系,在其类结构中,您可以稍微重新设计不应失去任何功能的类。设计的这种微小变化应该改善内存管理以及类之间的关系或联系。当一个班级Has a <T>Ownsobject到另一个classstructure所属班级只需要将对象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>

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

示例

<强> 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时,你应该能够减少包含指针或对拥有对象的引用的需要,只需将这两个类相互交友为完全朋友即可类或每个类中的特定方法。通过这个,您可以控制两个类与其他类交互的内部关系。通过精心设计,产品代码的用户不必担心功能,类及其方法和模板的内部实现细节,如果它结构良好且功能符合它们的意思为了便于阅读而做出相应的命名。

现在,如果您希望此对象包含其拥有对象的指针,那么您可能希望不再使用朋友,而是尝试避免其中任何一个接受指针或彼此的对象。另外两个类应该有这样的默认构造函数如果一个依赖于另一个构造它总是可以使用默认构造函数然后你可以使用initialize或update方法将类的内部变量指针更改为另一个类那种方法。在某些情况下或需要,您可以将一个类作为静态类而不是实际对象。然后你需要一个静态get函数将它的*this作为静态指针返回给你,然后其他依赖类也可以使用静态指针,特别是如果你只有1个该类对象的实例。

使用可以轻松搜索的朋友,看看它是如何完成的。至于后面的选项,我可以根据要求给出一个小例子。

由于您的应用程序似乎应该只有DESEngineUserEvents的单个实例,因此除非您正在汇集一堆{,否则不应该有多个这样的实例。 {1}}对象然后你可以从一个基本的Singleton接口派生这两个类。然后确保这两个对象都声明了一个指向自身的静态指针以及该指针的get方法。然后,使用此对象的其他类可以使用该静态指针,或者类似但不是静态单例,您可以使用智能指针甚至两者的组合。这一切都取决于您的特殊需求。

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

我还想补充一下WhozCraig的评论,即您将UserEvents的引用传递给DESEngine构造函数。所以,让我们看看编译器试图从中理解的内容......

您在其他功能的源代码中创建UserEvents的实例,例如;

<强>的main.cpp

DESEngine

所以编译器进入#include "DESEngine.h" int main() { DESEngine engine; // This is okay because you declared it with a default constructor. } 并在定义之前查看该类的声明并且它本身就说好了我需要一个DESEngine.h类让我们转到它的构造函数,Okay此构造需要UserEventsReference个对象,并且来自DESEngine变量声明到当前DESEngine,我还没有完成声明或定义UserEvents对象。因此,它试图根据声明完成定义。

答案 1 :(得分:0)

除了一些小的修正之外 - 你不能在B类中拥有A类的完整对象,而在A类中不能拥有B类的完整对象;产生无限递归。

其中一个(至少)需要是指针或参考。

答案 2 :(得分:0)

简而言之,您不应该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) { }
};