如何映射类对象并实例化它



我试图模仿这个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");
}
};

注意事项:

  1. 工厂制造的对象是多态的。(否则通常没有太多的意义,有一个工厂。)
  2. 对象通过指向其基类的指针返回。
  3. 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,更直接的方法是编写一个工厂函数或类,直接切换字符串参数并直接抛出适当的异常。

无论如何,这要求MyObjvirtual析构函数是很重要的。否则程序将有未定义的行为。这就是static_assert要防止的,因为std::unique_ptr本身目前不幸没有验证这一点。

最新更新