2013-11-19 4 views
2

Я следую за дизайном архитектуры игрового объекта presented by Marcin Chady. В качестве краткого описания класс Game Object может содержать несколько экземпляров подкласса атрибутов и поведения. Объекты игры также содержатся в классе Scene. Эта схема может прояснить вещи:C++ композиция с использованием умных указателей

enter image description here

Поведение подклассы должны быть в состоянии получить доступ к их родительским Game класса Object. Это необходимо, так как поведение должно иметь возможность читать и записывать различные атрибуты и подписываться на получение событий от игрового объекта. Некоторым поведением также может потребоваться доступ к сцене родительского игрового объекта, чтобы они могли создавать экземпляры других игровых объектов.

В настоящее время я использую общие указатели и слабые указатели. Например, класс Game Object содержит vector<shared_ptr<Attribute>> и vector<shared_ptr<Behaviour>>. Затем классы поведения и атрибута содержат weak_ptr<GameObject>. Это нарушает опорный цикл.

Теперь, если поведение хочет получить доступ к сцене, он должен выполнить несколько вложенных слабые указатели замков:

if (std::shared_ptr<GameObject> sharedGameObject = GetGameObject().lock()) 
{ 
    if (std::shared_ptr<Scene> sharedScene = sharedGameObject->GetScene().lock()) 
    { 
     // Do something with the scene. 
    } 
} 

Это может получить немного грязное. Если нам нужно получить доступ к классу Application, тогда потребуются 3 вложенных блокировки. Это явно не требуется при использовании исходных указателей. Использую ли я правильные умные указатели? Если я есть, есть ли что-нибудь, что я могу сделать, чтобы привести в порядок это, или это то, с чем мне просто придется жить?

+0

Одно из возможных решений: полностью удалить объекты weak_ptr. В идеале для объектов Behavior и Attribute вообще не нужно обращаться к GameObject, но если они абсолютно необходимы, передайте им указатель GameObject (или ссылку) в качестве аргумента методам, который вы им вызываете. –

+0

Почему вы используете интеллектуальные указатели здесь. Кажется, что нет необходимости. –

+0

Вы можете закодировать его с помощью общих петель указателей, а затем вызвать метод выключения, когда вы хотите, чтобы объект уходил. В методе выключения вы можете сбросить общие указатели, чтобы удалить целевые объекты. Вероятно, вам все равно придется протестировать указатели, прежде чем использовать их, чтобы убедиться, что они не были сброшены, но это позволяет избежать хита производительности слабой блокировки указателя. –

ответ

1

Вы действительно хотите видеть умные указатели здесь. Из первого предположения, на основе иерархии дизайна (и имена):

  • Если это типичная иерархия, там будет только один Application, который не будет выделяться динамически, но быть локальная переменная в main. Таким образом, никогда не было бы умного указателя .

  • Все Scene принадлежат к Application, так что это понятно из проекта , которому принадлежит что.В обоих случаях это отношение от 1 до n, которое означает, что указатели на объект-владелец должны быть в контейнерах . Вы можете использовать std::unique_ptr, но это не действительно кажется необходимым; на самом деле, кажется немного запутывающим: , чтобы удалить объект, вы не используете delete, вы erase от контейнер. (С другой стороны, с сырыми указателями, вы должны как удалить и удалить его из контейнера. Это в значительной степени зависит от вас здесь, хотя я, как правило, предпочитают простоту сырых указателей.)

  • Если эти классы представляют то, что я думаю, они делают, GameObject приходят и уходят в результате внешних событий. Это типичный случай , где ни один из существующих интеллектуальных указателей не является релевантным. Я бы предположил, что большая часть GameObject создана Scene в ответ на событие; некоторые могут быть созданы другими GameObject. Но после создания они управляют своей собственной жизнью, и, вероятно, будут удалены delete this в их случае обработчик. (Конечно, все объекты, которые заинтересованы в их жизни должны регистрироваться в качестве наблюдателей, с помощью шаблона наблюдателя. Но это будет так или иначе.)

  • EventDispatcher несомненно имеет указатели на любые объекты заинтересованных в событиях , но они предназначены для навигации. Они должны не быть умными указателями. Это ответственность каждого объекта для регистрации и disenrol с EventDispatcher в соответствии с событиями, он заинтересован в — довольно очевидно, будет disenrol за все в деструкторе, , потому что мертвые объекты не заинтересованы в каких-либо событиях, но он также (возможно) зачислит и разобьет в другое время .

Обратите внимание, что необходимость в weak_ptr обычно является хорошим показателем того, что ссылки подсчитывали указатели не решение вы ищете.

+2

'delete this' почти всегда является ошибкой. В лучшем случае это неопределенное поведение. Если вам это нужно, вам нужно реализовать какую-то функцию 'deleteLater', которая вызывается после завершения выполнения объекта, который нужно удалить. –

+0

Благодарим вас за подробное объяснение. – Homar

+0

@KubaOber Да, мой класс GameObject имеет флагов 'dead', который я использую для удаления всех мертвых игровых объектов из сцены в конце обновления сцены. – Homar

1

Я думаю, что вам не нужны никакие смарт-указатели здесь вообще, если предположить:

  • что GameObject объект действительно владеетAttribute & Behavior объектов или
  • , что время жизни GameObject объекта будет всегда длиннее, чем Attribute & Behavior срок службы объектов.

Или, учитывая ваш пример кода, подумайте об этих вопросах: что бы сделали else stanzas? Неужели они когда-нибудь будут казнены? Я думаю, что ответ будет следующим: «Они только обнаруживают ошибку/обнаружение ошибок &», и «Нет, я не могу представить».

Вы должны переключиться на внутренние ссылки на класс GameObject, используя std::reference_wrapper<T>, если вы хотите, чтобы это был немного более современный C++-просмотр, или raw-указатели, или raw-ссылки.

Смежные вопросы