在C++11引入的新智能指针中,shared_ptr指针通过其计数机制实现与其他指针共享内存。此外,在C++11中还存在一个与之相应的智能指针——unique_prt:它同样可以自动管理内存,只是这块内存不和其他的unique_ptr分享。

1. unique_ptr指针的初始化 & 赋值

有了shared_ptr的基础,我们首先介绍unique_ptr指针的初始化、赋值:

1.1 初始化

如果观察unique_ptr的构造函数声明会发现,unique_ptr支持的构造函数和shared_ptr相差无几,这里直接举例:
1)初始化为空指针

2)从指针创建

3)从右值引用创建

从输出结果可见,内存从p3转到了p4:

1.2 赋值

需要强调的是,由于unique_ptr对于内存的独占特性,unique_ptr不支持直接的赋值操作,而只能支持右值引用的赋值,基本形式如下:

unique_ptr<MyString> p1(new MyString("hello unique ptr"));
cout << "p1.get = " << p1.get() << endl;

unique_ptr<MyString> p2;
cout << "p2.get = " << p2.get() << endl;

p2 = move(p1);
cout << "p1.get = " << p1.get() << endl;
cout << "p2.get = " << p2.get() << endl;

/*
输出:
Constructed (hello unique ptr)!
p1.get = 0x7f8381c05a00
p2.get = 0x0
p1.get = 0x0
p2.get = 0x7f8381c05a00
Destructed (hello unique ptr)!
*/

需要说明的是,上述代码其实并没有什么实际意义,仅是为了说明unique_ptr采用了所有权独占机制。在真正的项目中,unique_ptr最常见的用法如下:

unique_ptr<MyString> GetUnique(string ms)
{
    unique_ptr<MyString> tmp(new MyString(ms));
    cout << tmp.get() << endl;
    return tmp;
}

int main(){
    unique_ptr<MyString> p1;
    p1 = GetUnique("hello unique_ptr");

    p1->Output();
    cout << p1.get() << endl;
    return 0;
} 

/*
输出:
Constructed (hello unique_ptr)!
0x7fde05405a00
hello unique_ptr
0x7fde05405a00
Destructed (hello unique_ptr)!
*/

GetUnique()返回一个临时unique_ptr,然后p1接管了原本归返回的unique_ptr所有的对象,而返回时临时的 unique_ptr 被销毁,也就是说没有机会使用unique_ptr 来访问无效的数据,换句话来说,这种赋值是不会出现任何问题的,即没有理由禁止这种赋值。实际上,编译器确实允许这种赋值,这正是unique_ptr更聪明的地方。

总之,党程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做,比如:

unique_ptr<string> pu1(new string ("hello world"));
unique_ptr<string> pu2;
pu2 = pu1;                                      //#1 not allowed---编译器报错
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You"));   //#2 allowed

其中#1留下悬挂的unique_ptr(pu1),这可能导致危害。而#2不会留下悬挂的unique_ptr,因为它调用 unique_ptr 的构造函数,该构造函数创建的临时对象在其所有权让给 pu3 后就会被销毁。