【问题标题】:C++ cannot establish handle reference to 'parent' object -- circular includes or undefined type errorsC++ 无法建立对“父”对象的句柄引用——循环包含或未定义的类型错误
【发布时间】:2012-08-07 00:39:25
【问题描述】:

我希望有一个层次结构的类结构,其中一个级别的控制器“父”类负责创建/指导许多“子”类。父类应该能够直接引用它创建的每个子类,并且每个子类应该能够直接引用它的父类(并且,假设这个子类不是更多类的父类,只是它的父类)。这允许通过父级引用兄弟姐妹。我发现这种范例在 Java 和 C# 等 JIT 编译语言中很有用,但 C++ 存在一个独特的问题......

我第一次尝试实现这个范例如下:

父类 TreeRoot.h

#ifndef __CCPP_SCENE_H__
#define __CCPP_SCENE_H__
#include "ChildA.h"
#include "ChildB.h"

class TreeRoot : 
{
private:


ChildA* a;
ChildB* b;

public:

//member getters
ChildA* getA();
ChildB* getB();
};

#endif // __CCPP_SCENE_H__

子类 ChildA.h

#ifndef CHILDA_H_
#define CHILDA_H_


#include "TreeRoot.h"


class ChildA
{

private:
TreeRoot* rootScene;

public:
ChildA(TreeRoot*);
~ChildA(void);

TreeRoot* getRootScene();
void setRootScene(TreeRoot*);

};

#endif /*CHILDA_H_*/

子类 ChildB.h

#ifndef CHILDB_H_
#define CHILDB_H_


#include "TreeRoot.h"


class ChildB
{

private:
TreeRoot* rootScene;

public:
ChildB(TreeRoot*);
~ChildB(void);

TreeRoot* getRootScene();
void setRootScene(TreeRoot*);

};

#endif /*CHILDB_H_*/

当然,由于循环包含(TreeRoot.h 包括 ChildA.h 和 ChildB.h,它们都包括 TreeRoot.h 等),所以现在当然不会编译,所以我尝试使用前向声明:

父类 TreeRoot.h

#ifndef __CCPP_SCENE_H__
#define __CCPP_SCENE_H__
#include "ChildA.h"
#include "ChildB.h"

class TreeRoot : 
{
private:


ChildA* a;
ChildB* b;

public:

//member getters
ChildA* getA();
ChildB* getB();
};

#endif // __CCPP_SCENE_H__

子类 ChildA.h

#ifndef CHILDA_H_
#define CHILDA_H_


//#include "TreeRoot.h" //can't use; circular include!
class TreeRoot;

class ChildA
{

private:
TreeRoot* rootScene;

public:
ChildA(TreeRoot*);
~ChildA(void);

TreeRoot* getRootScene();
void setRootScene(TreeRoot*);

};

#endif /*CHILDA_H_*/

子类 ChildB.h

#ifndef CHILDB_H_
#define CHILDB_H_


//#include "TreeRoot.h" //can't use; circular include!
class TreeRoot;



class ChildB
{

private:
TreeRoot* rootScene;

public:
ChildB(TreeRoot*);
~ChildB(void);

TreeRoot* getRootScene();
void setRootScene(TreeRoot*);

};

#endif /*CHILDB_H_*/

该实现几乎可以工作,因为我可以成功地将消息广播到子对象并执行从子对象到父类的回调,如下所示:

TreeRoot.cpp

...
a->someChildMethod();
a->getRootScene()->someParentMethod();

但是当我尝试以下操作时:

ChildA.cpp

...
rootScene->someParentMethod(); //ERROR C2027: use of undefined type TreeRoot

我得到一个未定义的类型错误。这是有道理的,因为使用上面的前向声明并不会告知编译器 TreeRoot 的实际含义。那么问题是如何启用来自子对象的调用,例如上面的 rootScene->someParentMethod() 调用?也许通过模板使用泛型类型会使编译器满意并提供我正在寻找的功能?

