Круговая зависимость с отношениями «содержит» и «находится в» - PullRequest
0 голосов
/ 03 июня 2018

Я хотел бы узнать ваше мнение по поводу следующей проблемы.

У нас есть класс под названием "Комната".Каждая комната может содержать ноль или более экземпляров класса «Персона», поэтому в Комнате хранится коллекция персон (например, вектор).Он владеет им.Однако существует некоторая трудоемкая логика, связанная с перемещением людей между комнатами, поэтому человек также содержит текущую комнату, в которой он находится. Это просто указатель без владельца .Эта информация теоретически избыточна, потому что ее можно было бы извлечь из коллекций людей в комнатах, но при условии большого количества комнат >> количества людей такая операция будет медленной ...

class Room {
std::vector<Person> peopleInside;
};

class Person {
Room* currentRoom; //could be shared_ptr to avoid raw pointers
};

Естественно, он более сложный (у классов их больше), но я максимально упростил его.

Мои вопросы:

1) В этой ситуации это круговая зависимость дляse?

2) Является ли это решение грязным / нелегальным для вас?

3) Стоит ли переходить на что-то другое?

1 Ответ

0 голосов
/ 03 июня 2018

Этот вопрос действительно нужно изучать в контексте.Если Person s существует только в контексте Room, то обратный указатель безопасен.Когда Room уничтожается, тогда Person также уничтожаются (бум!), Поэтому ничего не может пойти не так.

Но я подозреваю, что это слишком упрощенно.Объявление Room, скорее всего, будет выглядеть примерно так:

class Room {
    std::vector<std::shared_ptr<Person>> peopleInside;
};

И теперь у нас есть возможная проблема с «висящим указателем», которую вы не можете решить с помощью std::shared_pointer<Room> в Personпотому что тогда у вас do есть круговая зависимость, и ни shared_ptr никогда не сможет удалить объект, которым он управляет (потому что Room содержит ссылку на Person и наоборот, поэтому тупик).

Итак, вместо этого, объявите Person следующим образом:

class Person {
    std::weak_ptr<Room> currentRoom;
};

И инициализируйте currentRoom из некоторого shared_ptr<Room>, который вы сохраняете доступным, пока существует Room.Это нарушает круговую зависимость.

Для разыменования currentRoom, вы можете сделать:

if (auto room_I_am_currently_in = currentRoom.lock())
{
    room_I_am_currently_in->OpenDoor ();
}

И если исходный shared_ptr<Room> был уничтожен, то lock не удастся.Блокировка будет снята, когда room_I_am_currently_in выйдет из области видимости (на самом деле это shared_ptr<Room>).

А чтобы переместить человека в другую комнату, просто переназначьте currentRoom.

Подробнеео std::weak_ptr при cpreference .

...