【问题标题】:How/where does std::map initialize its members?std::map 如何/在哪里初始化其成员?
【发布时间】:2016-10-04 20:07:09
【问题描述】:

我想尽可能笼统地表达这个问题,但它确实与我正在编程的游戏有关。

我有一个具有 std::map 成员的类:

class Player {
    ...
    private:
        std::map<Action, Command>  mActionBinding;
    ...
}

Action 是 Player 类中的枚举。它包含动作标题,例如 MoveLeft 或 MoveRight。 Command 是一个包含函数和类别的结构体。

struct Command
{
        Command();

        std::function<void(SceneNode&, sf::Time)>        action;
        unsigned int                                     category;
        // SceneNode&                                    node
};

这个系统背后的想法是,我可以将一个函数绑定到一个 SceneNode 类别(我的游戏中的一个可绘制对象)。游戏的 update() 将遍历我游戏中的所有 SceneNode,如果 SceneNode 的类别与命令中的类别匹配,则在 SceneNode 上执行命令。 (如果这一切听起来很熟悉,这是 SFML 的 GQE 引擎,源代码可以在这里找到:https://github.com/LaurentGomila/SFML-Game-Development-Book

我希望命令比这个实现更具体。如果 SceneNode 与命令指定的匹配,我希望在 SceneNode 上执行命令,而不仅仅是类别比较。

但是,命令是在这个 Player 类中创建的,并且它们是在那个 std::map 中创建的。玩家的构造函数调用了一个 initializeAction() 函数,该函数调用如下:

mActionBinding[MoveLeft].action = 
      derivedAction<SpaceCraft>(SpaceCraftMover(-playerSpeed, 0.f));

基本上,它为一些操纵航天器节点的函数设置动作。稍后在 Player 构造函数中,mActionBinding 的命令类别设置如下:

for(auto& pair : mActionBinding)
            pair.second.category = Category::PlayerSpaceCraft;

我的问题:Command 的构造函数在我看不到的地方被调用。我想更改 Command 的构造函数以接受 SceneNode&,这样我就可以将其设置为 SpaceCraft 节点(将 unsigned int 替换为注释的 SceneNode&)。如何以可以看到 Command(SceneNode&) 的方式创建这些命令,从而能够正确设置它?换句话说,我怎样才能得到这个:

Command::Command(SceneNode& n)
: action()
// , category(Category::None)
, node(n)
{}

目前在 stl_map.h 中给我一个我不理解的错误,可能是因为在 Player 的函数中调用了 Command 构造函数不正确

提前致谢,抱歉这个冗长的问题,但我认为上下文是必要的。

【问题讨论】:

    标签: c++ struct constructor sfml stdmap


    【解决方案1】:

    如果我正确理解了这个问题,我认为您正在观察 std::map::operator[] 的 create-if-does-not-exist-and-return-a-reference 行为:

    如果 [the key] 与容器中任何元素的键都不匹配,则该函数会使用该键插入一个新元素并返回对其映射值的引用。请注意,这总是将容器大小增加一,即使没有为元素分配映射值(该元素是使用其默认构造函数构造的)。

    如果您想自己构造Command 对象(可能通过非default constructor),您可能需要调用std::map::insertstd::map::emplace,具体取决于您想要的语义(以及您的编译器对较新的标准),而不是使用operator[]

    综合起来,试试这样的:

    Command cmd(theSceneNode);
    cmd.action = derivedAction<SpaceCraft>(SpaceCraftMover(-playerSpeed, 0.f));
    // more command setup, possibly
    mActionBinding.insert(std::make_pair(MoveLeft, cmd));
    // or possibly, mActionBinding.insert(std::make_pair(MoveLeft, std::move(cmd)));
    

    【讨论】:

    • 对,但即使在那里,构造函数总是被调用。如果你不调用它的构造函数,你永远不会得到一个对象,如果你添加一个 printf,你会发现这是真的。如果你自己构造然后插入,你会得到一个调用的复制/移动构造函数,你也可以用 printf 观察它。
    • 嗯,我想我明白你的回答,但我仍然不确定如何继续。你是说在调用 mActionBinding[MoveLeft].action = blahblah 之前,我需要创建一个新的命令,比如 mActionBinding[MoveLeft] = Command(SceneNode&)?
    • 哦,等一下,我想我了解 std::map::insert 和 std::map::emplace。如果它不起作用,我会试一试并在这里发表评论。谢谢。谢谢remyabel,这解决了我要遇到的另一个问题。
    • @c45207 我遇到了问题。我的代码中仍有其他区域使用commands.push(mActionBinding[found-&gt;second]); 之类的东西(发现是一个应该设置为操作的自动,我们的键)这给了我以前遇到的相同的地图错误。这些代码行是否仍在使用“create-if-does-not-exist-and-return-a-reference”行为?如果我需要这些行仍然按原样工作,我该怎么办?
    • 哎呀,让我说得更具体一些。我有另一个名为 mKeyBindings 的映射,它将键盘键绑定到操作。因此 found->second 是我们自动检测到的键盘键(这是我们的 mKeyBinding 的键)关联的操作。此外,commands 是我们的 Command 结构的队列。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-01
    • 1970-01-01
    • 2016-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多