C ++:多态容器,可以一次添加多个对象

时间:2013-01-09 12:54:49

标签: c++ polymorphism containers

尝试重新考虑一些基础设施代码我决定使用继承来实现多个类之间的多态行为。下一步是将它们放入容器中,迭代它们和DoSomething();

我目前对容器的构造很笨拙。即使我将这个elegant解决方案用于容器,构造仍然是一团糟。

vector<unique_ptr<Parent>> vec;

vec.push_back(unique_ptr<Parent>(new ChildA(var1, var2)));
vec.push_back(unique_ptr<Parent>(new ChildB(var3, var4)));
vec.push_back(unique_ptr<Parent>(new ChildC(var5, var6)));

ChildA,ChildB和ChildC彼此都是多态的。 Var1 ... var6可以来自不同类型,并且是构造子对象所必需的。

我想在几个地方创建这个矢量以供使用。问题是儿童的数量可能会有所不同,因此孩子们需要建筑的数据。试图走stdarg.h的路径失败,因为不支持:

if     !defined(_WIN32)
#error ERROR: Only Win32 target supported!
#endif

您如何实现构造此数组的工厂方法?

更新,期望的解决方案:

vec = CreatePolyVec(var1, var2, typeInfo1, var3, var4, typeInfo2, var5, var6 typeInfo3);
vec = CreatePolyVec(var1, var2, typeInfo1, var3, var4, typeInfo2);

第1行将创建与上面相同的数组。 第2行将重用相同的代码来创建一个类似的向量,但只有一个对象。 Typeinfo保存创建对象所需的信息。

这是一个基本的解决方案,更高级的解决方案将在编译时强制执行参数列表。例如,以下对该函数的调用没有意义:

vec = CreatePolyVec(var1, var2,typeinfo1, var3, var4, typeinfo2, var5);

没有足够的参数来创建最后一个子对象。

2 个答案:

答案 0 :(得分:4)

这里有两个问题,1。如何确定每个孩子的类型,以及2.如何创建多个孩子。

决定创建哪个孩子

这可以在编译时或运行时完成。要在编译时执行此操作,您需要模板。

template<class Child, class Arg1, class Arg2>
vector<unique_ptr<Parent>> CreateVec(Arg1&& arg1, Arg2&& arg2)
{
    vector<unique_ptr<Parent>> result;
    result.push_back(unique_ptr<Child>(
        new Child(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2))));
    return result;
}

调用如下CreateVec<MyChild>(myArg1, myArg2)

如果需要在运行时决定,可以使用由运行时变量索引的工厂函数映射。或者您可以使用指向工厂对象的指针作为运行时变量。

创建多个孩子

您可以在链接函数和可变参数模板之间进行选择。

链式功能

这基本上就是iostreams所做的。让你的函数创建一个向量并添加一个子对象,返回一个允许你添加第二个子对象的对象,并返回它自己让你继续。

这里的问题是你希望函数返回向量,因此它也不能返回另一个对象。您可以使用转换运算符来获取向量或显式函数,但最简单的方法是首先创建向量,然后使用函数添加子对象。

class AddChildren
{
    vector<unique_ptr<Parent>>& m_vector;
public:
    explicit AddChildren(vector<unique_ptr<Parent>>& theVector)
        : m_vector(theVector) {}

    template<class Child, class Arg1, class Arg2>
    AddChildren& add(Arg1&& arg1, Arg2&& arg2)
    {
        m_vector.push_back(unique_ptr<Child>(
            new Child(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2))));
        return *this;
    }
};

使用如下:

vector<unique_ptr<Parent>> myvector;
AddChildren(myvector)
    .add<ChildA>(var1, var2)
    .add<ChildB>(var3, var4)
    .add<ChildC>(var5, var6);

如果您使用的是运行时选择类型的方法,可以使用operator()并使其如下所示:

vector<unique_ptr<Parent>> myvector;
AddChildren(myvector)
    (childAType, var1, var2)(childBType, var3, var4)(childCType, var5, var6);

(对于每种子类型使用特定类型 - 选择器类型的虚拟对象作为参数,也可以通过编译时选择类型来完成。)

使用可变参数模板

使用可变参数模板一次剥离三个参数,并添加子对象。

void addChildren(vector<unique_ptr<Parent>>& theVector)
{}

template<class FirstChild, class FirstArg1, class FirstArg2, class... Rest>
void addChildren(vector<unique_ptr<Parent>>& theVector,
    FirstChild childtype, FirstArg1&& arg1, FirstArg2&& arg2, Rest&&... rest)
{
    addChild(theVector, childtype,
        std::forward<Arg1>(arg1), std::forward<Arg2>(arg2));
    addChildren(theVector, std::forward<Rest>(rest)...);
}

template<class... Args>
vector<unique_ptr<Parent>> CreateVec(Args&&... args)
{
    vector<unique_ptr<Parent>> result;
    addChildren(result, std::forward<Args>(args)...);
    return result;

}

我假设存在一个函数addChild,它可以根据类型(作为参数)及其参数添加一个子函数。

这个问题的主要问题是VS2012没有可变参数模板。有两种方法可以模拟可变参数模板。 1.编写一个函数,它接受您可能需要的最大参数数量,并将其中大多数默认为某种已知类型,您可以将其表示为“不存在”。 2.写出你认为需要的多次重载。

如果你知道你永远不会需要十个以上的子对象,那么第二个选项实际上是完全可行的 - 你只需要编写一次,它可能少于150行代码。或者,您可以使用Boost.Preprocessor生成函数,但这是一个全新的复杂程度。

答案 1 :(得分:2)

随着2012年11月的编译器更新,VS 2012支持可变参数模板,因此以下内容应该可行(它不是您所使用的语法,但我认为它非常接近):

struct VectorCreator
{
  explicit VectorCreator(std::vector<Parent> &vec)
    : vec_(vec)
  {}

  template <typename Type, typename... ArgType>
  VectorCreator& create(ArgType&&... arg)
  {
    vec_.push_back(std::unique_ptr<Parent>(new Type(std::forward<ArgType>(arg)...));
    return *this;
  }

private:
  std::vector<Parent> &vec_;
};

像这样使用:

std::vector<Parent> vec;
VectorCreator(vec).create<ChildA>(var1, var2).create<ChildB>(var3, var4).create<ChildC>(var5, var6);