2010-07-05 37 views
2

背景:我正在致力于生成基于现有Java类模型的C++代码的framework。出于这个原因,我不能改变下面提到的循环依赖。模板,循环依赖,方法,哦,我的!

考虑:

  • 父子类关系
  • 家长包含儿童的名单
  • 用户必须能够查找列表元素类型在运行时

我已经在下面的测试用例中对此进行了建模:

Main.cpp

#include "Parent.h" 

#include <iostream> 
using std::cout; 
using std::endl; 

int main(int argc, char* argv[]) 
{ 
    Parent parent; 
    cout << Parent::getType() << endl; 
    cout << parent.getChildren().getType() << endl; 
    return 0; 
} 

Parent.h

#ifndef PARENT_H 
#define PARENT_H 

#include <string> 

#include "Array.h" 
class Child; 

class Parent 
{ 
public: 
    Array<Child> getChildren() 
    { 
     return Array<Child>(); 
    } 

    static std::string getType() 
    { 
     return "parent"; 
    } 
}; 

#endif 

Child.h

#ifndef CHILD_H 
#define CHILD_H 

#include "Parent.h" 

class Child: public Parent 
{ 
}; 

#endif 

Array.h

template <typename ElementType> 
class Array 
{ 
public: 
    static std::string getType() 
    { 
     return ElementType::getType(); 
    } 
}; 
  1. 当我编译上面的代码我得到: error C2027: use of undefined type 'Child'return ElementType::getType();

  2. 如果我尝试#include "Child.h",而不是向前声明我得到: error C2504: 'Parent' : base class undefinedclass Child: public Parent

  3. 如果我尝试的Array<Child*>代替Array<Child>我得到: error C2825: 'ElementType': must be a class or namespace when followed by '::'return ElementType::getType();

循环依赖的出现是因为:

  1. Child.h需要了解类家长
  2. Parent.h需要了解类Array
  3. Array.h需要了解类儿童

任何想法?

+4

它知道它的派生类的家长对我来说似乎是一个设计缺陷。 – Shirik 2010-07-05 04:36:47

+0

够公平的,但正如我所解释的,我无能为力去改变这一点。我正在生成基于野外设计的代码。 – Gili 2010-07-05 04:42:05

+1

代码'ElementType child;'在哪里? – rlbond 2010-07-05 04:49:33

回答

4

的错误是由于不存在当儿童类该模板被实例化。

添加以下任一主或在端Parent.h的

#include "Child.h" 

编译没有既克++ 4和VS 2010

+0

正如问题所示,您将收到错误C2504。 – Gili 2010-07-05 13:10:45

+1

@Gili这个问题讨论了在parent的定义之前包含child.h。包括它在*后的定义解决问题。 – 2010-07-05 13:43:59

+0

@Pete,我的错。你是对的! – Gili 2010-07-05 23:27:08

4

解决此问题的一种方法是将实现与接口分开。

因此,将Parent的实现放到.cpp文件中,以便编译器在编译Parent :: getChildren()时可以看到Parent,Child和Array的定义。

#ifndef PARENT_H 
#define PARENT_H 
#include <string> 
#include "Array.h" 

class Child; 

class Parent 
{ 
public: 
    Array<Child> getChildren(); 
    static std::string getType(); 
}; 

#endif 

并在父母。CPP:

#include "parent.hpp" 
#include "child.hpp" 

Array<Child> Parent::getChildren() { 
    return Array<Child>(); 
} 

// etc. 

更新:

是,实际的问题是由阵列引起::的getType()儿童不存在的定义被实例化,所以我的解决方案是不完整的。

Pete Kirkham的解决方案很好:只需将child.hpp包含在main中即可。

要使接口/实现分离工作,需要一个单独的Array实现文件,并且需要显式实例化Array和任何其他必需的实例。这可能不是你想要的东西,但对于完整性,它看起来是这样的:

在array.hpp:

#ifndef ARRAY_HPP 
#define ARRAY_HPP 

#include <string> 

template <typename ElementType> 
class Array 
{ 
public: 
    static std::string getType(); 
}; 

#endif 

而且在array.cpp:

#include "array.hpp" 
#include "child.hpp" 

template<typename ElementType> 
std::string Array<ElementType>::getType() 
{ 
    return ElementType::getType(); 
} 

template class Array<Child>; 
+0

+1从接口单独实施。 – 5ound 2010-07-05 05:31:51

+0

@janm,我不认为你的解决方案适用于这个问题。编译器抱怨“返回的ElementType ::的getType()”而不是“家长::的getChildren()”和前不能进入一段cpp的文件,因为它使用的模板。 – Gili 2010-07-05 13:17:09

+0

@Gili:不,这是正确的方法。该错误信息是由'返回的ElementType ::的getType()'“近端”造成的,但根本原因是'父::的getChildren()' - *阵','的那*是什么原因造成这些实例失败因为我们还没有看到“孩子”的定义。你必须等到'Child'已经被实例化'阵列之前定义的',这意味着父母的'定义::的getChildren()'*必须*是不在线。 – 2010-07-05 23:34:51

