class 构造函数上的指针

Pointer on class contructor

我正在做一个个人项目,我需要一个动态工厂。 目前我有以下“系统”:

class Action { ...} // Which is abstract
class A : public Action { ... }
class B : public Action { ... }
class C : public Action { ... }

...

std::map<std::string, Action *> _actions;
_action.insert( { "A", new A() } );
_action.insert( { "B", new B() } );
_action.insert( { "C", new C() } );

Action * act = _actions.find("B")->second; // Get action's pointer

但我对这个解决方案并不满意,因为即使我从不使用这些操作,它们也会在启动时实例化并在应用程序的所有生命周期内保留在内存中,如果我想要一个“干净的”对象,我需要动态转换我的对象以确定其类型以通过复制调用构造函数...我不是这个解决方案的真正粉丝,因为我必须“硬编码”所有情况...

我不知道我想象的解决方案是否真的可行,但这是我的想法:

我保留了之前的 class 层次结构,即:

class Action { ...} // Which is abstract
class A : public Action { ... }
class B : public Action { ... }
class C : public Action { ... }

但我想以不同的方式使用地图:

std::map<std::string, POINTER_ON_CLASS_CONSTRUCTOR> _actions;
_actions.insert( { "A", &A::A } );
_actions.insert( { "B", &B::B } );
_actions.insert( { "C", &C::C } );

Action * act = new (_actions.find("B")->second);

继续我的想法:

我想在每个要映射的构造函数上映射一个带有指针的键,然后根据该键动态实例化一个基于动作的对象。

提前感谢您的帮助,如果您有解决此问题的建议,请毫不犹豫地提交 :)


上下文:

我正在为我的软件创建一个 CLI,如果我想编写代码,这个解决方案是将一个动作的第一个词映射到一个关联的动作。

示例:如果我输入“add file.txt”,“add”将是我的地图的键,用于在 class“AddAction”(这是一个Action 的子级 - 它是抽象的并且对所有基于动作的 class) 都是通用的 class).

编辑:在发布这个问题之前,我在网上和这个论坛上进行了搜索,但没有找到我要找的东西.. :/

您可以将名称映射到工厂函数,然后在每次要创建新实例时调用这些函数。将它包裹在 unique_ptr 中会在使用后给你带来你想要的破坏。 live link

std::unordered_map<std::string, std::function<std::unique_ptr<Action>()>> ACTIONS = {
  {"A", [] { return std::make_unique<A>(); }},
  {"B", [] { return std::make_unique<B>(); }},
  {"C", [] { return std::make_unique<C>(); }}
};

int main() {
  auto action = ACTIONS["A"]();
}

您可以实现克隆惯用语以避免必须 dynamic_cast 对象来确定要调用哪个复制构造函数,例如:

class Action {
public:
    virtual ~Action() {};
    virtual Action* clone() = 0;
    ...
};

class A : public Action {
public:
    Action* clone() { return new A(*this); }
    ...
};

class B : public Action {
public:
    Action* clone() { return new B(*this); }
    ...
};

class C : public Action {
public:
    Action* clone() { return new C(*this); }
    ...
};

...

std::map<std::string, Action*> _actions;
_action.insert( { "A", new A } );
_action.insert( { "B", new B } );
_action.insert( { "C", new C } );

Action *act = _actions["B"]->clone(); // see Note below...
...
delete act;

注意:当您不验证返回的迭代器时,没有理由使用 map::find()。只使用 map::operator[] 会更容易,如果你想在找不到指定的键时抛出错误,则使用 map::at() 会更容易。

然而,如果你不想在 map 中存储实际对象,你可以使用工厂函数代替(你无法获得指向构造函数的指针),例如:

class Action { ... }
class A : public Action { ... }
class B : public Action { ... }
class C : public Action { ... }

...

using ActionFactoryFunc = Action* (*)();

std::map<std::string, ActionFactoryFunc> _actions;
_action.emplace( "A", []() -> Action* { return new A; } );
_action.emplace( "B", []() -> Action* { return new B; } );
_action.emplace( "C", []() -> Action* { return new C; } );

Action *act = _actions["B"]();
...
delete act;

最后,你真的应该使用 std::unique_ptr<Action> 而不是原始 Action* 指针,例如:

#include <memory>

class Action {
public:
    virtual ~Action() = default;
    virtual std::unique_ptr<Action> clone() = 0;
    ...
};

class A : public Action {
public:
    std::unique_ptr<Action> clone() override { return std::make_unique<A>(*this); }
    ...
};

class B : public Action {
public:
    std::unique_ptr<Action> clone() override { return std::make_unique<B>(*this); }
    ...
};

class C : public Action {
public:
    std::unique_ptr<Action> clone() override { return std::make_unique<C>(*this); }
    ...
};

...

std::map<std::string, std::unique_ptr<Action>> _actions;
_action.emplace( "A", std::make_unique<A>() );
_action.emplace( "B", std::make_unique<B>() );
_action.emplace( "C", std::make_unique<C>() );

auto act = _actions["B"]->clone();
...

或者:

#include <memory>

class Action { ... }
class A : public Action { ... }
class B : public Action { ... }
class C : public Action { ... }

...

using ActionFactoryFunc = std::unique_ptr<Action> (*)();

std::map<std::string, ActionFactoryFunc> _actions;
_action.emplace( "A", []() -> std::unique_ptr<Action> { return std::make_unique<A>(); } );
_action.emplace( "B", []() -> std::unique_ptr<Action> { return std::make_unique<B>(); } );
_action.emplace( "C", []() -> std::unique_ptr<Action> { return std::make_unique<C>(); } );

auto act = _actions["B"]();
...