unique_ptr 使用详解 (C++11)
auto_ptr是用于C++11之前的智能指针。由于 auto_ptr 基于排他所有权模式:两个指针不能指向同一个资源,复制或赋值都会改变资源的所有权。auto_ptr 主要有两大问题:
复制和赋值会改变资源的所有权,不符合人的直觉。
在 STL 容器中使用auto_ptr存在重大风险,因为容器内的元素必需支持可复制(copy constructable)和可赋值(assignable)。
不支持对象数组的操作
demo 代码(一)
例子:
#include <iostream>
#include <stdio.h>
#include <string>
#include <memory>
#include <vector>
using namespace std;
int main()
{
/* 弊端 1. auto_ptr 被C++11 抛弃的主要理由 p1 = p2, 赋值或赋值都会改变资源的所有权 */
auto_ptr<string> p1(new string("I 'm weifc."));
auto_ptr<string> p2(new string("I 'm rock."));
printf("p1: %p\n", p1, p1.get());
printf("p2: %p\n", p2.get());
p1 = p2;
printf("after p1 = p2\n");
printf("p1: %p\n", p1.get());
printf("p2: %p\n", p2.get());
/* 弊端2. 在 STL 容器中使用 auto_ptr 存在重大的风险, 因为容器内的元素必需支持可复制(copy constructable)
和可赋值 (assignable) */
vector<auto_ptr<string>> va;
auto_ptr<string> p3(new string("I 'm p3"));
auto_ptr<string> p4(new string("I 'm p4"));
va.push_back(std::move(p3));
va.push_back(std::move(p4));
cout << "va[0]" << *va[0] << endl;
cout << "va[1]" << *va[1] << endl;
/* 风险来啦 */
va[0] = va[1];
cout << "va[0]:" << *va[0] << endl;
cout << "va[1]:" << *va[1] << endl;
/* 弊端3. 不支持对象数组的内存管理 */
//auto_ptr<int[]> ai(new int[5]); /* 不能这样定义 */
//auto_ptr 陷阱, 不能把同一段内存交给多个 auto_ptr 变量去管理
/*{
auto_ptr < string p2;
p2.reset(str);
{
auto_ptr<string> p1;
p1.reset(str);
}
cout << "str: " << p2 << endl;
}*/
system("pause");
return 0;
}
所以, C++11 用更严谨的 unique_ptr 取代了 auto_ptr !
unique_ptr 特性
- 基于排他所有权模式: 两个指针不能指向同一资源
- 无法进行左值 unique_ptr 复制构造, 也无法进行左值复制赋值操作, 但允许临时右值赋值构造和赋值
- 保存指定某个对象的指针, 当它本身离开作用域会自动释放它指向的对象.
- 在容器中保存指针是安全的
demo 代码(二)
#include <stdio.h>
#include <iostream>
#include <string>
#include <memory>
#include <vector>
using namespace std;
int main()
{
/* 弊端 1. auto_ptr 被C++11 抛弃的主要理由 p1 = p2, 复制或赋值都会改变资源的所有权 */
/* unique_ptr 如何解决这个问题? 不允许显示的右值赋值和构造 */
unique_ptr<string> p1(new string("I 'm weifc"));
unique_ptr<string> p2(new string("I 'm weikc"));
printf("p1: %p\n", p1.get());
printf("p2: %p\n", p2.get());
/* 如果一定要转移, 使用 move 把左值转成右值 */
p1 = std::move(p2);
printf("p1: %p\n", p1.get());
printf("p2: %p\n", p2.get());
/* p1 = p2; 左值赋值禁止 */
unique_ptr<string> p3(new string("I 'm p3."));
unique_ptr<string> p4(std::move(p3)); /* 左值拷贝构造也不行, 必须转成右值 */
/* 弊端2. 在 STL 容器中使用 auto_ptr 存在重大风险, 因为容器内的元素必需支持可复制(copy constructable)
和 可赋值 (assignable). */
vector<unique_ptr<string>> vu;
unique_ptr<string> p5(new string("I 'm p5"));
unique_ptr<string> p6(new string("I 'm p6"));
vu.push_back(std::move(p3));
vu.push_back(std::move(p4));
cout << "va[0]: " << *vu[0] << endl;
cout << "va[1]: " << *vu[1] << endl;
// vu[0] = vu[1]; // unique_ptr 不支持直接赋值, 没有风险
/* 弊端 3. auto_ptr 不支持对象数组的内存管理, unique_ptr 支持 */
// 但是 unique_ptr 支持对象数组的管理
// auto_ptr<int[]> ai(new int[5]); // 不能这样定义
unique_ptr<int[]> ui(new int[5]); // 自动会调用 delete [] 函数去释放
system("pause");
return 0;
}
构造函数
unique_ptr<T> up ; //空的unique_ptr,可以指向类型为T的对象
unique_ptr<T> up1(new T()) ;//定义unique_ptr,同时指向类型为T的对象
unique_ptr<T[]> up ; //空的unique_ptr,可以指向类型为T[的数组对象
unique_ptr<T[]> up1(new T[]) ;//定义unique_ptr,同时指向类型为T的数组对象
unique_ptr<T,D> up(); //空的unique_ptr,接受一个D类型的删除器d,使用d释放内存
unique_ptr<T,D> up(new T()); //定义unique_ptr,同时指向类型为T的对象,接受一个D 类型的删除器d,使用删除器d来释放内存
赋值
unique_ptr<int> up1(new int(10));
unique_ptr<int> up2(new int(11));
up1 = std::move(up2);//必须使用移动语义,结果,up1 内存释放, up2 交由up1 管理
主动释放对象
up = nullptr ;//释放up指向的对象,将up置为空
或 up = NULL; //作用相同
放弃对象控制权
up.release(); //放弃对象的控制权,返回指针,将up置为空,不会释放内存
重置
up.reset(…) ; //参数可以为 空、内置指针,先将up所指对象释放,然后重置up的值
交换
up.swap(up1); //将智能指针up 和up1管控的对象进行交换
结语:
时间: 2020-07-03