Вопрос

В проекте на C ++, использующем интеллектуальные указатели, такие как boost::shared_ptr, какова хорошая философия дизайна в отношении использования "this"?

Подумайте об этом:

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

  • Нестатические члены класса по своей сути используют this указатель.Это необработанный указатель, и он не может быть изменен.

Если я когда-нибудь сохраню this в другой переменной или передаю ее другой функции, которая потенциально может сохранить ее на потом или привязать к обратному вызову, я создаю ошибки, которые появляются, когда кто-либо решает создать общий указатель на мой класс.

Учитывая это, когда мне когда-нибудь будет уместно явно использовать this указатель? Существуют ли парадигмы дизайна, которые могут предотвратить ошибки, связанные с этим?

Это было полезно?

Решение

Хотя у меня нет общего ответа или какой-то идиомы, есть boost::enable_shared_from_this .Это позволяет вам получить shared_ptr, управляющий объектом, который уже управляется shared_ptr.Поскольку в функции-члене у вас нет ссылки на те, которые управляют shared_ptr, enable_shared_ptr позволяет вам получить экземпляр shared_ptr и передать его, когда вам нужно передать указатель this.

Но это не решит проблему прохождения this изнутри конструктора, поскольку на тот момент ни один shared_ptr еще не управляет вашим объектом.

Другие советы

Неправильный вопрос

В проекте на C ++, использующем интеллектуальные указатели

На самом деле проблема не имеет ничего общего с умными указателями.Речь идет только о собственности.

Умные указатели - это всего лишь инструменты

Они ничего не меняют в отношении концепции собственности, особенно. необходимость иметь четко определенное право собственности в вашей программе, тот факт, что право собственности может быть добровольно передано, но не может быть принято клиентом.

Вы должны понимать, что интеллектуальные указатели (также блокировки и другие объекты RAII) представляют значение и связь с этим значением одновременно.A shared_ptr является ссылкой на объект и устанавливает связь:объект не должен быть уничтожен до этого shared_ptr, и когда это shared_ptr уничтожается, если это последний псевдоним этого объекта, объект должен быть немедленно уничтожен.(unique_ptr может рассматриваться как частный случай shared_ptr где по определению существует нулевое сглаживание, так что unique_ptr это всегда последний псевдоним объекта.)

Почему вы должны использовать умные указатели

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

Умные указатели могут выражать только четко определенный дизайн, они не отменяют необходимости определять владельца.Напротив, сборка мусора избавляет от необходимости определять, кто отвечает за освобождение памяти.(Но не отменяйте необходимости определять, кто несет ответственность за очистку других ресурсов.)

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

А как насчет необработанных указателей?

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

Вот почему многие программисты на C ++ считают, что использование необработанных указателей вместо адекватного интеллектуального указателя является неполноценный:потому что это менее выразительно (я специально избегал терминов "хороший" и "плохой").Я полагаю, что ядро Linux было бы более читабельным с несколькими объектами C ++ для выражения взаимосвязей.

Вы можете реализовать определенный дизайн с интеллектуальными указателями или без них.Реализация, которая надлежащим образом использует smart pointer, будет считаться превосходной многими программистами на C ++.

Ваш настоящий вопрос

Какова хорошая философия дизайна в проекте на C ++ относительно использования "этого"?

Это ужасно расплывчато.

Опасно хранить необработанный указатель для последующего использования.

Зачем вам нужен указатель для последующего использования?

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

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

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

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

Очевидно, что решение состоит в том, чтобы либо:

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

Только в первом случае вы могли бы в конечном итоге получить интеллектуальный указатель в реализации класса.

Источник вашей проблемы

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

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

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

Одним из примеров правильного использования является return *this; в таких функциях, как operator++() и operator<<().

Когда вы используете класс интеллектуального указателя, вы правы в том, что прямое раскрытие опасно "this".Есть несколько классов указателей, связанных с boost::shared_ptr<T> это может быть полезно:

  • boost::enable_shared_from_this<T>
    • Предоставляет возможность, чтобы объект возвращал общий указатель на себя, который использует те же данные подсчета ссылок, что и существующий общий указатель на объект
  • boost::weak_ptr<T>
    • Работает рука об руку с общими указателями, но не содержит ссылки на объект.Если все общие указатели исчезнут и объект будет освобожден, слабый указатель сможет сообщить, что объект больше не существует, и вернет вам NULL вместо указателя на недопустимую память.Вы можете использовать слабые указатели, чтобы получить общие указатели на действительный объект с подсчетом ссылок.

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

Если вам нужно использовать this, просто используйте его явно.Интеллектуальные указатели переносят только указатели на объекты, которыми они владеют , - либо эксклюзивно (unique_ptr) или общим образом (shared_ptr).

Лично мне нравится использовать это указатель при обращении к переменным-членам класса.Например:

void foo::bar ()
{
    this->some_var += 7;
}

Это всего лишь безобидный вопрос стиля.Кому-то это нравится, кому-то нет.

Но используя это указатель на любую другую вещь, скорее всего, вызовет проблемы.Если вам действительно нужно делать с ним необычные вещи, вам действительно следует пересмотреть свой дизайн.Однажды я видел некоторый код, который в конструкторе класса присваивал указатель this другому указателю, хранящемуся где-то в другом месте!Это просто безумие, и я никогда не могу придумать причину для этого.Кстати, весь код представлял собой огромный беспорядок.

Можете ли вы сказать нам, что именно вы хотите сделать с указателем?

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top