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

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

Ответ 1

UPDATE

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

Был также std::auto_ptr. Это было очень похоже на указатель с областью видимости, за исключением того, что он также имел "особую" опасную способность для копирования, которая также неожиданно передает право собственности.
Он устарел в C++ 11 и удален в C++ 17, поэтому его не следует использовать.

std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> 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++, чтобы управлять временем жизни объекта, на который указывает указатель. Не существует единого типа интеллектуального указателя, но все они пытаются абстрагировать необработанный указатель практическим способом.

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

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

// 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<MyObject> 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()
{
    {
       std::unique_ptr<MyObject> ptr(new MyObject());
       ptr->DoSomethingUseful();
    } // ptr goes out of scope -- 
      // the MyObject is automatically destroyed.

    // ptr->Oops(); // Compile error: "ptr" not defined
                    // since it is no longer in scope.
}

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

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

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

void f()
{
    typedef std::shared_ptr<MyObject> 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 {
   std::shared_ptr<Owner> other;
};

std::shared_ptr<Owner> p1 (new Owner());
std::shared_ptr<Owner> 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.

Ответ 2

Вот простой ответ для современных дней C++:

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

Ответ 3

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

Небольшое вступление доступно на странице Smart Pointers - Что, Почему, Что? ,

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

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

Тема подробно рассмотрена в книге Дэвида Вандевурда "Николас М. Хосуттис", глава 20 " Шаблоны C++: Полное руководство", глава 20 "Умные указатели". Некоторые темы:

Ответ 4

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

Ответ 5

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

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

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

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

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

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

См. также:

Ответ 6

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

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

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

Ответ 7

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

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

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

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

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

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

Ответ 8

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

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

Пример:

template <class X>
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 <X> &);
               const smart_pointer <X> & operator =(const smart_pointer<X>&);
               ~smart_pointer();
          private:
               //...
};

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

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

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

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

Ответ 9

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

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

Ответ 10

Пусть T - класс в этом учебнике Указатели на С++ можно разделить на 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-- и т.д. не будет работать. Прочитать назад: const указатель на тип T

const T * const ptr3 ; 

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

3) Интеллектуальные указатели: {#include <memory>}

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

  T a ; 
     //shared_ptr<T> shptr(new T) ; not recommended but works 
     shared_ptr<T> shptr = make_shared<T>(); // 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<T>() ; // frees the original object and points to new object

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

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

T a ; 
shared_ptr<T> shr = make_shared<T>() ; 
weak_ptr<T> 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<T> 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<T> uptr1(new T);
unique_ptr<T> 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 

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

Смотрите: В чем разница между переменной-указателем и ссылочной переменной в С++?

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 Спасибо Андре за то, что он указал на этот вопрос.

Ответ 11

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

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

Ответ 12

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

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

Ответ 13

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

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

Ответ 14

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