Составная ссылка C ++ на владельца повреждена при перемещении владельца - PullRequest
1 голос
/ 20 февраля 2012

Я работал над этим весь день, поэтому я надеюсь, что не забуду каких-либо важных деталей, но здесь идет. Моя первоначальная цель состояла в том, чтобы создать фабрику игроков, которая заключала в себе логику создания игрока.

Вот как это выглядит:

Player PlayerFactory::CreatePlayer() {

    Player constructee(id_generator++);

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

Внутри проигрывателя есть составная переменная-член PlayerInputComponent. Этот объект PlayerInputComponent инкапсулирует логику работы с вводом игрока, для этого ему нужна ссылка / указатель на самого игрока. Достаточно просто, он принимает ссылку на игрока как единственный параметр своего конструктора.

Когда объект player создан, его список инициализаторов передает ссылку на себя в объект 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? Есть ли лучший способ решить эту проблему? Мне нравится семантическое значение наличия ссылки против указателя в этой ситуации, хотя я попробовал это с указателем и получил то же самое.

Может ли кто-нибудь объяснить мне, что происходит со ссылкой PlayerInputComponent на игрока, когда объект player перемещается из функции-члена CreatePlayer () и назначается «auto player»?

Для полноты здесь приведены объявления объекта:

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 ]

5 голосов
/ 20 февраля 2012

Перемещение делает недействительными ссылки. (Ссылка делается на место хранения, а не на объект)

Помните, что перемещение создает новый объект (отсюда и название, move constructor ). Право собственности на содержимое передается, но объект фактически не перемещается в новое место в памяти. Он уничтожается после постройки нового объекта.

Вы должны определить конструкторы копирования и перемещения Player, чтобы правильно связать ссылку в новом объекте.

1 голос
/ 20 февраля 2012

Я не вижу никакой семантики перемещения в вашем образце. CreateB просто получит тот внутренний временный ( temp ), который будет тривиально скопирован для временного объекта возврата (xvalue), который будет присвоен би .Ваши ссылки искажены, потому что они тривиально скопированы ссылки на уже уничтоженные локальные временные объекты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...