之前的例子都是关于定义在作用域内的对象的,它们可以在作用域结束的时候释放掉资源。但是如果对象是在自由存储上分配的呢?在< memory>当中,标准库提供了两种“智能指针”来管理自由存储上的对象:
    【1】 unique_ptr对应所有权唯一的情况(见343.1节)。
    【2】 shared_ptr对应所有权共享的情况(见34.3.2节)。
    这些“智能指针”最基本的作用是防止由于编程疏忽而造成的内存泄漏。例如:

    1. void f(int i,int j) //对比X*和unique_ptr<X>
    2. {
    3. X* p=new X; //分配一个新的X
    4. unique_ptr<X> sp{new X}; //分配一个新的X,把它的指针赋给unique_ptr
    5. //...
    6. if(i<99) throw Z{}; //可能会抛出异常
    7. if(j<77) return; //..可能会"过早的"返回
    8. p->do_something(); //可能会抛出异常
    9. sp->do_something(); //可能会抛出异常
    10. //...
    11. delete p; //销毁*p
    12. }

    在这段代码中,如果i<99或者j<77,我们会“忘记”释放掉指针p。另一方面, unique_pt确保不论我们以哪种方式(通过抛出异常,或者通过执行 return语句,或者跳转到了函数末尾)退出f()都会释放掉它的对象。其实换个角度思考一下,如果我们干脆不使用指针也不使用new,那么上面的问题也就不复存在了:

    1. void f(int i,int j) //使用局部变量
    2. {
    3. X x;
    4. //...
    5. }

    不幸的是,越来越多的程序员喜欢不加节制地滥用new(以及指针和引用)。
    如果你确实需要使用指针,那么与内置指针相比, unique_ptr是更好的选择。后者是种轻量级的机制,消耗的时空代价并不比前者大。通过使用 unique_ptr,我们还可以把自由存储上申请的对象传递给函数或者从函数中传出来:

    1. unique_ptr<X> make_X(int i) //创建一个X,然后立即把他赋给unique_ptr
    2. {
    3. //检查i以及其他操作
    4. return unique_ptr<X>{new X{i}};
    5. }

    unique_ptr是一个独立对象或数组的句柄,就像 vector是对象序列的句柄一样。这二者都以RAⅡ的机制控制其他对象的生命周期,并且都通过移动操作使得 return语句简单高效。
    shared_pt在很多方面都和 unique_ptr非常相似,唯一的区别是 shared_ptr的对象使用拷贝操作而非移动操作。某个对象的多个 shared_ptr共享该对象的所有权,只有当最后一个 shared_ pt被销毁时对象才被销毁。例如:

    1. void f(shared_ptr<fstream>);
    2. void g(shared_ptr<fstream>);
    3. void user(const string& name,ios_base::openmode mode)
    4. {
    5. shared_ptr<fstream> fp{new fstream(name,mode)};
    6. if(!*fp)
    7. throw No_file{}; //检查文件是否被正确打开
    8. f(fp);
    9. g(fp);
    10. //...
    11. }

    fp的构造函数打开的文件将会被使用了fp的最后一个函数(显式地或者隐式地)关闭。其中f()或者g()有可能含有fp的一份拷贝,而这份拷贝直到user()执行完还在使用。因此,与使用析构函数管理内存对象的资源管理方式相比, shared_ptr提供的垃圾回收机制需要慎重使用。这与时空代价无关,而是说 shared_ptr使得对象的生命周期变得不那么容易掌控了。我们的建议是:除非你确实需要共享所有权,否则别轻易使用shared_ptr
    通过使用 unique_ptrshared_ptr,我们就能在很多程序中实现完全“没有裸new”的目标(见3.2.1.2节)。不过,这些“智能指针”从概念上讲仍然是指针,因此我在管理资源时只把它们当成次优选择—把容器和其他可以在更高的概念层次上管理资源的类型作为第一选择效果更好。还有一点值得注意, shared_ptr本身没有制定任何规则用以指明共享指针的哪个拥有者有权读写对象。因此尽管在一定程度上解决了资源管理的问题,但是数据竞争(见41.2.4节)和其他形式的数据混淆依然存在。
    那么什么情况下我们才应该选择“智能指针”(比如 unique_ptr)而非带有特定操作的资源句柄(比如 vectorthread)呢?显然,答案应该是“当我们需要使用指针的语义时”。

    • 当我们共享某个对象时,需要让多个指针或者引用指向被共享的对象,此时选择shared_ptr是显而易见的(除非所有人都知道资源有且只有一个拥有者)。
    • 当我们指向一个多态对象时,很难确切地知道对象到底是什么类型(甚至连对象的大小都不知道),所以应该使用指针或者引用,此时 unique_ptr成为必然的选择。
    • 共享的多态对象通常会用到 shared_ptr.

    当我们需要从函数返回对象的集合时,不必用指针,使用容器能让这个任务更加简单高效(见3.3.2节)。