1

我整天都在做这件事,所以我希望我不会忘记任何重要的细节,但是就这样吧。我最初的目标是拥有一个封装了如何创建播放器的逻辑的播放器工厂。

看起来是这样的:

Player PlayerFactory::CreatePlayer() {

    Player constructee(id_generator++);

    // c++1x move semantics says this isn't a copy!
    return constructee;
}

在播放器中,有一个复合的 PlayerInputComponent 成员变量。这个 PlayerInputComponent 对象封装了处理玩家输入的逻辑,为此它需要一个指向实际玩家本身的引用/指针。很简单,它将对播放器的引用作为其构造函数的唯一参数。

当玩家对象被构造时,它的初始化列表将一个对自身的引用传递给 PlayerInputComponent 对象。这是它的外观:

Player::Player(const unsigned int id) 
    : id(id), 
    input_component(*this)
{
...
}

我知道在初始化列表中取消引用它通常是一个坏主意,但我只是使用它在 PlayerInputComponent 对象中设置引用。这是构造函数:

PlayerInputComponent::PlayerInputComponent(Player &player) 
    : player_entity(player) { // set the reference, it is never used within the constructor body
}

无论出于何种原因,当播放器工厂返回它创建的播放器的本地副本时,引用就会出现乱码。我的意图是让在堆栈上创建的播放器实例被移动并分配给被调用者。如下所示:

auto player = player_factory.CreatePlayer();

在代码执行播放器的复合 PlayerInputComponent 对象后,对其播放器的引用被破坏。我知道当工厂将本地播放器对象返回给被调用者时会调用播放器的移动构造函数,但是播放器在 PlayerInputComponent 中的引用会发生什么?有没有更好的方法来解决这个问题?我喜欢在这种情况下使用引用与指针的语义含义,尽管我确实尝试使用指针并得到相同的结果。

当播放器对象移出 CreatePlayer() 成员函数并分配给“自动播放器”时,谁能向我解释 PlayerInputComponent 对播放器的引用发生了什么?

为了完整起见,这里是对象的声明:

class PlayerInputComponent {

private:
    Player &player_entity;
    void HandleKeyboardInput();
public:
    //PlayerInputComponent(PlayerInputComponent &&other);
    //PlayerInputComponent(const PlayerInputComponent &other);
    PlayerInputComponent(Player &player);
    void Update();
};

这是播放器:

class Player : public Entity{
    friend class PlayerFactory;
private:
    const unsigned int id;

public:
    Player(const unsigned int id);

    // components
    PlayerInputComponent input_component;
};

我在下面重建了一个小例子,它显示了确切的行为,它编译/演示了问题。谢谢!

class B;

class A  {
public:
    A(B& bb) : b(bb) { }

private:
    B &b;
};

class B {
public:
    B() : a(*this) { othercrap = othercrap2 = 0; }
    int othercrap;
    int othercrap2;
private:
    A a;
};

class BFactory {
public:
    B CreateB() {
    B temp;
        return temp;
    };
};

B start() {
    BFactory fac;
    auto bi = fac.CreateB();

    return bi;
}

int main(int argc, char *argv[]) {
    auto i = start();
}
4

2 回答 2

5

移动会使引用无效。(引用的是存储位置,而不是对象)

请记住,移动会创建一个新对象(因此名称为移动构造函数)。内容的所有权被转移,但对象实际上并未移动到内存中的新位置。在构造新对象后将其销毁。

您应该定义Player的复制和移动构造函数以正确绑定新对象中的引用。

于 2012-02-20T03:39:22.733 回答
1

我在您的示例中看不到任何移动语义。CreateB只会获得内部临时(temp),它将被简单地复制到要分配给bi的临时返回对象(xvalue)上。您的引用会出现乱码,因为它们是对已经破坏的本地临时对象的简单复制引用。

于 2012-02-20T03:40:41.987 回答