我试图模仿这个Python功能:
class Bla:
def __init__(self, arg):
self.arg = arg
{"bla": Bla}["bla"](None) # create a Bla class dynamically
我看了这个,但它没有帮助我…
我的c++模块看起来像这样:
// MyObj is an abstract class with all its subclasses using same signature constructor
class Factory{
MyObj create_obj(int n);
}
std::map<std::string, MyObj> my_map = {"example1", MyObj1};
my_map["example1"](0);
我怎样才能使它工作?
对象工厂通常是这样的(省略了大部分不重要的细节)。
class Animal { virtual ~Animal() = default; }
class Dog : public Animal {};
class Cat : public Animal {};
class AnimalFactory
{
public:
std::unique_ptr<Animal> make(std::string kind) {
if (kind == "cat") return std::make_unique<Cat>();
if (kind == "dog") return std::make_unique<Dog>();
throw FactoryException("Unsupported animal");
}
};
现在你可以用地图代替make
中的if-else
。但这是一个实现细节。
class AnimalFactory
{
using fn = std::function<std::unique_ptr<Animal>()>;
std::map<std::string, fn> make_map = std::map<std::string, fn> {
{"cat", []() { return std::make_unique<Cat>(); }},
{"dog", []() { return std::make_unique<Dog>(); }}
};
public:
std::unique_ptr<Animal> make(std::string kind) {
auto fn = make_map.find(kind);
if (fn != make_map.end()) return fn->second();
else throw FactoryError("Unsupported animal");
}
};
注意事项:
- 工厂制造的对象是多态的。(否则通常没有太多的意义,有一个工厂。) 对象通过指向其基类的指针返回。
- map(如果有的话)将字符串映射到生成对象的函数。
与Python不同,c++是静态类型的。类不是一等对象,你不能存储它们。
在下面我假设MyObj
是所有"存储"的抽象基。类型意味着继承,MyObj1
/MyObj2
是这些类型中的一些。
而不是类本身,您可以存储函数指针或std::function
年代返回std::unique_ptr<MyObj>
指向相应类型的新对象,以便my_map["example1"](0);
有定义良好的静态类型。例如:
#include<functional> // for std::function
#include<memory> // for std::unique_ptr and std::make_unique
#include<type_traits> // for std::has_virtual_destructor_v
//...
template<typename T>
std::unique_ptr<MyObj> MakeMyObj(int n) {
static_assert(std::has_virtual_destructor_v<MyObj>);
return std::make_unique<T>(n);
}
// or std::map<std::string, std::unique_ptr<MyObj>(*)(int)>
std::map<std::string, std::function<std::unique_ptr<MyObj>(int)>> my_map = {
{ "example1", MakeMyObj<MyObj1> },
{ "example2", MakeMyObj<MyObj2> }
};
//...
auto x = my_map["example1"](0); // x has type std::unique_ptr<MyObj>
auto y = my_map["example2"](123); // y also has type std::unique_ptr<MyObj>
std::function
支持比函数指针方法更多类型的可调用对象,但运行时开销明显增加。对于函数指针方法,尝试使用未赋值的字符串参数也将导致未定义的行为,而std::function
方法将抛出异常。
正如在另一个答案中提到的,如果不需要在运行时修改map,更直接的方法是编写一个工厂函数或类,直接切换字符串参数并直接抛出适当的异常。
无论如何,这要求MyObj
有virtual
析构函数是很重要的。否则程序将有未定义的行为。这就是static_assert
要防止的,因为std::unique_ptr
本身目前不幸没有验证这一点。