【转】STL 四种智能指针

STL 一共给咱们提供了四种智能指针:auto_ptr、unique_ptr、shared_ptr 和 weak_ptr,auto_ptr 是 C++98 提供的解决方案,C+11 已将其摒弃,并提出了 unique_ptr 做为 auto_ptr 替代方案。虽然 auto_ptr 已被摒弃,但在实际项目中仍可以使用,但建议使用较新的 unique_ptr,由于 unique_ptr 比 auto_ptr 更加安全,后文会详细叙述。shared_ptr 和 weak_ptr 则是 C+11 从准标准库 Boost 中引入的两种智能指针。此外,Boost 库还提出了 boost::scoped_ptr、boost::scoped_array、boost::intrusive_ptr 等智能指针,虽然还没有获得 C++ 标准采纳,可是在开发实践中可使用。ios

auto_ptr

auto_ptr 一样是 STL 智能指针家族的成员之一,由 C++98 引入,定义在头文件 。其功能和用法相似于 unique_ptr,由 new expression 得到对象,在 auto_ptr 对象销毁时,他所管理的对象也会自动被 delete 掉。
auto_ptr 从 C++98 使用至今,从 C++11 开始,引入unique_ptr 来替代 auto_ptr。
先来看下面的赋值语句:
算法

auto_ptr<string> ps (new string ("I reigned lonely as a cloud.”);
auto_ptr<string> vocation; 
vocaticn = ps;

若是 ps 和 vocation 是常规指针,则两个指针将指向同一个 string 对象。这是不能接受的,由于程序将试图删除同一个对象两次,一次是 ps 过时时,另外一次是 vocation 过时时。
要避免这种问题,方法有多种:express

一、定义陚值运算符,使之执行深复制。这样两个指针将指向不一样的对象,其中的一个对象是另外一个对象的副本,缺点是浪费空间,因此智能指针都未采用此方案。
二、创建全部权(ownership)概念。对于特定的对象,只能有一个智能指针可拥有,这样只有拥有对象的智能指针的析构函数会删除该对象。而后让赋值操做转让全部权。这就是用于 auto_ptr 和 unique_ptr 的策略,但 unique_ptr 的策略更严格。
三、建立智能更高的指针,跟踪引用特定对象的智能指针数。这称为引用计数。例如,赋值时,计数将加 1,而指针过时时,计数将减 1,。当减为 0 时才调用 delete。这是 shared_ptr 采用的策略。

固然,一样的策略也适用于复制构造函数,即auto_ptr vocation(ps)时也须要上面的策略。每种方法都有其用途,但为什么要摒弃 auto_ptr 呢? 安全

下面举个例子来讲明。请看以下代码:函数

#include <iostream>
#include <string>
#include <memory>
using namespace std;

int main()
{
    auto_ptr<string> films[5] ={
    auto_ptr<string> (new string("Fowl Balls")),
    auto_ptr<string> (new string("Duck Walks")),
    auto_ptr<string> (new string("Chicken Runs")),
    auto_ptr<string> (new string("Turkey Errors")),
    auto_ptr<string> (new string("Goose Eggs"))
    };
    auto_ptr<string> pwin;
    pwin = films[2]; // films[2] loses ownership. 将全部权从films[2]转让给pwin,此时films[2]再也不引用该字符串从而变成空指针

    cout << "The nominees for best avian baseballl film are\n";
    for(int i = 0; i < 5; ++i)
    {
        cout << *films[i] << endl;
    }
    cout << "The winner is " << *pwin << endl;
    return 0;
}

运行下发现程序崩溃了,缘由在上面注释已经说的很清楚,films[2] 已是空指针了,下面输出访问空指针固然会崩溃了。但这里若是把 auto_ptr 换成 shared_ptr 后,程序就不会崩溃,缘由以下:
使用 shared_ptr 时运行正常,由于 shared_ptr 采用引用计数,pwin 和films[2] 都指向同一块内存,在释放空间时由于事先要判断引用计数值的大小所以不会出现屡次删除一个对象的错误。
使用 unique_ptr 时编译出错,与 auto_ptr 同样,unique_ptr 也采用全部权模型,但在使用 unique_ptr 时,程序不会等到运行阶段崩溃,而在编译期因下述代码行出现错误:spa

unique_ptr<string> pwin;
pwin = films[2];                    //films[2] loses ownership

指导你发现潜在的内存错误。这就是为什么要摒弃 auto_ptr 的缘由,一句话总结就是:避免因潜在的内存问题致使程序崩溃。
从上面可见,unique_ptr 比 auto_ptr 更加安全,由于 auto_ptr 有拷贝语义,拷贝后原对象变得无效,再次访问原对象时会致使程序崩溃;unique_ptr 则禁止了拷贝语义,但提供了移动语义,便可以使用std::move() 进行控制权限的转移,以下代码所示:指针

unique_ptr<string> upt(new string("lvlv"));
unique_ptr<string> upt1(upt);   //编译出错,已禁止拷贝
unique_ptr<string> upt1=upt;    //编译出错,已禁止拷贝
unique_ptr<string> upt1=std::move(upt);  //控制权限转移

auto_ptr<string> apt(new string("lvlv"));
auto_ptr<string> apt1(apt); //编译经过
auto_ptr<string> apt1=apt;  //编译经过

这里要注意,在使用std::move将unique_ptr的控制权限转移后,不可以再经过unique_ptr来访问和控制资源了,不然一样会出现程序崩溃。咱们能够在使用unique_ptr访问资源前,使用成员函数get()进行判空操做。code

unique_ptr<string> upt1=std::move(upt);                             //控制权限转移
if(upt.get()!=nullptr)                                                              //判空操做更安全
{
    //do something
}

2.unique_ptr

unique_ptr 由 C++11 引入,旨在替代不安全的 auto_ptr。unique_ptr 是一种定义在头文件<memory>中的智能指针。它持有对对象的独有权——两个unique_ptr不能指向一个对象,即 unique_ptr 不共享它所管理的对象。它没法复制到其余 unique_ptr,没法经过值传递到函数,也没法用于须要副本的任何标准模板库 (STL)算法。只能移动 unique_ptr,即对资源管理权限能够实现转移。这意味着,内存资源全部权能够转移到另外一个 unique_ptr,而且原始 unique_ptr 再也不拥有此资源。实际使用中,建议将对象限制为由一个全部者全部,由于多个全部权会使程序逻辑变得复杂。所以,当须要智能指针用于存 C++ 对象时,可以使用 unique_ptr,构造 unique_ptr 时,可以使用 make_unique Helper 函数。对象

下图演示了两个 unique_ptr 实例之间的全部权转换。

unique_ptr与原始指针同样有效,并可用于 STL 容器。将 unique_ptr 实例添加到 STL 容器颇有效,由于经过 unique_ptr 的移动构造函数,再也不须要进行复制操做。unique_ptr指针与其所指对象的关系:在智能指针生命周期内,能够改变智能指针所指对象,如建立智能指针时经过构造函数指定、经过reset方法从新指定、经过release方法释放全部权、经过移动语义转移全部权,unique_ptr还可能没有对象,这种状况被称为empty。
unique_ptr的基本操做有:blog

//智能指针的建立  
unique_ptr<int> u_i; //建立空智能指针
u_i.reset(new int(3)); //"绑定”动态对象  
unique_ptr<int> u_i2(new int(4));//建立时指定动态对象
unique_ptr<T,D> u(d);   //建立空unique_ptr,执行类型为T的对象,用类型为D的对象d来替代默认的删除器delete

//全部权的变化  
int *p_i = u_i2.release(); //释放全部权  
unique_ptr<string> u_s(new string("abc"));  
unique_ptr<string> u_s2 = std::move(u_s); //全部权转移(经过移动语义),u_s全部权转移后,变成“空指针” 
u_s2.reset(u_s.release());//全部权转移
u_s2=nullptr;//显式销毁所指对象,同时智能指针变为空指针。与u_s2.reset()等价

unique_ptr不只安全,并且灵活

若是unique_ptr 是个临时右值,编译器容许拷贝语义。参考以下代码:

unique_ptr<string> demo(const char * s){
    unique_ptr<string> temp (new string (s)); 
    return temp;
}

//假设编写了以下代码:
unique_ptr<string> ps;
ps = demo('Uniquely special");

demo()返回一个临时unique_ptr,而后ps接管了本来归返回的unique_ptr全部的对象,而返回时临时的 unique_ptr 被销毁,也就是说没有机会使用 unique_ptr 来访问无效的数据,换句话来讲,这种赋值是不会出现任何问题的,即没有理由禁止这种赋值。实际上,编译器确实容许这种赋值。相对于auto_ptr任何状况下都容许拷贝语义,这正是unique_ptr更加灵活聪明的地方。

相关文章
相关标签/搜索