如何在C++中创建一个类数组?

7

代码:

struct Base { ... };

struct A : public Base { ... };
struct B : public Base { ... };
struct C : public Base { ... };

是否可以创建一个包含结构体类型的数组?

Type inheritedTypesOfStruct[3] = {A, B, C};

这样做的目的是为了稍后从数组中取出一个随机类并创建一个对象。

3
你需要一个类型本身的数组吗?还是需要一个包含不同类型对象的数组? - Benjamin Lindley
1
@BenjaminLindley:显然他想要一个类型数组。然而,将Base用作数组项类型是具有误导性的。 - Niklas B.
1
一个类型数组。我需要这个,因为将来我会创建随机类型的 obj/struct。 - justi
2
@NiklasBaumstark:对我来说并不明显。这里很多人不能有效地传达他们的意图,要么是因为他们不熟练编程术语,要么是因为他们不熟练英语。 - Benjamin Lindley
@BenjaminLindley:是的,你说得有些道理。 - Niklas B.
显示剩余8条评论
5个回答

6
您可以创建一个函数数组,每个函数返回一个指向不同派生类对象的基指针(或智能指针)。例如:

``` std::function createFuncs[] = {&Derived1::Create, &Derived2::Create, &Derived3::Create};```

typedef std::unique_ptr<Base> base_ptr;

template<typename Derived>
base_ptr CreateObject()
{
    return base_ptr(new Derived);
}

int main()
{
    std::function<base_ptr(void)> f[3] = {
        CreateObject<A>, CreateObject<B>, CreateObject<C>
    };

    base_ptr arr[10];
    for (int i=0; i<10; ++i)
        arr[i] = f[rand()%3]();
}

这是它的实际效果:http://ideone.com/dg4uq

我尝试了您的示例,但在我的编译器中无法工作:unique_ptr存在一些问题。 - justi
1
@justi:尝试包含头文件<functional>。我本应该这样做的,只是其中一个其他头文件显然将其引入了。如果这不起作用,你使用的编译器(及版本)是什么?这是C++11的特性,所以你的编译器可能不支持它,或者你可能需要设置一些标志。例如,对于g ++,您需要-std=c++0x。如果您无法使用c ++ 11,请不要担心,还有其他解决方案,但让我们先尝试这个。 - Benjamin Lindley
1
我使用默认的DevCpp环境。我添加了头文件,但没有结果。尝试将编译器参数改为-std=c++0x,但也没有任何积极的结果。 - justi
我不确定DevCpp使用哪个编译器,而且很不幸,我现在没有时间去调查它。尝试获取一个现代编译器。我确定Visual C++ 2010和MinGW都可以直接支持上面的代码。尝试获取其中之一。稍后我会回来检查。 - Benjamin Lindley
1
很不幸,使用DevCpp是从上面强制规定的。谢谢你的回复。 - justi
@justi:在这种情况下,您可以使用boost中提供的类似设施,在此处 - 或者您可以使用函数指针。示例在此处。函数指针不像std::functionboost::function那样灵活,但它们应该适用于您的目的。还要注意,我将独特指针替换为常规指针,以防您无法访问它们。您的编译器没有抱怨吗? - Benjamin Lindley

5
如果您的编译器支持RTTI,您可以这样做:
const type_info *inheritedTypesOfStruct[3] = {
    &typeid(A), &typeid(B), &typeid(C)
};

然而,你不能仅使用type_info来实例化一个类。工厂模式可能是解决根本问题的更好答案。
更新:由于type_info实例不能被复制(它们的复制构造函数和赋值运算符是private),并且引用数组是非法的,因此在上面的示例中必须使用常量指针。

1
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <map>
#include <vector>
#include <memory>

using namespace std;




// interface
class Base
{
public:
    virtual ~Base() { }
    virtual int getClassId() = 0;
};


// class A relizes interface Base, has ID == 1 (is used in automatic registration to factory)
class A : public Base
{
public:
    const static int ID = 1;
    static Base* CreateInstance()
    {
        return new A();
    }

    virtual int getClassId()
    {
        return ID;
    }

    virtual ~A() { }
};


// class B relizes interface Base, has ID == 2 (is used in automatic registration to factory)
class B : public Base
{
public:
    const static int ID = 2;
    static Base* CreateInstance()
    {
        return new B();
    }

    virtual int getClassId()
    {
        return ID;
    }

    virtual ~B() { }
};



// this is the objects factory, with registration only (unregister s not allowed)
class ObjectFactory
{
    ObjectFactory() { }
    ObjectFactory(ObjectFactory&) { }
public:
    virtual ~ObjectFactory() { }
    static ObjectFactory& instance()
    {
        static ObjectFactory objectFactory;

        return objectFactory;
    }

    typedef Base* (*Creator) ();

    void registerCreator(int id, Creator creator)
    {
        registry[id] = creator;
    }

    Base* CreateById(int id)
    {
        return registry[id]();
    }

private:
    map<int, Creator> registry;
};


// this template class is used for automatic registration of object's creators
template <class T>
struct RegisterToFactory
{
    RegisterToFactory(ObjectFactory& factory)
    {
        factory.registerCreator(T::ID, &T::CreateInstance);
    }
};


namespace
{
    // automaticaly register creators for each class
    RegisterToFactory<A> autoregisterACreator(ObjectFactory::instance());
    RegisterToFactory<B> autoregisterBCreator(ObjectFactory::instance());
}




// lets this this solution
int main(int argc, char *argv[])
{
    vector<int> ids;

    ids.push_back(static_cast<int>(A::ID));
    ids.push_back(static_cast<int>(B::ID));

    srand(time(0));

    for (int i = 0; i < 20; ++i)
    {
        int randomClasssId = ids[rand() % ids.size()];
        auto_ptr<Base> testObject(ObjectFactory::instance().CreateById(randomClasssId));
        cout << "Object of classId = " << testObject->getClassId() << " has been produced by factory." << endl;
    }


    system("PAUSE");
    return EXIT_SUCCESS;
}

如果您想要保护对象创建者,请将静态创建者移动到私有/受保护部分,并使该类成为具体外部创建者类的友元。当创建过程复杂且容易出错时,这可能非常有用。外部创建者拥有完整的知识,知道如何正确地实例化此类的对象,因此最终用户永远不会失败。 - rzur2004
我不推荐使用RTTI,除非您了解具体平台,并且每个模块(DLL,SO,LIB等)都有完全相同的编译和链接选项。一些旧的编译器可能会在具有命名空间和跨模块注册到工厂的RTTI方面存在问题,特别是当我们尝试注册模板类时。总的来说,RTTI是弱势的,请尽量避免使用它,因为在构建配置中可能会禁用RTTI,导致无法正常运行。 - rzur2004

0
我不明白这个问题。你是在问一个可以同时容纳不同类型实例的数组吗?当然可以使用多态性来实现。还是你想要一个类型数组(比如反射)?这可以使用RTTI或Qt类型信息(例如)来实现,但我从未这样做过。

请在评论中提出澄清请求,而不是作为答案。 - Benjamin Lindley

-1

问题不是关于多态性,链接也不是答案。 - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接