Умные указатели + "this" считаются вредными?

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

Рассмотрим, что:

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

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

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

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

Ответ 1

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

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

Ответ 2

Неверный вопрос

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

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

Умные указатели - это просто инструменты

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

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

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

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

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

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

Как насчет сырых указателей?

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

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

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

Ваш реальный вопрос

В проекте С++, что является хорошей философией дизайна относительно использования "this"?

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

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

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

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

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

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

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

Решение, очевидно, должно быть либо:

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

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

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

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

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

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

Мне лично нравится использовать указатель this при доступе к переменным-членам класса. Например:

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

Это просто безобидный вопрос о стиле. Некоторым людям это нравится, некоторые люди этого не делают.

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

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

Ответ 7

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

Ответ 8

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