文章目录
1.unique_ptr概述
一个 unique_ptr “拥有”它所指向的对象。与 shared_ptr 不同,某个时刻只能有一个 unique_ptr 指向一个给定对象。当 unique_ptr 被销毁时,它所指向的对象也被销毁。
2.unique_ptr的初始化
2.1 直接初始化
unique_ptr<int> p; // 可以指向int对象的一个空智能指针
unique_ptr<int> p(new int(105)); // p指向一个值为105的int对象
auto p(new int(105)); // 不能简写为auto,这里auto推断出是普通指针
2.2 make_unique函数
C++14 才有的 make_unique,C++11 中没有。
make_unique 不支持指定删除器。如果不用删除器,建议优先使用 make_unique,拥有更高的性能。
unique_ptr<int> p = make_unique<int>(100);
auto p = make_unique<int>(200);
3.unique_ptr不支持拷贝构造和拷贝赋值
由于一个 unique_ptr 拥有它指向的对象,因此 unique_ptr 不支持普通的拷贝构造和拷贝赋值操作:
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("hello"));
unique_ptr<string> p2(p1); // 错误:unique_ptr不支持拷贝构造
unique_ptr<string> p3;
p3 = p1; // 错误:unique_ptr不支持拷贝赋值
return 0;
}
4.unique_ptr支持移动构造和移动赋值
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("hello"));
unique_ptr<string> p2(std::move(p1)); // 移动构造一个新的智能指针对象p2,p1变成空,p2指向该内存
return 0;
}
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("hello"));
unique_ptr<string> p2;
p2 = std::move(p1); // 移动赋值,p1变成空,p2指向该内存
return 0;
}
5.unique_ptr的常用操作
5.1 release()
虽然我们不能拷贝或赋值 unique_ptr,但可以通过调用 release() 或 reset() 将指针的所有权从一个(非const)unique_ptr 转移给另一个 unique_ptr。
p.release()
返回裸指针,同时将该智能指针 p
置空。也就是说,调用 release() 会切断 unique_ptr 和它原来管理的对象间的联系。release() 返回的指针通常被用来初始化另一个智能指针或给另一个智能指针赋值。
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("hello"));
// release()返回p1当前保存的指针,并将p1置为空。p2被初始化为p1原来保存的指针,即将所有权从p1转移给p2
unique_ptr<string> p2(p1.release());
return 0;
}
在上面代码中,管理内存的责任简单地从一个智能指针转移给另一个。但是,如果我们不用另一个智能指针来保存release() 返回的指针,我们的程序就要负责资源的释放:
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("hello"));
p1.release(); // 错误,导致内存泄漏
return 0;
}
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("hello"));
string* p = p1.release(); // 可以简写为auto p = p1.release();
delete p; // 手动delete释放
return 0;
}
5.2 reset()
若 reset 不带参数:释放智能指针所指向的对象,并将智能指针置空。
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("hello"));
p1.reset();
return 0;
}
若 reset 带参数:释放智能指针所指向的对象,并让该智能指针指向新对象。
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("hello"));
p1.reset(new string("world"));
return 0;
}
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("hello"));
unique_ptr<string> p2(new string("world"));
p1.reset(p2.release());
// release()返回p2当前保存的指针,并将p2置为空。
// reset()释放p1指向的对象内存,让p1指向p2所指向的内存,即将p2对指针的所有权转移给p1
return 0;
}
5.3 = nullptr
p = nullptr
:释放智能指针所指向的对象,并将智能指针置空。
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p(new string("hello"));
p = nullptr; // 释放p所指向的对象,并将p置空
return 0;
}
5.4 指向一个数组
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<int[]> p(new int[10]);
p[0] = 110;
p[1] = 120;
return 0;
}
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
};
int main()
{
unique_ptr<A[]> p(new A[10]); // 如果有自己的析构函数,那么定义时<>里面必须加上[],否则会报异常
return 0;
}
5.5 get()
p.get()
:返回智能指针 p
中保存的裸指针。
因为有些第三方库的函数参数需要的是内置裸指针,所以引入该函数。
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> up(new string("hello"));
string* p = up.get();
*p = "world";
//delete p; // 这里不要delete,否则会产生不可预料的后果
return 0;
}
5.6 解引用
*p
解引用:获取该智能指针指向的对象,可以直接操作。
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("hello"));
*p1 = "world";
unique_ptr<int> p2(new int(100));
*p2 = 200;
unique_ptr<int[]> p(new int[10]); // 如果定义的内容是数组,没有解引用运算符
return 0;
}
5.7 swap()
交换两个智能指针所指向的对象。
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("hello"));
unique_ptr<string> p2(new string("world"));
p1.swap(p2); // 等价于std::swap(p1, p2);
return 0;
}
5.8 智能指针名字作为判断条件
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> p(new string("hello"));
if (p) cout << "p不为空" << endl;
else cout << "p为空" << endl;
p.reset();
if (p) cout << "p不为空" << endl;
else cout << "p为空" << endl;
return 0;
}
5.9 转换成shared_ptr类型
如果 unique_ptr 为右值,就可以将它赋值给 shared_ptr。因为 shared_ptr 包含一个显式构造函数,可用于将右值 unqiue_ptr 转换为 shared_ptr,shared_ptr 将接管原来归 unique_ptr 所拥有的对象。
#include <iostream>
#include <memory>
using namespace std;
auto myfunc()
{
return unique_ptr<string>(new string("hello")); // 临时对象都是右值
}
int main()
{
shared_ptr<string> sp = myfunc(); // 这里系统会为shared_ptr创建控制块
return 0;
}
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<string> up(new string("hello"));
shared_ptr<string> sp = std::move(up);
return 0;
}
6.返回unique_ptr
不能拷贝 unique_ptr 的规则有一个例外:我们可以拷贝或赋值一个将要被销毁的 unique_ptr。最常见的例子是从函数返回一个 unique_ptr。
#include <iostream>
#include <memory>
using namespace std;
unique_ptr<int> fun(int value)
{
unique_ptr<int> p(new int(value));
return p; // 返回这种局部对象,导致系统生成一个临时unique_ptr对象,调用unique_ptr的移动构造函数
// 上面两行和下面一行是等价的
// return unique_ptr<int>(new int(value));
}
int main()
{
unique_ptr<int> up = fun(120);
// 如果用up来接,则临时对象直接构造在up里。
// 如果不接,则临时对象会被释放,同时会释放掉所指向的对象的内存。
return 0;
}
7.指定删除器
unique_ptr 指定删除器,先要在类型模板参数中传递进去类型名,然后在参数中再给具体的删除器函数名。
举例1:
#include <iostream>
#include <memory>
using namespace std;
void mydeleter(string* pdel)
{
delete pdel;
pdel = nullptr;
}
int main()
{
typedef void(*fp)(string*); // 定义一个函数指针类型,类型名为fp
unique_ptr<string, fp> up(new string("hello"), mydeleter);
return 0;
}
举例2:
#include <iostream>
#include <memory>
using namespace std;
void mydeleter(string* pdel)
{
delete pdel;
pdel = nullptr;
}
int main()
{
using fp = void(*)(string*); // 用using定义一个函数指针类型,类型名为fp
unique_ptr<string, fp> up(new string("hello"), mydeleter);
return 0;
}
举例3:
#include <iostream>
#include <memory>
using namespace std;
void mydeleter(string* pdel)
{
delete pdel;
pdel = nullptr;
}
int main()
{
typedef decltype(mydeleter)* fp; // decltype返回的是函数类型void(string*),加*表示函数指针类型void(*)(string*),类型名为fp
unique_ptr<string, fp> up(new string("hello"), mydeleter);
return 0;
}
举例4:
#include <iostream>
#include <memory>
using namespace std;
void mydeleter(string* pdel)
{
delete pdel;
pdel = nullptr;
}
int main()
{
unique_ptr<string, decltype(mydeleter)*> up(new string("hello"), mydeleter);
return 0;
}
举例5:
#include <iostream>
#include <memory>
using namespace std;
int main()
{
auto mylambda = [](string* pdel) {
delete pdel;
pdel = nullptr;
};
unique_ptr<string, decltype(mylambda)> up(new string("hello"), mylambda);
return 0;
}
即使两个 shared_ptr 指定的删除器不相同,但只要他们所指向的对象相同,那么这两个 shared_ptr 也属于同一个类型,即可以放到同一个容器里。
但是 unique_ptr 不一样,如果 unique_ptr 指定的删除器不同,则相当于 unique_ptr 的类型不同,即不能放到同一个容器里。
8.unique_ptr的尺寸
通常情况下,unique_ptr 的尺寸(大小)跟裸指针一样。
如果增加了自己的删除器,则 unique_ptr 的尺寸可能增加,也可能不增加。
- 如果 lambda 表达式作为删除器,尺寸就没变化。
- 如果定义一个函数作为删除器,尺寸发生变化。
shared_ptr 不管指定什么删除器,shared_ptr 的尺寸都是裸指针的 2 倍。
#include <iostream>
#include <memory>
using namespace std;
int main()
{
string* p;
cout << sizeof(p) << endl; // x86: 4字节,x64:8字节
return 0;
}
#include <iostream>
#include <memory>
using namespace std;
int main()
{
auto mylambda = [](string* pdel) {
delete pdel;
pdel = nullptr;
};
unique_ptr<string, decltype(mylambda)> up(new string("hello"), mylambda);
cout << sizeof(up) << endl; // x86: 4字节,x64:8字节
return 0;
}
#include <iostream>
#include <memory>
using namespace std;
void mydeleter(string* pdel)
{
delete pdel;
pdel = nullptr;
}
int main()
{
typedef void(*fp)(string*); // 定义一个函数指针类型,类型名为fp
unique_ptr<string, fp> up(new string("hello"), mydeleter);
cout << sizeof(up) << endl; // x86: 8字节,x64:16字节
return 0;
}