根据C ++中的字符串值创建类对象实例的好方法?

问题描述

有关问题,请参见代码片段:

class Base; 
typedef Base* BasePtr; 

template<typename D>
BasePtr CreateDerivedClass() {
    return new D();
}

class D1 : public Base {
....
};

class D2 : public Base {
....
}

void main() {
    std::string derived_name = {"D1","D2"}; 
    std::vector<BasePtr> derived_vec; 
    for (auto& name : derived_name) {
// QUESTION: Other than factory design pattern,how can I pass the derived 
// class typename which is the value of string "name" to CreateDerivedClass<name> ?
        derived_vec.push_back(CreateDerivedClass<name>()); 
    }
}

注意:在这里,我不想使用工厂设计模式,该模式要求在定义过程中派生类本身进行注册

解决方法

模板参数值必须在编译时知道。没有简单的方法可以直接将 在运行时将字符串转换为用于模板参数的类型。您将必须在运行时比较字符串值,以决定要调用哪个CreateDerivedClass专业化名称。

例如,处理此问题的最简单方法是使用std::map,其中其元素包含指向您希望能够调用的专用函数的指针,例如:

using createFuncType = BasePtr (*)();
std::map<std::string,createFuncType> createFuncs;
createFuncs["D1"] = &CreateDerivedClass<D1>;
createFuncs["D2"] = &CreateDerivedClass<D2>;
...

然后您可以执行以下操作:

std::string derived_names[] = {"D1","D2"}; 
std::vector<BasePtr> derived_vec; 
for (auto& name : derived_names) {
    derived_vec.push_back(createFuncs[name]());
}

在这种情况下,确实没有其他选择。


话虽如此,您应该使用std::unique_ptr<Base>而不是原始的Base*指针,例如:

#include <map>
#include <vector>
#include <string>
#include <memory>

class Base {
public:
    virtual ~Base() = default;
};

using BasePtr = std::unique_ptr<Base>;
using createFuncType = BasePtr (*)();

template<typename D>
BasePtr CreateDerivedClass() {
    return std::make_unique<D>();
}

class D1 : public Base {
    ...
};

class D2 : public Base {
    ...
}

...

int main() {
    std::map<std::string,createFuncType> createFuncs;
    createFuncs["D1"] = &CreateDerivedClass<D1>;
    createFuncs["D2"] = &CreateDerivedClass<D2>;
    ...

    std::string derived_names = {"D1","D2",...};
    std::vector<BasePtr> derived_vec; 
    for (auto& name : derived_names) {
        derived_vec.push_back(createFuncs[name]()); 
    }

    ...

    return 0;
}
,

应该在编译时就知道模板的类型T,这是因为编译器需要创建与类型T的类/函数的实现匹配的代码。这就是C ++中模板的工作方式。 如果derived_names将包含未知类型的值"D8",该怎么办?