2

我想要一个容器(比如说一个 std::vector),它可以容纳各种继承的类型,并实例化它们,即类向量 --> 对象向量。

例如:

class A{};

class B: public class A
{};

class C: public class A
{};

void main()
{
    std::vector<of inherited A types> typesVec;
    std::vector<A*> objectsVec;

    typesVec.push_back(class B);
    typesVec.push_back(class C);

    for (int i = 0; i < typesVec.size(); i++)
    {
        A* pA = new typesVec.at(i);
        objectsVec.push_back(pA);
    }

}

提前致谢..

4

4 回答 4

6

这在 C++ 中是不可能的(至少不是直接的)。我可以看到这种情况发生在具有反射的语言中,但 C++ 没有。

您可以做的是创建一个工厂或简单的方法来创建指定类型的对象。

您将拥有一个对象生成器向量,而不是一个类型向量(足够接近,对吗?):

class A{};

class B: public class A
{};

class C: public class A
{};

struct AFactory
{
    virtual A* create() { return new A; }
};
struct BFactory : AFactory
{

    virtual A* create() { return new B; }
};
struct CFactory : AFactory
{

    virtual A* create() { return new C; }
};

//...

typesVec.push_back(new BFactory);
typesVec.push_back(new CFactory);

for (int i = 0; i < typesVec.size(); i++)
{
    A* pA = typesVec.at(i)->create();
    objectsVec.push_back(pA);
}
于 2012-10-10T11:42:10.600 回答
1

模板有一种可重用的方法。这是派生类型的通用工厂,带有一个install和一个create方法,可以让您编写如下代码:

int main() {
    TypeVector<Base> t;
    t.install<Foo>("Foo");
    t.install<Bar>("Bar");

    t.create("Foo")->hello();
}

请注意,这是一个草图实现。在现实世界中,您可以提供另一个模板参数来指定底层容器类型(对于少数类型,vector可能比 更有效set)。

类型向量是这样的:

    template <typename Base>
    class Creator;

    template <typename Base>
    class TypeVector {
    public:
        template <typename Derived>
        void install (std::string const &name) ;

        std::shared_ptr<Base> create (std::string const &name) const;

    private:
        struct Meta {
            Meta(std::shared_ptr<Creator<Base>> creator, std::string const &name)
                : creator(creator), name(name) {}

            std::shared_ptr<Creator<Base>> creator;
            std::string name;
        };

        std::vector<Meta> creators_;
    };

我们不知何故需要一种以可分配方式存储类型的方法。我们这样做boost::shared_ptr,它结合了一个抽象基类和一个模板派生类:

template <typename Base>
class Creator {
public:
    virtual ~Creator() {}        
    virtual std::shared_ptr<Base> create() const = 0;
};

template <typename Base, typename Derived>
class ConcreteCreator : public Creator<Base> {
public:
    virtual std::shared_ptr<Base> create() const {
        return std::shared_ptr<Base>{new Derived()};
    }
};

“具体的创建者”能够分配一个实际的对象,并返回一个指向它的指针。

最后,这里是TypeVector::installand的实现TypeVector::create

template <typename Base>
template <typename Derived>
void
TypeVector<Base>::install (std::string const &name)
{
    creators_.emplace_back(
        std::shared_ptr<Creator<Base>>(new ConcreteCreator<Base, Derived>()),
        name);
}

template <typename Base>
std::shared_ptr<Base> 
TypeVector<Base>::create (std::string const &name) const
{
    for (auto m : creators_) {
        if (name == m.name) return m.creator->create();
    }
    throw std::runtime_error("...");
}

最后,这是一个测试:

#include <iostream>
struct Base {
    virtual ~Base() {}
    virtual void hello() const = 0;
};
struct Foo : Base {
    virtual void hello() const { std::cout << "I am a Foo\n"; }
};
struct Bar : Base {
    virtual void hello() const { std::cout << "I am a Bar\n"; }
};

int main() {
    TypeVector<Base> t;
    t.install<Foo>("Foo");
    t.install<Bar>("Bar");

    t.create("Foo")->hello();
}

你可以走得更远,使任何构造函数都可用于代码,例如...

...
    Bar(Color, Age, int)
...
t.create("Foo", Color::Red, Age::TooOld, 42)

... but this requires an awesome grasp of variadic template argument lists, and how to fold them into a constructor call (can be done and has been done, but it would explode this answer).

于 2012-10-10T12:25:54.470 回答
0

只是一个快速的解决方案草图:

C++ 标准不提供对构造函数的直接调用。因此,您不能拥有指向构造函数的函数指针;但是,您可以使用包装函数“create”,例如:

template<typename T>
T* create () {
   return (new T();
}

为一个参数、两个参数……提供重载的创建定义,或者尝试使用可变参数模板;或者,如果您已经知道需要什么类型,则可以专门创建创建函数。然后你可以有一个指向创建函数的函数指针:

&create<TheType>

但请注意,此函数的签名取决于所使用的类型。但是,您可以创建一个结构,其中包含模板类型的 typdef、类型指针的 typedef 以及作为 functor 的 create 函数operator()

因此,您可以有两个向量,一个用于指向创建函数的函数指针,或者替代前面提到的结构,另一个用于实际对象。在您只有继承类型的情况下,您可以为每个继承的类型 B、C、... 定义函数A* createB() { return new B(); }, A* createC() { return new C(); }, ... 并为指向这些创建函数的指针提供一个向量,并为 A 定义第二个向量指针。

于 2012-10-10T11:54:45.187 回答
0

我可能会指出 Andrei Alesandrescu 的书 Modern C++ Design(或他在书中描述的 Loki 库)和关于类型列表的章节。这将要求您typeVec.insert( type )在编译时执行。

于 2012-10-10T12:10:11.957 回答