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

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

智能指针是一个包装“裸”(或“裸”)C ++指针的类,用于pipe理指向的对象的生命周期。 没有单一的智能指针types,但他们都试图以一种实际的方式抽象一个原始指针。

智能指针应该优于原始指针。 如果你觉得你需要使用指针(首先考虑你是否真的这么做),你通常会使用一个智能指针,因为这可以减轻原指针的许多问题,主要是忘记删除对象和泄漏内存。

使用原始指针时,程序员必须在对象不再有用时明确地销毁对象。

// 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...? 

智能指针通过比较来定义关于何时销毁对象的策略。 你仍然需要创build这个对象,但是你不用担心被破坏。

 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_ptrstd::unique_ptr

 void f() { { boost::scoped_ptr<MyObject> 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实例不能被复制。 这可以防止多次删除指针(不正确)。 但是,您可以将引用传递给您调用的其他函数。

当你想把对象的生命周期和特定的代码块联系起来,或者如果你把它作为成员数据embedded到另一个对象中,这个对象的生命周期时,scoped指针是很有用的。 该对象一直存在,直到包含的代码块被退出,或者直到包含的对象本身被销毁。

更复杂的智能指针策略涉及指针的计数。 这确实允许指针被复制。 当对象的最后一个“引用”被销毁时,该对象被删除。 这个策略是由boost::shared_ptrstd::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. 

当您的对象的生命周期非常复杂,并且不直接绑定到特定的代码段或另一个对象时,引用计数指针非常有用。

引用计数的指针有一个缺点 – 创build悬挂引用的可能性:

 // 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! 

另一种可能性是创build循环引用:

 struct Owner { boost::shared_ptr<Owner> other; }; boost::shared_ptr<Owner> p1 (new Owner()); boost::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的弱(不计数)的引用。


UPDATE

这个答案是相当古老的,因此描述了当时的“好”,这是由Boost库提供的智能指针。 由于C ++ 11,标准库提供了足够的智能指针types,因此您应该倾向于使用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 ++的这些日子,这是一个简单的答案:

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

智能指针是一种类似指针的types,具有一些额外的function,例如自动内存释放,引用计数等。

小介绍可在页面智能指针 – 什么,为什么,哪个? 。

其中一个简单的智能指针types是std::auto_ptr (C ++标准的第20.4.5章),它允许在超出范围时自动释放内存,并且在抛出exception时比使用简单指针更健壮灵活。

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

David Vandevoorde,Nicolai M. Josuttis在第20章“智能指针”一章中详细讨论了“C ++模板:完全指南”一书。 一些主题包括:

  • 防止例外
  • 持有者,(注意, std :: auto_ptr就是这种types的智能指针的实现)
  • 资源获取是初始化 (这常常用于C ++中的exception安全资源pipe理)
  • 持有人限制
  • 引用计数
  • 并发计数器访问
  • 销毁和释放

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

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

这是有用的:

  • 必须用新分配的对象,但是您希望与该堆栈上的某个对象具有相同的生命周期。 如果对象被分配给一个智能指针,那么当程序退出该函数/块时,它们将被删除。

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

在下列情况下您可能不想使用智能指针:

  • …指针实际上不应该拥有数据…也就是说,当你使用这些数据,但是你希望它能够在你引用它的函数中生存下来。
  • 智能指针本身并不会在某个时刻被破坏。 你不希望它坐在永远不会被破坏的内存中(例如在一个dynamic分配的对象中,但不会被明确删除)。
  • 两个智能指针可能指向相同的数据。 (然而,有更聪明的指针可以处理这个…就是所谓的引用计数 。)

也可以看看:

  • 垃圾收集 。
  • 关于数据所有权这个堆栈溢出问题

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

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

shared_ptrfunction非常强大,可处理各种各样的处理场景,包括需要“跨DLL边界传递”对象的情况(如果在代码和DLL之间使用不同的libc则是常见的恶梦案例)。

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

一个人可以实现自己的智能指针,但是许多库也提供了各自具有不同优点和缺点的智能指针实现。

例如, Boost提供了以下智能指针实现:

  • shared_ptr<T>是一个指向T的指针,使用一个引用计数来确定何时不再需要该对象。
  • scoped_ptr<T>是指针超出范围时自动删除的指针。 没有分配是可能的。
  • intrusive_ptr<T>是另一个引用计数指针。 它提供比shared_ptr更好的性能,但要求typesT提供自己的引用计数机制。
  • 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中,但现在已被弃用。

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

一个智能指针是一个行为的对象,外观和感觉像一个普通的指针,但提供更多的function。 在C ++中,智能指针被实现为封装指针并覆盖标准指针运算符的模板类。 它们比常规指针有许多优点。 它们保证被初始化为空指针或指向堆对象的指针。 通过空指针进行间接检查。 没有必要删除。 对象被自动释放时,最后一个指针已经消失。 这些智能指针的一个重要问题是,不像常规的指针,他们不尊重inheritance。 智能指针对多态代码没有吸引力。 下面给出的是智能指针实现的例子。

例:

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

这个类实现了一个指向Xtypes对象的智能指针。对象本身位于堆上。 以下是如何使用它:

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

像其他重载操作符一样,p将像一个常规指针一样工作,

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

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

在计算机科学中,智能指针是一种抽象数据types,它在模拟指针的同时提供了附加function,例如自动垃圾收集或边界检查。 这些附加function旨在减less由于滥用指针而导致的错误,同时保持效率。 智能指针通常跟踪指向它们的对象,以进行内存pipe理。 指针的滥用是bug的主要来源:必须由使用指针编写的程序执行的常量分配,释放和引用使得很可能发生一些内存泄漏。 智能指针通过自动进行资源释放来防止内存泄漏:当指向对象的指针(或指针系列中的最后一个指针)被销毁时,例如因为超出了作用域,被指向的对象也被销毁。

让T成为本教程中的一个类C ++中的指针可以分为3类:

1) 原始指针

 T a; T * _ptr = &a; 

他们将内存地址保存到内存中的某个位置。 谨慎使用,因为程序变得复杂难以追踪。

用const数据或地址指针{向后读取}

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

指向一个常量的数据typesT的指针。 意思是你不能用指针改变数据types。 即*ptr1 = 19 ; 不pipe用。 但是你可以移动指针。 即ptr1++ , ptr1-- ; 等将工作。 向后读:指向typesT的指针是const

  T * const ptr2 ; 

一个指向数据typesT的const指针。 意思是你不能移动指针,但你可以改变指针指向的值。 即*ptr2 = 19将工作,但ptr2++ ; ptr2-- ptr2++ ; ptr2--等不起作用。 向后读:const指针指向Ttypes

 const T * const ptr3 ; 

指向常量数据typesT的const指针。 这意味着您不能移动指针,也不能将数据types指针更改为指针。 即, ptr3-- ; ptr3++ ; *ptr3 = 19; 不pipe用

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 

使用引用计数来实现,以跟踪有多less“东西”指向指针指向的对象。 当这个计数变为0时,该对象被自动删除,即当指向该对象的所有share_ptr超出范围时被删除。 这摆脱了不得不删除你已经分配使用新的对象的头痛。

弱指针:帮助处理使用共享指针时产生的循环引用如果有两个共享指针指向两个对象,并且有一个指向彼此共享指针的内部共享指针,则会有一个循环引用,对象不会当共享指针超出范围时被删除。 为了解决这个问题,将内部成员从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指针,也就是一个const指针,不能用更好的语法移动。

请参阅: C ++中的指针variables和引用variables之间的区别是什么?

 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感谢安德烈指出这个问题。

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

自动指针虽然看起来相似,但与智能指针完全不同。 当一个自动指针对象超出variables作用域时,这是一个方便的类来释放资源。 在某种程度上,它使得一个指向(dynamic分配的内存)的指针类似于一个堆栈variables(在编译时间中静态分配)。

智能指针是那些您不必担心内存取消分配,资源共享和传输的地方。

您可以很好地使用这些指针,就像Java中的任何分配工作一样。 在Java垃圾收集器做的伎俩,而在智能指针,诀窍是由析构函数完成。