谢谢, CCJ

【问题讨论】:

  • 恕我直言,但这种“范式”不是推荐的方法。您正在依赖图中创建循环,这是不可以的。您的子对象现在不需要关于父对象。如果需要回调,我建议添加一个独立于父子的接口,由父亲实现,然后由孩子使用。
  • @Alex:正如双向链表有其用途一样,子节点知道其父节点的树也是如此。
  • @Alex 你会建议什么替代设计范式来提供等效的功能(例如,假设子节点确实需要知道它们的父节点)?您将如何实现您建议的接口?
  • 这可能是一个很好的例外情况,但是: 1) 分层/非循环设计有一个明确的构造/破坏顺序,这避免了可怕的空指针错误。 2)在我看来,最糟糕的问题是控制问题。在层次结构中,当您构建类时,您依赖于您拥有设计控制权的类,即在层次结构或抽象中位于您之下的类。如果您破坏了这一点并依赖于上层类,那么这些类中的更改将破坏您的代码。见:www10.informatik.uni-erlangen.de/Teaching/Courses/SS2012/CPP/…
  • 从幻灯片#5 开始,我假设写这篇文章的人从未听说过“模拟”的概念及其在单元测试中的用途。顺便说一句,使用 all-nodes-use-one-(super)class 方法会使讨论的问题变得毫无意义,因为不会有那些循环的类依赖关系。

标签: c++ c compiler-construction


【解决方案1】:

在所有.h 文件中使用前向声明。您可以这样做,因为您只将指针存储为类成员,因此您不需要完整的类声明。

然后,在您所有对应的.cpp 文件中,#include 为您需要的类的头文件。

所以,在TreeRoot.h 中,您转发声明ChildAChildB。在TreeRoot.cpp,你#includeChildA.hChildB.h

冲洗并重复其他 2 个课程。

请注意,这将解决您当前的问题,但这种设计充其量似乎很不稳定。

【讨论】:

  • 这确实可以解决问题,但是必须在每个子类的实现方法中包含兄弟节点的标头,这违背了这一点……您会建议什么替代范式?
  • @CCJ 我建议只制作一个类(节点),并让这个类包含一个指向其父节点的指针和一个指向子节点的指针向量。这通常是树的表示方式。这一切都归结为这样一个事实,即树中的每个节点都可以被视为树根本身。
  • 这听起来像是一个可靠的计划。如何将父指针和子指针向量声明为泛型类型?我认为让 Node 成为每个树结构对象都可以继承的类可能会很有用,并且会有类似这样的成员(尽管这显然不是正确的语法)... template * parent_ptr;向量
  • @CCJ 单一类的内在优势在于,即使您有节点子类,它们也可以被视为节点,仅此而已。我会按照以下方式声明我的课程:pastebin.com/TCuXDpLs。您不希望您的节点知道其父或子的具体类型,这就是 OO 设计和多态性的优势。
【解决方案2】:

您可以尝试在 ChildA 和 ChildB 文件中包含“TreeRoot.h”。我还建议使用多态性并创建一个父类,A 和 B 从该类继承,以实现共享行为。

【讨论】:

    【解决方案3】:

    这不涉及摆弄头文件,但我的建议:要么让所有节点成为同一个类(实现更大的灵活性[如果你决定要将一棵树变成另一棵树的子树怎么办?你' d 必须将根节点的类从第一棵树更改为子类],并且至少在我看来,这更有意义/看起来更优雅/会减少和/或简化代码的数量必须编写),或者对父节点和子节点类都使用超类(正如 ATaylor 建议的那样),尽管我认为只有当您的父节点和子节点具有很多不同的功能时,这才是更好的解决方案形成树状结构。

    【讨论】:

      【解决方案4】:

      在 ChildA.cpp 文件中,您必须将父标题包含为

      #include "TreeRoot.h"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-02-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多