использование объекта-блока после удаления указателя this - PullRequest
0 голосов
/ 08 мая 2018

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

void Foo::func()
{
    ...
    Bar b;
    b.func2();
}

Здесь func2 может уничтожить вызывающий его объект foo. Поскольку это не очень очевидно, что это может произойти, я хотел бы убедиться, что никакие переменные-члены объекта foo не могут быть доступны после этого вызова, так как я не могу гарантировать, что они все еще действительны. Если этот вызов b является последним вызовом, у меня все отлично, но поскольку я не единственный, кто работает над этим проектом, а уничтожение не очевидно, я хотел бы реализовать что-то, чтобы блокировать использование this после этих вызовов. Есть ли способ реализовать это без полного рефакторинга текущего проекта (поскольку он широко используется в проекте)?

Некоторая справочная информация:

Это приложение Qt UI, которое использует QML для отслеживания стека «экранов» (экран - это QML + соответствующая ему модель C ++). Класс Bar в моем примере - это «менеджер стека», который управляет временем жизни экранов. Это синглтон (я знаю). Класс Foo является моделью C ++ для определенного QML. Функция Foo::func() - это функция, которая может быть вызвана либо из пользовательского ввода в QML, либо из другого системного события. Функция обычно обрабатывает событие, но иногда она может сказать менеджеру экрана удалить один или несколько экранов, которые, в свою очередь, удаляют модель, соответствующую этому экрану (которая может быть вызывающей моделью).

1 Ответ

0 голосов
/ 08 мая 2018

Серьезная проблема проектирования?

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

Но это еще не все: что, если Foo, который вызывал fun2(), был локальным объектом с длительностью автоматического хранения?

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

Альтернативный дизайн с помощью умных указателей

Одним из выходов из этой ситуации может быть создание всех объектов Foo и Bar с использованием фабрик (созданиеубедитесь, что конструктор не является общедоступным), и пусть эти фабрики возвращают shared_ptr.

Это имеет то преимущество, что если объект больше не используется, он уничтожит себя.Это намного чище, чем управлять уничтожением вручную.

Однако вы столкнетесь с несколькими проблемами:

  • Вам придется реорганизовать весь код, чтобы заменить необработанные указатели умными указателями
  • , чтобы избежать бесконечныхв прямом эфире из-за перекрестных ссылок (два объекта держат указатель slart на другой), вам необходимо выяснить, когда использовать shared_ptr (если указатель должен гарантировать, что объект еще жив) и когда использовать weak_ptr(т.е. указатель, который не способствует поддержанию объектов в живых).

Альтернативный дизайн с состояниями

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

Это может быть реализовано:

  • или очень хорошо с шаблоном проектирования состояний (но это может потребовать некоторого обширного рефакторинга),
  • или с простым флагом.Затем он будет проверяться в начале каждой функции-члена и перед доступом к элементам данных, которые следует за вызовом, который потенциально может изменить состояние объекта.Это все еще подвержено ошибкам и требует анализа, но потребует в основном «локальных» изменений и, таким образом, дает большую гибкость для рефакторинга.

Преимущество этого подхода состоит в том, что функция изменения состояния будет очень простой (большинство вещей, которые у вас есть в настоящее время в деструкторе, но избежать их, может быть сделано случайно во второй раз) и сделатьхитрая часть очень хорошо видна.

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

...