умные указатели + «это» считается вредным? - PullRequest
11 голосов
/ 19 декабря 2008

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

Считайте, что:

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

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

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

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

Ответы [ 8 ]

13 голосов
/ 26 октября 2011

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Решение, очевидно, либо:

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

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

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

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

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

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

12 голосов
/ 19 декабря 2008

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

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

5 голосов
/ 20 декабря 2008

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

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

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

5 голосов
/ 20 декабря 2008

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

1 голос
/ 19 января 2009

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

1 голос
/ 20 декабря 2008

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

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

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

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

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

1 голос
/ 19 декабря 2008

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

0 голосов
/ 20 декабря 2008

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

...