Что такое умный указатель, и когда я должен его использовать?

Что такое умный указатель, и когда я должен его использовать?

    14 Solutions collect form web for “Что такое умный указатель, и когда я должен его использовать?”

    Умный указатель – это class, который обертывает указатель C ++ «raw» (или «голый») для управления временем жизни объекта, на который указывают. Существует не один тип интеллектуального указателя, но все они пытаются абстрагировать необработанный указатель на практике.

    Смарт-указатели должны быть предпочтительнее, чем исходные указатели. Если вы считаете, что вам нужно использовать указатели (сначала подумайте, действительно ли вы это делаете), вы обычно хотели бы использовать интеллектуальный указатель, поскольку это может облегчить многие проблемы с необработанными указателями, в основном забывая удалить объект и утечку памяти.

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

    // Need to create the object to achieve some goal MyObject* ptr = new MyObject(); ptr->DoSomething(); // Use the object in some way delete ptr; // Destroy the object. Done with it. // Wait, what if DoSomething() raises an exception...? 

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

     SomeSmartPtr ptr(new MyObject()); ptr->DoSomething(); // Use the object in some way. // Destruction of the object happens, depending // on the policy the smart pointer class uses. // Destruction would happen even if DoSomething() // raises an exception 

    Простейшая используемая политика включает в себя область объекта оболочки интеллектуального указателя, например, реализована boost::scoped_ptr или std::unique_ptr .

     void f() { { boost::scoped_ptr ptr(new MyObject()); ptr->DoSomethingUseful(); } // boost::scopted_ptr goes out of scope -- // the MyObject is automatically destroyed. // ptr->Oops(); // Compile error: "ptr" not defined // since it is no longer in scope. } 

    Обратите внимание, что экземпляры scoped_ptr не могут быть скопированы. Это предотвращает удаление указателя несколько раз (неправильно). Тем не менее, вы можете передавать ссылки на другие функции, которые вы вызываете.

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

    Более сложная политика интеллектуальных указателей включает ссылку, подсчитывающую указатель. Это позволяет скопировать указатель. Когда последняя «ссылка» на объект уничтожается, объект удаляется. Эта политика реализуется boost::shared_ptr и std::shared_ptr .

     void f() { typedef std::shared_ptr MyObjectPtr; // nice short alias MyObjectPtr p1; // Empty { MyObjectPtr p2(new MyObject()); // There is now one "reference" to the created object p1 = p2; // Copy the pointer. // There are now two references to the object. } // p2 is destroyed, leaving one reference to the object. } // p1 is destroyed, leaving a reference count of zero. // The object is deleted. 

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

    Существует один недостаток ссылок на подсчитанные указатели – возможность создания ссылки:

     // Create the smart pointer on the heap MyObjectPtr* pp = new MyObjectPtr(new MyObject()) // Hmm, we forgot to destroy the smart pointer, // because of that, the object is never destroyed! 

    Другая возможность заключается в создании круговых ссылок:

     struct Owner { boost::shared_ptr other; }; boost::shared_ptr p1 (new Owner()); boost::shared_ptr p2 (new Owner()); p1->other = p2; // p1 references p2 p2->other = p1; // p2 references p1 // Oops, the reference count of of p1 and p2 never goes to zero! // The objects are never destroyed! 

    Чтобы обойти эту проблему, как Boost, так и C ++ 11 определили weak_ptr чтобы определить слабую (бесчисленную) ссылку на shared_ptr .


    ОБНОВИТЬ

    Этот ответ довольно старый, и поэтому описывает, что было «хорошим» в то время, что было умными указателями, предоставляемыми библиотекой Boost. Начиная с C ++ 11, стандартная библиотека предоставила достаточные типы интеллектуальных указателей, поэтому вам следует использовать std::unique_ptr , std::shared_ptr и std::weak_ptr .

    Существует также std::auto_ptr . Он очень похож на указатель с областью действия, за исключением того, что он также имеет «специальную» опасную способность, которую можно скопировать, что также неожиданно передает право собственности! Он устарел в новейших стандартах, поэтому вы не должны его использовать. Вместо этого используйте std::unique_ptr .

     std::auto_ptr p1 (new MyObject()); std::auto_ptr p2 = p1; // Copy and transfer ownership. // p1 gets set to empty! p2->DoSomething(); // Works. p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception. 

    Вот простой ответ на эти дни современного C ++:

    • Что такое умный указатель?
      Это тип значения, который можно использовать как указатель, но обеспечивает дополнительную функцию автоматического управления памятью: когда указатель больше не используется, память, на которую он указывает, освобождается (см. Также более подробное определение в Википедии ).
    • Когда я должен использовать его?
      В коде, который включает в себя отслеживание собственности на кусок памяти, выделение или де-распределение; умный указатель часто спасает вас от необходимости делать это явно.
    • Но какой умный указатель я должен использовать в каких случаях?
      • Используйте std::unique_ptr если вы не собираетесь хранить несколько ссылок на один и тот же объект. Например, используйте его для указателя на память, который получает выделение при вводе некоторой области действия и не выделяется при выходе из области.
      • Используйте std::shared_ptr когда вы хотите ссылаться на свой объект из нескольких мест – и не хотите, чтобы он был выделен до тех пор, пока все эти ссылки не исчезнут.
      • Используйте std::weak_ptr когда вы хотите ссылаться на свой объект из нескольких мест – для тех ссылок, для которых это нормально игнорировать и освобождать (так что они просто заметят, что объект исчез, когда вы пытаетесь разыменовать).
      • Не используйте boost:: умные указатели или std::auto_ptr за исключением особых случаев, которые вы можете прочитать, если нужно.
    • Эй, я не спрашивал, какой из них использовать!
      Ах, но ты действительно хотел, признай это.
    • Итак, когда я должен использовать регулярные указатели?
      В основном в коде, который не обращает внимания на владение памятью. Обычно это будет в функциях, которые получают указатель откуда-то еще и не выделяют, не выделяют или не хранят копию указателя, который превышает их выполнение.

    Смарт-указатель – это указательный тип с некоторыми дополнительными функциями, например, автоматическое освобождение памяти, подсчет ссылок и т. Д.

    Маленькое введение доступно на странице Smart Pointers – What, Why, Which? ,

    Одним из простых типов смарт-указателей является std::auto_ptr (глава 20.4.5 стандарта C ++), который позволяет автоматически освобождать память, когда она выходит за пределы области действия и которая более надежна, чем простое использование указателя при выбросах исключений, хотя и меньше гибкий.

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

    Тема подробно освещена в книге «C ++ Templates: The Complete Guide» Дэвида Вандевоорда, Николая М. Йосуттиса , глава Глава 20. Умные указатели. Некоторые затронутые темы:

    • Защита от исключений
    • Держатели, (обратите внимание, std :: auto_ptr – реализация такого типа умного указателя)
    • Инициализация ресурсов (это часто используется для безопасного управления ресурсами на C ++)
    • Ограничения владельца
    • Подсчет ссылок
    • Параллельный доступ к счетчикам
    • Разрушение и освобождение

    Определения, предоставленные Крисом, Сергдеем и Ллодом, верны. Я предпочитаю более простое определение, но просто чтобы моя жизнь была простой: умный указатель – это просто class, который перегружает операторы -> и * . Это означает, что ваш объект семантически выглядит как указатель, но вы можете заставить его делать более крутые вещи, в том числе подсчет ссылок, автоматическое уничтожение и т. Д. shared_ptr и auto_ptr в большинстве случаев являются достаточными, но приходят вместе со своим набором небольших отличий.

    Умный указатель похож на обычный (типизированный) указатель, например «char *», за исключением случаев, когда сам указатель выходит из области видимости, и то, на что он указывает, также удаляется. Вы можете использовать его так же, как и обычный указатель, используя «->», но не если вам нужен фактический указатель на данные. Для этого вы можете использовать «& * ptr».

    Это полезно для:

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

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

    Вы не можете использовать интеллектуальный указатель, если:

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

    Смотрите также:

    • garbage collection .
    • Этот вопрос переполнения стека относительно владения данными

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

    Наиболее часто используемыми интеллектуальными указателями являются std::tr1::shared_ptr (или boost::shared_ptr ) и, реже, std::auto_ptr . Я рекомендую регулярно использовать shared_ptr .

    shared_ptr очень универсален и имеет большое разнообразие сценариев удаления, в том числе случаи, когда объекты должны быть «переданы через границы DLL» (общий случай кошмара, если между вашим кодом и DLL используются разные libc s).

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

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

    Например, Boost предоставляет следующие реализации интеллектуальных указателей:

    • shared_ptr является указателем на T используя счетчик ссылок, чтобы определить, когда объект больше не нужен.
    • scoped_ptr – указатель, автоматически удаляемый при выходе из области видимости. Никакое назначение невозможно.
    • intrusive_ptr – другой указатель подсчета ссылок. Он обеспечивает лучшую производительность, чем shared_ptr , но требует, чтобы тип T предоставлял свой собственный механизм подсчета ссылок.
    • weak_ptr – слабый указатель, работающий совместно с shared_ptr чтобы избежать циклических ссылок.
    • shared_array подобен shared_ptr , но для массивов T
    • scoped_array похож на scoped_ptr , но для массивов T

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

    Кроме того, стандартная библиотека C ++ предоставляет три интеллектуальных указателя; std::unique_ptr для уникального владельца, std::shared_ptr для совместного использования и std::weak_ptr . std::auto_ptr существует в C ++ 03, но теперь он устарел.

    Вот ссылка для похожих ответов: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

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

    Пример:

     template  class smart_pointer { public: smart_pointer(); // makes a null pointer smart_pointer(const X& x) // makes pointer to copy of x X& operator *( ); const X& operator*( ) const; X* operator->() const; smart_pointer(const smart_pointer  &); const smart_pointer  & operator =(const smart_pointer&); ~smart_pointer(); private: //... }; 

    Этот class реализует интеллектуальный указатель на объект типа X. Сам объект находится в куче. Вот как его использовать:

     smart_pointer  p= employee("Harris",1333); 

    Как и другие перегруженные операторы, p будет вести себя как обычный указатель,

     cout< <*p; p->raise_salary(0.5); 

    http://en.wikipedia.org/wiki/Smart_pointer

    В информатике интеллектуальный указатель представляет собой абстрактный тип данных, который имитирует указатель, предоставляя дополнительные функции, такие как автоматическая assembly мусора или проверка границ. Эти дополнительные функции предназначены для уменьшения ошибок, вызванных неправильным использованием указателей при сохранении эффективности. Умные указатели обычно отслеживают объекты, указывающие на них, для управления памятью. Неправильное использование указателей является основным источником ошибок: постоянное распределение, освобождение и ссылки, которые должны выполняться программой, написанной с помощью указателей, очень вероятно, что произойдет утечка памяти. Умные указатели пытаются предотвратить утечку памяти, автоматически перераспределяя ресурс: когда указатель на объект (или последний в серии указателей) уничтожается, например, потому что он выходит из области видимости, заостренный объект также уничтожается.

    Пусть T – class в этом учебнике Указатели на C ++ можно разделить на 3 типа:

    1) Исходные указатели :

     T a; T * _ptr = &a; 

    Они хранят адрес памяти в месте в памяти. Следует использовать с осторожностью, так как программы становятся сложными для отслеживания.

    Указатели с данными или адресом const {Прочитать назад}

     T a ; const T * ptr1 = &a ; T const * ptr1 = &a ; 

    Указатель на тип данных T, который является константой. Это означает, что вы не можете изменить тип данных с помощью указателя. т.е. *ptr1 = 19 ; не будет работать. Но вы можете переместить указатель. т.е. ptr1++ , ptr1-- ; и т. д. Прочитать назад: указатель на тип T, который является const

      T * const ptr2 ; 

    Указатель const для типа данных T. Это означает, что вы не можете переместить указатель, но вы можете изменить значение, на которое указывает указатель. т.е. *ptr2 = 19 будет работать, но ptr2++ ; ptr2-- ptr2++ ; ptr2-- т. д. не будет работать. Прочитать назад: const указатель на тип T

     const T * const ptr3 ; 

    Контр-указатель на тип данных const. Это означает, что вы не можете либо переместить указатель, либо не можете изменить указатель типа данных как указатель. т.е. ptr3-- ; ptr3++ ; *ptr3 = 19; не будет работать

    3) Умные указатели : { #include }

    Общий указатель :

      T a ; //shared_ptr shptr(new T) ; not recommended but works shared_ptr shptr = make_shared(); // faster + exception safe std::cout < < shptr.use_count() ; // 1 // gives the number of " things " pointing to it. T * temp = shptr.get(); // gives a pointer to object // shared_pointer used like a regular pointer to call member functions shptr->memFn(); (*shptr).memFn(); // shptr.reset() ; // frees the object pointed to be the ptr shptr = nullptr ; // frees the object shptr = make_shared() ; // frees the original object and points to new object 

    Реализован с использованием подсчета ссылок, чтобы отслеживать, сколько «вещей» указывает на объект, на который указывает указатель. Когда этот счетчик переходит в 0, объект автоматически удаляется, т. Е. Объект objected удаляется, когда весь share_ptr, указывающий на объект, выходит из области видимости. Это избавляет от головной боли, связанной с необходимостью удаления объектов, которые вы выделили с помощью нового.

    Слабый указатель: помогает справиться с циклической ссылкой, которая возникает при использовании общего указателя. Если у вас есть два объекта, на которые указывают два общих указателя, и есть внутренний общий указатель, указывающий на общий общий указатель, тогда будет циклическая ссылка, и объект не будет быть удалены, если общие указатели выходят за frameworks. Чтобы решить эту проблему, измените внутренний член с shared_ptr на weak_ptr. Примечание. Чтобы получить доступ к элементу, на который указывает слабый указатель, используйте lock (), это возвращает weak_ptr.

     T a ; shared_ptr shr = make_shared() ; weak_ptr wk = shr ; // initialize a weak_ptr from a shared_ptr wk.lock()->memFn() ; // use lock to get a shared_ptr // ^^^ Can lead to exception if the shared ptr has gone out of scope if(!wk.expired()) wk.lock()->memFn() ; // Check if shared ptr has gone out of scope before access 

    См .: Когда используется std :: weak_ptr?

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

     unique_ptr uptr(new T); uptr->memFn(); //T * ptr = uptr.release(); // uptr becomes null and object is pointed to by ptr uptr.reset() ; // deletes the object pointed to by uptr 

    Чтобы изменить объект, на который указывает уникальный ptr, используйте семантику перемещения

     unique_ptr uptr1(new T); unique_ptr uptr2(new T); uptr2 = std::move(uptr1); // object pointed by uptr2 is deleted and // object pointed by uptr1 is pointed to by uptr2 // uptr1 becomes null 

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

    См .: Каковы различия между переменной указателя и ссылочной переменной в C ++?

     r-value reference : reference to a temporary object l-value reference : reference to an object whose address can be obtained const reference : reference to a data type which is const and cannot be modified 

    Ссылка: https://www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ Спасибо Андре за то, что он указал на этот вопрос.

    Умный указатель – это class, shell нормального указателя. В отличие от обычных указателей, жизненный круг смарт-точки основан на подсчете ссылок (сколько времени назначается объекту интеллектуального указателя). Поэтому всякий раз, когда интеллектуальный указатель присваивается другому, внутренний счетчик ссылок плюс плюс. И всякий раз, когда объект выходит из сферы действия, счетчик ссылок минус минус.

    Автоматический указатель, хотя выглядит похожим, полностью отличается от умного указателя. Это удобный class, который освобождает ресурс всякий раз, когда объект автоматического указателя выходит из переменной области. В какой-то степени он делает указатель (для динамически распределенной памяти) работает аналогично переменной стека (статически выделяется во время компиляции).

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

    Вы можете очень хорошо использовать этот указатель так же, как любое распределение работает в Java. В java Garbage Collector делает трюк, в то время как в Smart Pointers трюк выполняется Destructors.

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

    Среди прочего (хорошо объясняется в других ответах) использование умного указателя – это возможное решение. Как мы используем абстрактный class как возвращаемый тип функции? который был отмечен как дубликат этого вопроса. Тем не менее, первый вопрос, задающий вопрос об искушении указать абстрактный (или фактически, какой-либо) базовый class как возвращаемый тип в C ++, «что вы на самом деле имеете в виду?». Существует хорошее обсуждение (с дальнейшими ссылками) идиоматического объектно-ориентированного программирования на C ++ (и как это отличается от других языков) в документации библиотеки контейнеров ускорителя . Таким образом, на C ++ вы должны думать о собственности. Какие интеллектуальные указатели вам помогут, но не единственное решение или всегда полное решение (они не дают вам полиморфной копии) и не всегда являются решением, которое вы хотите разоблачить в своем интерфейсе (а функция return звучит ужасно как интерфейс). Например, может быть достаточно вернуть ссылку. Но во всех этих случаях (умный указатель, контейнер указателя или просто возврат ссылки) вы изменили возврат из значения в некоторую форму ссылки . Если вам действительно нужна копия, вам может потребоваться добавить более подробную «идиому» шаблона или перейти за идиоматический (или иначе) ООП на C ++ к более универсальному polymorphismу с использованием библиотек, таких как Adobe Poly или Boost.TypeErasure .

    Я хотел бы добавить еще один момент к вышеуказанному вопросу, умный указатель std :: shared_ptr не имеет оператора индекса и не поддерживает арифметику ponter, мы можем использовать get () для получения встроенного указателя.

    Давайте будем гением компьютера.