1.简介
unique_ptr 是 C++ 11 提供的用于防止内存泄漏的智能指针中的一种实现,独享被管理对象指针所有权的智能指针。unique_ptr对象包装一个原始指针,并负责其生命周期。当该对象被销毁时,会在其析构函数中删除关联的原始指针。
(替换auto_ptr)unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露(例如“以new创建对象后因为发生异常而忘记调用delete”)特别有用。
2. 初始化unique_ptr对象的方法?
- 构造函数
1] 将已存在的指向动态内存的普通指针作为参数来构造
int* p = new int(33);
unique_ptr<int> api(p);
2] 直接构造智能指针
unique_ptr< int > api( new int( 33 ) );
3. 防止两个unique_ptr对象拥有同一个对象(一块内存)
因为unique_ptr的所有权独有,所以下面的代码会造成混乱。
int* p = new int(0);
unique_ptr<int> ap1(p);
unique_ptr<int> ap2(p);
因为ap1与ap2都认为指针p是归它管的,在析构时都试图删除p, 两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用unique_ptr。
4. 警惕智能指针作为参数!
- 按值传递时,函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的unique_ptr(拷贝构造),这样,传入的实参unique_ptr就失去了其对原对象的所有权,而该对象会在函数退出时被局部unique_ptr删除。如下例:
void f(unique_ptr<int> ap){
cout<<*ap;
}
unique_ptr<int> ap1(new int(0));
f(ap1);
cout<<*ap1; //错误,经过f(ap1)函数调用,ap1已经不再拥有任何对象了。
- 引用或指针时,不会存在上面的拷贝过程。但我们并不知道在函数中对传入的unique_ptr做了什么,如果当中某些操作使其失去了对对象的所有权,那么这还是可能会导致致命的执行期错误。
结论:const reference是智能指针作为参数传递的底线。
5. unique_ptr常用的成员函数
- get()
返回unique_ptr指向的那个对象的内存地址。如下例:
int* p = new int(33);
cout << "the adress of p: "<< p << endl;
unique_ptr<int> ap1(p);
cout << "the adress of ap1: " << &ap1 << endl;
cout << "the adress of the object which ap1 point to: " << ap1.get() << endl;
输出如下:
the adress of p: 00481E00
the adress of ap1: 0012FF68
the adress of the object which ap1 point to: 00481E00
第一行与第三行相同,都是int所在的那块内存的地址。第二行是ap1这个类对象本身所在内存的地址。
- reset()
重新设置unique_ptr指向的对象。类似于赋值操作,但赋值操作不允许将一个普通指针指直接赋给unique_ptr,而reset()允许。如下例:
unique_ptr< string > pstr_auto( new string( "Brontosaurus" ) );
pstr_auto.reset( new string( "Long -neck" ) );
在例子中,重置前pstr_auto拥有"Brontosaurus"字符内存的所有权,这块内存首先会被释放。之后pstr_auto再拥有"Long -neck"字符内存的所有权。
注:reset(0)可以释放对象,销毁内存。
- release()
返回unique_ptr指向的那个对象的内存地址,并释放对这个对象的所有权。
用此函数初始化unique_ptr时可以避免两个unique_ptr对象拥有同一个对象的情况(与get函数相比)。
例子如下:
unique_ptr< string > pstr_auto( new string( "Brontosaurus" ) );
unique_ptr< string > pstr_auto2( pstr_auto.get() ); //这是两个unique_ptr拥有同一个对象
unique_ptr< string > pstr_auto2( pstr_auto.release() ); //release可以首先释放所有权
4)move()
如果确实想执行类似赋值的操作,要安全的重用这种指针,可给它赋新值。C++有一个标准库函数std::move(),让你能够将一个unique_ptr赋给另一个。尽管转移所有权后 还是有可能出现原有指针调用(调用就崩溃)的情况。但是这个语法能强调你是在转移所有权,让你清晰的知道自己在做什么,从而不乱调用原有指针。
unique_ptr<string> ps1, ps2;
ps1 = unique_ptr<string>(new string("hello"));
ps2 = move(ps1);
ps1 = unique_ptr<string>(new string("alexia"));
cout << *ps2 << *ps1 << endl;
6.unique_ptr的缺陷
(1)不要使用unique_ptr对象保存指向静态分配对象的指针。否则,当unique_ptr对象本身被撤销时,它将试图删除指向非动态分配对象的指针,导致未定义的行为。
int a=1;
unique_ptr<int> ap(&a); //编译没有问题,会导致未定义行为
(2)不要使两个unique_ptr对象指向同一对象。
unique_ptr<int> ap1(new int (1024));
unique_ptr<int> ap2(ap1.get()); //会被释放两次
(3)不要使用unique_ptr对象保存指向动态分配数组的指针。从源代码中可以看出,它用的是delete操作符,而不是delete [ ] 操作符
(4)不要将unique_ptr对象存储在容器中。因为unique_ptr的复制和赋值具有破坏性。不满足容器要求:复制或赋值后,两个对象必须具有相同值。
7.完整测试代码
#include <iostream>
#include <memory> //auto_ptr 的头文件
#include <string>
using namespace std;
class ptrTest
{
public:
ptrTest(int i) {
index = i;
cout << "ptrTest():"<<i<<endl;
};
~ptrTest() {
cout << "delete ptrTest:" << index << endl;
};
public:
int getIndex() {
return index;
};
private:
int index = 0;
};
void main() {
{
unique_ptr<string> p3(new string("auto")); //#4
unique_ptr<string> p4; //#5
//p4 = p3;//此时会报错!!
unique_ptr<string> pu1(new string("hello world"));
unique_ptr<string> pu2;
//pu2 = pu1; // #1 不允许
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string("You")); // #2 允许
}
{
unique_ptr< string > pstr_auto(new string("Brontosaurus"));
//unique_ptr< string > pstr_auto2( pstr_auto ); //错误
}
{
unique_ptr<ptrTest> p3(new ptrTest(2)); //#4
unique_ptr<ptrTest> p4; //#5
}
{
int* p = new int(0);
unique_ptr<int> ap1(p);
//unique_ptr<int> ap2(p);
}
{
unique_ptr<string> ps1, ps2;
ps1 = unique_ptr<string>(new string("hello"));
ps2 = move(ps1);
ps1 = unique_ptr<string>(new string("alexia"));
cout << *ps2 << *ps1 << endl;
ps1.get();
ps1.reset(new string("Long -neck"));
ps1.release();
}
cout << "1234" << endl;
{
unique_ptr<ptrTest> ps1, ps2;
ps1 = unique_ptr<ptrTest>(new ptrTest(1));
ps2 = move(ps1);
ps1 = unique_ptr<ptrTest>(new ptrTest(2));
//cout << *ps2 << *ps1 << endl;
ps1.get();
ps1.reset(new ptrTest(3));//ps1会释放new ptrTest(2),然后被赋值
cout << "3";
ps1.release(); //ps1释放了new ptrTest(3)的控制权 会导致3无法释放
}
system("pause");
}
运行结果