什么是智能指针,什么时候应该使用?

c++ pointers c++11 smart-pointers c++-faq

516471 观看

13回复

什么是智能指针,什么时候应该使用?

作者: Alex Reynolds 的来源 发布者: 2019 年 7 月 24 日

回应 (13)


1802

决定

UPDATE

这个答案相当陈旧,因此描述了当时的“好”,这是Boost库提供的智能指针。从C ++ 11开始,标准库提供了足够的智能指针类型,所以你应该喜欢使用std::unique_ptrstd::shared_ptrstd::weak_ptr

还有std::auto_ptr。它非常像一个范围指针,除了它还具有“特殊”危险的复制能力 - 这也意外地转移了所有权!它在最新标准中已弃用,因此您不应使用它。使用std::unique_ptr来代替。

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当你想要将对象的生命周期与特定的代码块联系起来,或者如果你把它作为成员数据嵌入到另一个对象中时,s是有用的,那就是另一个对象的生命周期。该对象一直存在,直到退出包含的代码块,或者直到包含的对象本身被销毁为止。

更复杂的智能指针策略涉及引用计数指针。这确实允许复制指针。当销毁对象的最后一个“引用”时,将删除该对象。该政策由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定义a的弱(不计数)引用shared_ptr

作者: Lloyd 发布者: 20.09.2008 12:48

248

这是现代C ++的简单答案:

  • 什么是智能指针?
    它是一种类型,其值可以像指针一样使用,但它提供了自动内存管理的附加功能:当智能指针不再使用时,它指向的内存被释放(请参阅维基百科上更详细的定义)。
  • 我应该什么时候使用?
    在代码中涉及跟踪一块内存的所有权,分配或取消分配; 智能指针通常可以节省您明确执行这些操作的需要。
  • 但是我应该在哪些情况下使用哪个智能指针?
    • 使用std::unique_ptr时,你不打算持有到同一对象的多个引用。例如,将它用作指向内存的指针,该指针在进入某个范围时分配,并在退出范围时取消分配。
    • std::shared_ptr当你想要从多个地方引用你的对象时使用- 并且不希望你的对象被解除分配,直到所有这些引用都消失了。
    • std::weak_ptr当你想要从多个地方引用你的对象时使用- 对于那些可以忽略和解除分配的引用(所以当你尝试取消引用时它们只会注意到对象已经消失)。
    • 不要使用boost::智能指针或std::auto_ptr在特殊情况下使用,如果必须,可以阅读。
  • 嘿,我没问过哪一个用!
    啊,但你真的很想,承认这一点。
  • 那我什么时候应该使用常规指针呢?
    主要是代码中没有内存所有权。这通常是在从其他地方获取指针并且不分配或解除分配的函数中,并且不存储比其执行更长的指针的副本。
作者: einpoklum 发布者: 09.05.2015 07:06

107

智能指针是类似指针的类型,具有一些附加功能,例如自动内存释放,引用计数等。

小型介绍可在页面上找到智能指针 - 什么,为什么,哪个?

其中一个简单的智能指针类型是std::auto_ptr(C ++标准的第20.4.5节),它允许在超出范围时自动释放内存,并且在抛出异常时比简单指针使用更强大,尽管不太灵活。

另一个方便的类型是boost::shared_ptr实现引用计数,并在没有对象的引用时自动释放内存。这有助于避免内存泄漏,并且易于使用来实现RAII

题目在David Vandevoorde,Nicolai M. Josuttis,第20章,智能指针的书“C ++模板:完整指南”中有详细介绍。涉及的一些主题:

作者: sergtk 发布者: 20.09.2008 12:32

38

Chris,Sergdev和Llyod提供的定义是正确的。我更喜欢一个更简单的定义,只是为了让我的生活更简单:智能指针只是一个重载->*运算符的类。这意味着你的对象在语义上看起来像一个指针,但你可以让它做更酷的事情,包括引用计数,自动破坏等等 shared_ptrauto_ptr并且在大多数情况下都足够了,但是伴随着它们自己的一组小特性。

作者: Sridhar Iyer 发布者: 20.09.2008 01:53

28

智能指针就像常规(类型)指针,如“char *”,除非指针本身超出范围,否则它指向的内容也会被删除。您可以像使用常规指针一样使用“ - >”,但如果您需要实际的数据指针则不能。为此,您可以使用“&* ptr”。

它对以下内容很有用:

  • 必须使用new分配的对象,但是您希望与该堆栈上的内容具有相同的生命周期。如果将对象分配给智能指针,则在程序退出该功能/块时将删除它们。

  • 类的数据成员,以便在删除对象时删除所有拥有的数据,在析构函数中没有任何特殊代码(您需要确保析构函数是虚拟的,这几乎总是一件好事) 。

您可能希望在以下情况下使用智能指针:

  • ...指针实际上不应该拥有数据...即,当您只是使用数据时,但是您希望它能够在您引用它的函数中存活。
  • ......智能指针本身不会在某些时候被破坏。您不希望它位于永远不会被破坏的内存中(例如在动态分配但不会被显式删除的对象中)。
  • ......两个智能指针可能指向相同的数据。(然而,有更聪明的指针可以处理...这称为引用计数。)

也可以看看:

作者: markets 发布者: 20.09.2008 12:13

16

大多数类型的智能指针都会为您处理指针对象的处理。它非常方便,因为您不必再​​考虑手动处理对象了。

最常用的智能指针是std::tr1::shared_ptr(或boost::shared_ptr),不太常见std::auto_ptr。我建议经常使用shared_ptr

shared_ptr是非常通用的,处理各种各样的处理场景,包括对象需要“跨越DLL边界”的情况(如果libc在代码和DLL之间使用不同的s ,常见的噩梦案例)。

