使用索引创建类的对象

Creating object of a class using index

本文关键字:对象 创建 索引      更新时间:2023-10-16

我有这样的类:Class1类别2类别3一年级Class5Class6.........ClassN

在其中一个函数中,我需要根据函数的输入参数创建类的对象。

例如,如果输入为1,则需要创建Class1对象,如果输入为2,则创建Class2对象。

是否可以不使用if条件创建对象?我不希望所有的课程都是这样。

由于使用了继承,因此需要一个工厂函数来生成任何派生类型…

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

…和支持operator[]随机访问的容器。

如果需要不连续的索引,请选择映射:

std::map<int, base *(*)()> map_spawner =
{
    { 0, &spawn<child_1> },
    { 1, &spawn<child_2> }
};

如果索引是连续的,选择一个向量:

std::vector<base *(*)()> vec_spawner =
{
    &spawn<child_1>,
    &spawn<child_2>
};

工作示例:

#include <map>
#include <vector>
class base
{
    public:
        virtual ~base () = default;
    public:
        virtual int f () const = 0;
};
class child_1 : public base
{
    public:
        int f () const override { return 1; }
};
class child_2 : public base
{
    public:
        int f () const override { return 2; }
};
template<typename T>
base * spawn ()
{
    return new T();
}
int main ()
{
    // With a vector
    std::vector<base *(*)()> vec_spawner =
    {
        &spawn<child_1>,
        &spawn<child_2>
    };
    base * child = vec_spawner[0]();
    // Do something with child here ...
    delete child;
    // With a map
    std::map<int, base *(*)()> map_spawner =
    {
        { 0, &spawn<child_1> },
        { 1, &spawn<child_2> }
    };
    child = map_spawner[1]();
    // Do something with child here ...
    delete child;
}

现在你可以使用用户输入来生成一个特定的实例。


如果你的派生类型构造函数不共享相同的参数,不幸的是,据我所知,你不能使用任何容器…我能想到的唯一的可能性是这样的(工作示例):

#include <utility>
class base
{
    public:
        virtual ~base () = default;
    public:
        virtual int f () const = 0;
};
class child_1 : public base
{
    public:
        int f () const override { return 1; }
};
class child_2 : public base
{
    public:
        child_2 (int i) { (void) i; }
    public:
        int f () const override { return 2; }
};
template<typename... Args>
base * spawn (int input, Args && ... args)
{
    switch (input)
    {
        case 0: return new child_1 {};
        case 1: return new child_2 { std::forward<Args>(args)... };
        // ...
    }
    return nullptr;
}
int main ()
{
    int input = 1;
    base * child = spawn(input, 42);
    // Do something with child here ...
    delete child;
}

如果您的项目是windows dll,我有一个可能的解决方案:

为项目中的每个派生类定义一个工厂方法。创建Class1对象的工厂方法应该命名为us1(或者任何触发该对象创建的用户输入)。

然后使用GetProcAddress API根据其名称获得工厂方法的地址(同样,名称是用户输入的)-获得后,只需调用它来创建对象。

一些代码片段来展示这个概念(因为你还没有发布你的代码,所以很难提出一些真实的建议)

class Base
{
public:
   void foo() = 0;
}
class Derived1 : public Base
{
public:
   void foo() {};  
}
class Derived2 : public Base
{
public:
   void foo() {};  
}

extern "C"
{
    void __declspec(dllexport) us1 (Base*& pObj)
    {
       pObj = new Derived1();
    }
    void __declspec(dllexport) us2 (Base*& pObj)
    {
       pObj = new Derived2();
    }
}

然后在你的CPP文件中使用类似

的内容
typedef void (__cdecl FUNCPROC)(Base*&);
string userInput;
cin >> userInput;
// get a handle to your dll memory (if this is a dll)
// assuming moduleHandle points to your dll memory
FUNCPROC* pProcAddr = GetProcAddress((HMODULE)moduleHandle, userInput);

现在pProcAddr是一个基于用户输入的受尊重的工厂方法的指针。

得到正确的对象

Base* pTmp = NULL;
pProcAddr(pTmp);

现在我上面的建议是不完整的,还有更多的事情要处理(检查有效的用户输入,检查工厂方法是否存在于内存中,等等…),但它应该给你一个大致的想法。

我在很多情况下都使用了这段代码。

假设您的所有类Class1 ~ ClassN都派生自一个抽象基类class Base

#include <vector>
#include <memory>
#include <functional>
std::vector<std::function<std::shared_ptr<Base> ()>> CtorList = {
    [] { return std::shared_ptr<Base>(new Class1); },
    [] { return std::shared_ptr<Base>(new Class2); },
    [] { return std::shared_ptr<Base>(new ClassN); },
};
// Create
std::shared_ptr<Base> p = CtorList[num - 1]();

(生活)

我建议使用从输入类型映射到工厂对象的映射。工厂对象有一个返回所需类型的虚方法。因此,每个类(Class1…ClassN)都有一个匹配的工厂类(FCi),该工厂类知道如何构造(Classi)的对象。

typedef std::map< Input, Factory > FactoryMap;
FactoryMap factoryMap;
void initFactoryMap()
{
       factoryMap[ Input1 ] = new FactoryForClass1();  // subclass of Factory, implementing constructClass
       factoryMap[ Input2 ] = new FactoryForClass2();
       factoryMap[ Input3 ] = new FactoryForClass3();
       ...
}
AbstractClass *inputHandler( Input n )
{
    return factoryMap[n]->constructClass( n ); // may not need to pass n in
}

我真的很感激您定义了这么多的类…但我更愿意定义一个单一的类与一个函数,将负责创建所需的表,我会试图找出一些逻辑的方式来创建/打印表,如果表是类似的…或者在最坏的情况下,我会使用switch语句来选择创建/打印哪个表。