移动所有者时,对所有者的C ++复合引用已损坏

时间:2012-02-20 03:11:27

标签: c++ reference move-constructor

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

这就是看起来的样子:

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中播放器的引用会发生什么?有没有更好的方法来解决这个问题?我喜欢在这种情况下使用引用vs指针的语义,尽管我确实尝试使用指针并获得相同的东西。

当有人将玩家对象移出CreatePlayer()成员函数并分配给“auto player”时,有人可以向我解释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();
}

2 个答案:

答案 0 :(得分:5)

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

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

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

答案 1 :(得分:1)

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