我该如何解决这种循环依赖?
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 的完整对象;产生无限递归。
其中之一(至少)需要是指针或引用。
由于这是 DESEngine
和 UserEvents
之间的 has a
关系,在其 class 结构中,您可以稍微重新设计您的 classes不会失去任何功能。这种设计上的细微变化应该会改善内存管理以及 class 之间的关系或联系。当一个 class Has a <T>
或 Owns
一个 object
到另一个 class
或 structure
时,拥有 class 只需要包含 objects header
在它自己的 header
中,不需要 class prototype declaration
。现在属于另一个的 class 不应该在它自己的 header 中包含拥有 class 的 header
但确实需要 class prototype declaration and needs to include the owning class's
header 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 个实例
为了使用可以很容易地用谷歌搜索来查看它是如何完成的朋友。至于后一个选项,如果需要,我可以给出一个小例子。
因为您的应用程序似乎只应该有一个 DESEngine
和 UserEvents
的实例,因为不需要有多个实例,除非您正在汇集一堆的 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) { }
};
我有两个 类: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 的完整对象;产生无限递归。
其中之一(至少)需要是指针或引用。
由于这是 DESEngine
和 UserEvents
之间的 has a
关系,在其 class 结构中,您可以稍微重新设计您的 classes不会失去任何功能。这种设计上的细微变化应该会改善内存管理以及 class 之间的关系或联系。当一个 class Has a <T>
或 Owns
一个 object
到另一个 class
或 structure
时,拥有 class 只需要包含 objects header
在它自己的 header
中,不需要 class prototype declaration
。现在属于另一个的 class 不应该在它自己的 header 中包含拥有 class 的 header
但确实需要 class prototype declaration and needs to include the owning class's
header 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.
为了使用可以很容易地用谷歌搜索来查看它是如何完成的朋友。至于后一个选项,如果需要,我可以给出一个小例子。
因为您的应用程序似乎只应该有一个 DESEngine
和 UserEvents
的实例,因为不需要有多个实例,除非您正在汇集一堆的 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) { }
};