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

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

вопрос задан 20.09.2008
Alex Reynolds
66007 репутация

14 ответов


  • 1662 рейтинг

    Интеллектуальный указатель - это класс, который оборачивает «необработанный» (или «голый») указатель 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 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.
    
    ответ дан Lloyd, с репутацией 18655, 20.09.2008
  • 178 рейтинг

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

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

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

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

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

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

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

    • Защита от исключений
    • Holders, (обратите внимание, std :: auto_ptr - реализация такого типа интеллектуального указателя)
    • Получение ресурсов - инициализация (Это часто используется для безопасного управления исключениями в C ++)
    • Ограничения держателя
    • Подсчет ссылок
    • Параллельный доступ к счетчику
    • Уничтожение и удаление
    ответ дан sergtk, с репутацией 6511, 20.09.2008
  • 33 рейтинг

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

    ответ дан Sridhar Iyer, с репутацией 1901, 20.09.2008
  • 26 рейтинг

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

    Полезно для:

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

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

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

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

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

    ответ дан markets, с репутацией 6023, 20.09.2008
  • 14 рейтинг

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

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

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

    ответ дан Chris Jester-Young, с репутацией 178089, 20.09.2008
  • 14 рейтинг

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

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

    Например, 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, но сейчас устарел.

    ответ дан Saqlain, с репутацией 12799, 12.03.2013
  • 9 рейтинг

    Вот ссылка для похожих ответов: http: // sickprogrammersarea. Blogspot. в / 2014/03 / технико-интервью-вопросы-на-c_6. HTML

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

    Пример:

    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:
                   //...
    };
    

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

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

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

    cout<<*p;
    p->raise_salary(0.5);
    
    ответ дан Santosh, с репутацией 633, 7.03.2014
  • 7 рейтинг

    http: // en. википедия. org / wiki / Smart_pointer

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

    ответ дан Jorge Ferreira, с репутацией 73000, 20.09.2008
  • 3 рейтинг

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

    1) Сырые указатели :

    T a;  
    T * _ptr = &a; 
    

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

    Указатели с постоянными данными или адресом {Читать в обратном направлении}

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

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

      T * const ptr2 ;
    

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

    const T * const ptr3 ; 
    

    Постоянный указатель на постоянный тип данных T. Это означает, что вы не можете ни переместить указатель, ни изменить указатель типа данных на указатель. т.е. 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, объект автоматически удаляется, т.е. объект удаляется, когда все shared_ptr, указывающие на объект, выходит из области видимости. Это избавляет от головной боли необходимости удалять объекты, которые вы выделили с помощью new.

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

    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 
    

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

    См .: Каковы различия между переменной указателя и ссылочной переменной в 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. ru / channel / UCEOGtxYTB6vo6MQ-WQ9W_nQ Спасибо Андре за указание на этот вопрос.

    ответ дан nnrales, с репутацией 439, 3.03.2016
  • 2 рейтинг

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

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

    ответ дан Trombe, с репутацией 111, 12.06.2017
  • 1 рейтинг

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

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

    ответ дан Daksh, с репутацией 3613, 7.11.2016
  • 0 рейтинг

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

    ответ дан suresh m, с репутацией 111, 7.02.2018
  • 0 рейтинг

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

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

    ответ дан da77a, с репутацией 23, 26.01.2018