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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是智能指针,什么时候应该使用?相关的知识,希望对你有一定的参考价值。

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

答案

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_ptrstd::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_ptrs非常有用,即其他对象的生命周期。该对象一直存在,直到退出包含的代码块,或者直到包含的对象本身被销毁为止。

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

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

引用计数指针有一个缺点 - 创建悬空引用的可能性:

// 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的弱(不计数)引用。

另一答案

设T是本教程中的一个类C ++中的指针可分为3种类型:

1)原始指针:

T a;  
T * _ptr = &a; 

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

带有const数据或地址的指针{Read backwardwards}

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

指向数据类型T的指针,它是一个const。这意味着您无法使用指针更改数据类型。即*ptr1 = 19;不管用。但你可以移动指针。即ptr1++ , ptr1--;等会工作。向后读:指向类型T的指针,即const

  T * const ptr2 ;

指向数据类型T的const指针。意味着您无法移动指针,但您可以更改指针指向的值。即*ptr2 = 19将工作,但ptr2++ ; ptr2--等将无法正常工作。向后读:const指向类型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

见:When is std::weak_ptr useful?

独特指针:轻量级智能指针,拥有独家所有权。当指针指向唯一对象而不共享指针之间的对象时使用。

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指针,不能用更好的语法移动。

见:What are the differences between a pointer variable and a reference variable in 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 

参考:当它的目标应该被删除时,为啥这个智能指针会给出正确的结果?

什么时候应该使用指针分配给 int?

一个函数应该返回什么可以失败?

我啥时候应该明确使用 `this` 指针?

C11新特性之智能指针

C++ - 指针和“智能指针”