2

背景:我正在开发一个基于现有 Java 类模型生成 C++ 代码的框架。出于这个原因,我无法更改下面提到的循环依赖。

鉴于:

  • 父子类关系
  • Parent 包含一个 Children 列表
  • 用户必须能够在运行时查找列表元素类型

我在以下测试用例中对此进行了建模:

主文件

#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;
}

父.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

孩子.h

#ifndef CHILD_H
#define CHILD_H

#include "Parent.h"

class Child: public Parent
{
};

#endif

数组.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 需要了解 Parent 类
  2. Parent.h 需要了解类 Array
  3. Array.h 需要了解 Child 类

有任何想法吗?

4

5 回答 5

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

在 parent.cpp 中:

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

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

// etc.

更新:

是的,实际问题是由 Array::getType() 在没有 Child 定义的情况下实例化引起的,所以我的解决方案是不完整的。

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>;
于 2010-07-05T05:05:59.100 回答
4

该错误是由于在实例化模板时不存在 Child 类。

将以下内容添加到 Main 或 Parent.h 的末尾

#include "Child.h"

这适用于 g++ 4 和 VS 2010。

于 2010-07-05T08:38:47.340 回答
1

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

更改Array<Child>Array<Child*>更改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>元素并询问每个元素的类型?

于 2010-07-05T05:07:21.110 回答
0

也许您可以在 Parent 中使用指向 Child 的指针?就像是

#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)。更多关于它的信息在这里

于 2010-07-05T05:06:46.540 回答
0

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


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

如果您想要对象的运行时类型,请使用具有返回类型的虚函数的基类。对于场所的静态类型,您可以使用部分特化:

对象.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

父.h

#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

数组.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;
};

孩子.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

主文件

#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;
}
于 2010-07-05T08:24:52.397 回答