作者: Chris Jester-Young 发布者: 20.09.2008 12:14

16

智能指针是一个像指针一样的对象,但另外还提供对构造,销毁,复制,移动和解除引用的控制。

人们可以实现自己的智能指针,但许多库也提供智能指针实现,每个实现都有不同的优点和缺点。

例如,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的文档。

此外,C ++标准库提供了三个智能指针; std::unique_ptr独特的所有权,std::shared_ptr共享所有权和std::weak_ptrstd::auto_ptr存在于C ++ 03中但现已弃用。

作者: Saqlain 发布者: 12.03.2013 09:51

10

以下是类似答案的链接:http//sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

智能指针是一种对象,其行为,外观和感觉类似于普通指针,但提供更多功能。在C ++中,智能指针实现为封装指针和覆盖标准指针运算符的模板类。与常规指针相比,它们具有许多优点。保证将它们初始化为空指针或指向堆对象的指针。检查通过空指针的间接方向。不需要删除。当指向它们的最后一个指针消失时,对象会自动释放。这些智能指针的一个重要问题是,与常规指针不同,它们不尊重继承。智能指针对多态代码没有吸引力。下面给出了智能指针实现的示例。

例:

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

This class implement a smart pointer to an object of type X. The object itself is located on the heap. Here is how to use it:

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

Like other overloaded operators, p will behave like a regular pointer,

cout<<*p;
p->raise_salary(0.5);
作者: Santosh 发布者: 07.03.2014 09:03

8

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

In computer science, a smart pointer is an abstract data type that simulates a pointer while providing additional features, such as automatic garbage collection or bounds checking. These additional features are intended to reduce bugs caused by the misuse of pointers while retaining efficiency. Smart pointers typically keep track of the objects that point to them for the purpose of memory management. The misuse of pointers is a major source of bugs: the constant allocation, deallocation and referencing that must be performed by a program written using pointers makes it very likely that some memory leaks will occur. Smart pointers try to prevent memory leaks by making the resource deallocation automatic: when the pointer to an object (or the last in a series of pointers) is destroyed, for example because it goes out of scope, the pointed object is destroyed too.

作者: Jorge Ferreira 发布者: 20.09.2008 12:12

5

Let T be a class in this tutorial Pointers in C++ can be divided into 3 types :

1) Raw pointers :

T a;  
T * _ptr = &a; 

They hold a memory address to a location in memory. Use with caution , as programs become complex hard to keep track.

Pointers with const data or address { Read backwards }

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

Pointer to a data type T which is a const. Meaning you cannot change the data type using the pointer. ie *ptr1 = 19 ; will not work. But you can move the pointer. ie ptr1++ , ptr1-- ; etc will work. Read backwards : pointer to type T which is const

  T * const ptr2 ;

A const pointer to a data type T . Meaning you cannot move the pointer but you can change the value pointed to by the pointer. ie *ptr2 = 19 will work but ptr2++ ; ptr2-- etc will not work. Read backwards : const pointer to a type T

const T * const ptr3 ; 

指向const数据类型T的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时,将自动删除对象,即当指向对象的所有share_ptr超出范围时,将删除对象。这消除了必须删除使用new分配的对象的麻烦。

弱指针: 帮助处理使用共享指针时出现的循环引用如果有两个共享指针指向两个对象,并且有一个指向彼此共享指针的内部共享指针则会有一个循环引用,并且该对象不会当共享指针超出范围时删除。要解决此问题,请将内部成员从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指向的对象,请使用move语义

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指针,即一个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.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ 感谢Andre指出这个问题。

作者: nnrales 发布者: 03.03.2016 12:58

3

智能指针是一个类,是普通指针的包装器。与普通指针不同,智能点的生命周期基于引用计数(智能指针对象分配的时间)。因此,每当智能指针被分配给另一个时,内部引用计数加上加号。每当对象超出范围时,引用计数减去负数。

自动指针虽然看起来很相似,但与智能指针完全不同。每当自动指针对象超出变量范围时,它就是一个方便的类来释放资源。在某种程度上,它使得指针(动态分配的内存)的工作方式类似于堆栈变量(在编译时静态分配)。

作者: Trombe 发布者: 12.06.2017 11:23

2

Smart Pointers是您不必担心内存分配,资源共享和传输的地方。

您可以使用与Java中的任何分配类似的方式使用这些指针。在Java中,垃圾收集器可以解决问题,而在Smart Pointers中,诀窍是由Destructors完成的。

作者: Daksh 发布者: 07.11.2016 04:07

1

现有答案很好,但不包括当智能指针不是您要解决的问题的(完整)答案时要做什么。

除了其他事情(在其他答案中解释得很好)使用智能指针是一个可能的解决方案我们如何使用抽象类作为函数返回类型?已被标记为此问题的副本。但是,第一个问题是,是否试图在C ++中将抽象(或实际上是任意)基类指定为返回类型是“你真正的意思是什么?”。在boost指针容器库的文档中,对C ++中的惯用面向对象编程(以及它与其他语言的不同之处)进行了很好的讨论(进一步参考)。总之,在C ++中,您必须考虑所有权。哪些智能指针可以帮助您,但不是唯一的解决方案,或者总是一个完整的解决方案(它们不会给您多态复制)并且并不总是您希望在界面中公开的解决方案(并且函数返回听起来很糟糕)很像一个界面)。例如,返回引用可能就足够了。但在所有这些情况下(智能指针,指针容器或只是返回引用),您已将的返回更改为某种形式的引用。如果你真的需要复制,你可能需要添加更多的样板“idiom”或者使用C ++中的惯用(或其他)OOP超越使用Adobe PolyBoost.TypeErasure等库的更通用的多态性

作者: da77a 发布者: 26.01.2018 03:10
32x32