0

也许你可以使用父指针而不是父指针? 喜欢的东西

#ifndef PARENT_H 
#define PARENT_H 

#include <string> 

#include "Array.h" 
class Child; 

class Parent 
{ 
public: 
    Array<Child*> getChildren() 
    { 
     return Array<Child*>(); 
    } 

    static std::string getType() 
    { 
     return "parent"; 
    } 
}; 

#endif 

,或者更一般地说,也许它可以使用编译器的防火墙技术(又名不透明的指针,又名PIMLP)。更多关于它的信息here

+0

使用指向Child的指针不起作用。正如问题所解释的那样,我得到错误C2825。我认为这意味着更一般的PIMPL技术不会有帮助吗? – Gili 2010-07-05 05:27:30

+0

如果在j_random_hacker讯息中提到你会改变Array类的定义,它应该工作,即错误C2825应消失。 – Haspemulator 2010-07-05 05:48:43

1

编辑: OP编辑了这个问题,以消除我和rlbond注意到的无限大小数据结构问题。通过此更改,现在可以使用Array<Child>而不是Array<Child*>,如janm's answer所示。

变化Array<Child>Array<Child*>改变Array类型,了解它包含对象的指针,而不是对象本身:

新Array.h

// E.g. strip_pointer_from<Foo*>::type is Foo 
template <typename T> 
struct strip_pointer_from<T> {}; 

template <typename T> 
struct strip_pointer_from<T*> { 
    typedef T type; 
}; 

template <typename ElementType> 
class Array 
{ 
public: 
    static std::string getType() 
    { 
     return typename strip_pointer_from<ElementType>::type::getType(); 
    } 
}; 

我强烈建议重新考虑Array,但是 - 有没有什么方法可以使用普通的vector<Child>并且只是询问每个元素的类型?

+1

您在不正确编 - 对给出的代码,没有T存储在阵列可言,所以没有递归存储。如果充实阵列以与std :: vector 类似的方式运行,则Parent包含零个或多个初始为零的子对象,并存储用于根据需要动态分配的其他子对象。 struct Node {std :: vector children; }是完全合法的,如果倾向于重新分配的效果。 – 2010-07-05 07:47:14

+0

@Pete:你是对的 - 在我写这篇文章后,OP改变了我在附录中提到的错误信息。现在它的尺寸是有限的,“Array ”在janm的答案中表现得很好。 – 2010-07-05 23:41:48

0

以下是我倾向于解决需要元类信息的系统中的问题。另请参阅其他答案,以更直接地解决您的问题。


stl中使用的模式是使用类型来表示类型,而不是字符串。所以std::vector<T>::value_type代表存储在向量中的类型。然后由客户端代码来使用这种类型。

如果您想要对象的运行时类型,请使用具有返回类型的虚函数的基类。对于静态类型的地方,你可以使用偏特:

Object.h

#ifndef OBJECT_H 
#define OBJECT_H 

#include <string> 

template <typename T> 
struct type_info { 
    // extend type_info<void*> to inherit defaults 
    const static bool is_array = false; 
}; 

template <typename T> 
std::string type_name (const T&) 
{ 
    return type_info<T>::name(); 
}; 

template <typename T> 
std::string type_name() 
{ 
    return type_info<T>::name(); 
}; 

#endif 

家长。ħ

#include "Object.h" 
#include "Array.h" 

class Child; 
class Parent 
{ 
public: 
    Array<Child> getChildren() { 
     return Array<Child>(); 
    } 
}; 

template <> 
struct type_info <Parent> : public type_info<void*> { 
    static std::string name() { 
     return "parent"; 
    } 
}; 


#endif 

Array.h

template <typename ElementType> 
class Array 
{ 
public: 
    typedef ElementType value_type; 
}; 

template <typename T> 
struct type_info <Array<T > > { 
    static std::string name() { 
     return "Array<" + type_name<T>() + ">"; 
    } 

    const static bool is_array = true; 
}; 

Child.h

#ifndef CHILD_H 
#define CHILD_H 

#include "Parent.h" 

class Child: public Parent 
{ 
}; 

template <> 
struct type_info <Child> : public type_info<void*> { 
    static std::string name() { 
     return "child"; 
    } 
}; 

#endif 

Main.cpp的

#include "Object.h" 
#include "Parent.h" 
#include "Child.h" 

#include <iostream> 
#include <iomanip> 

using std::cout; 
using std::endl; 
using std::boolalpha; 

template<typename T> bool type_is_array (const T&) { return type_info<T>::is_array; } 

int main(int argc, char* argv[]) 
{ 
    Parent parent; 
    cout << type_name<Parent>() << endl; 
    cout << type_name(parent.getChildren()) << endl; 
    cout << boolalpha << type_is_array(parent) << endl; 
    cout << boolalpha << type_is_array(parent.getChildren()) << endl; 
    return 0; 
}