std::unique_ptr 学习笔记
unique_ptr
是不共享的智能指针。其不能被拷贝,不能通过传值的方式作为参数传递给函数,也不能用于 STL 中需要 copy 的算法中。
unique_ptr
能被 move。当 unique_ptr
被 move 时,unique_ptr
中内存资源的属主会被转移。
比较推荐的做法是一个对象有一个属主,因为一个对象有多个属主时会增加程序逻辑的复杂性,例如多线程时会有竞争。
右边的图例举了一个属主转移的例子;刚开始对象的属主是 ptrA,当执行 move 后,对象属主变为了 ptrB。
unique_ptr
定义在 <memory>
头文件中。其效率和原始指针差不多,也支持 *
和 →
运算符。
构造 unique_ptr
时,可以使用帮助函数 make_unique
。例如:
auto ptrA = make_unique<int>(10); |
Examples
如何创建 unique_ptr 实例
unique_ptr<Song> SongFactory(const std::wstring& artist, const std::wstring& title)
{
// Implicit move operation into the variable that stores the result.
return make_unique<Song>(artist, title);
}
void MakeSongs()
{
// Create a new unique_ptr with a new object.
auto song = make_unique<Song>(L"Mr. Children", L"Namonaki Uta");
// Use the unique_ptr.
vector<wstring> titles = { song->title };
// Move raw pointer from one unique_ptr to another.
unique_ptr<Song> song2 = std::move(song);
// Obtain unique_ptr from function that returns by value.
auto song3 = SongFactory(L"Michael Jackson", L"Beat It");
}如何在容器中使用 unique_ptr
void SongVector()
{
vector<unique_ptr<Song>> songs;
// Create a few new unique_ptr<Song> instances
// and add them to vector using implicit move semantics.
songs.push_back(make_unique<Song>(L"B'z", L"Juice"));
songs.push_back(make_unique<Song>(L"Namie Amuro", L"Funky Town"));
songs.push_back(make_unique<Song>(L"Kome Kome Club", L"Kimi ga Iru Dake de"));
songs.push_back(make_unique<Song>(L"Ayumi Hamasaki", L"Poker Face"));
// Pass by const reference when possible to avoid copying.
for (const auto& song : songs)
{
wcout << L"Artist: " << song->artist << L" Title: " << song->title << endl;
}
}将 unique_ptr 作为类成员如何使用
class MyClass
{
private:
// MyClass owns the unique_ptr.
unique_ptr<ClassFactory> factory;
public:
// Initialize by using make_unique with ClassFactory default constructor.
MyClass() : factory (make_unique<ClassFactory>())
{
}
void MakeClass()
{
factory->DoSomething();
}
};unique_ptr 数组如何使用
可以创建 unique_ptr 数组,但是不能使用 make_unique 来初始化数组元素
// Create a unique_ptr to an array of 5 integers.
auto p = make_unique<int[]>(5);
// Initialize the array.
for (int i = 0; i < 5; ++i)
{
p[i] = i;
wcout << p[i] << endl;
}make_unique 使用
class Animal
{
private:
std::wstring genus;
std::wstring species;
int age;
double weight;
public:
Animal(const wstring&, const wstring&, int, double){/*...*/ }
Animal(){}
};
void MakeAnimals()
{
// Use the Animal default constructor.
unique_ptr<Animal> p1 = make_unique<Animal>();
// Use the constructor that matches these arguments
auto p2 = make_unique<Animal>(L"Felis", L"Catus", 12, 16.5);
// Create a unique_ptr to an array of 5 Animals
unique_ptr<Animal[]> p3 = make_unique<Animal[]>(5);
// Initialize the elements
p3[0] = Animal(L"Rattus", L"norvegicus", 3, 2.1);
p3[1] = Animal(L"Corynorhinus", L"townsendii", 4, 1.08);
// auto p4 = p2; //C2280
vector<unique_ptr<Animal>> vec;
// vec.push_back(p2); //C2280
// vector<unique_ptr<Animal>> vec2 = vec; // C2280
// OK. p2 no longer points to anything
vec.push_back(std::move(p2));
// unique_ptr overloads operator bool
wcout << boolalpha << (p2 == false) << endl; // Prints "true"
// OK but now you have two pointers to the same memory location
Animal* pAnimal = p2.get();
// OK. p2 no longer points to anything
Animal* p5 = p2.release();
}
代码解读(MSVC SDK 10.0.19041.0)
unique_ptr 是类模板,其模板参数包括底层指针的类型和 deleter,deleter 是可选参数。
unique_ptr 支持两种类型,一种是管理单个对象,一种是管理对象数组。
template <class _Ty, class _Dx /* = default_delete<_Ty> */> |
智能指针符合 RAII。所以在构造函数中会申请资源,而在析构函数中会释放资源。unique_ptr 自身是一个类,在使用时通常会在一个域类创建栈对象,然后超出 scope 时会自动调用析构函数释放资源。
// 不带参数的构造函数 |
因为 unique_ptr 是不能够被拷贝的,所以在代码实现上,其拷贝构造函数和拷贝赋值函数都被设置成了 delete
unique_ptr(const unique_ptr&) = delete; |
unique_ptr 是支持移动操作的,其内部实现了移动构造函数和移动赋值函数。
// 移动构造函数 |
unique_ptr 中只有一个成员,就是底层的指针,以下图片展示了一个指向 int 类型的一个 unique_ptr,其在 x86 编译下大小为 4。图片中箭头所指的就是 MSVC 中 unique_ptr 的底层指针。
|
对于 unique_ptr,如果 T 是某个基类 B 的派生类, std::unique_ptr
unique_ptr 也可能是空指针,为了方便直接使用智能指针进行 bool 判断,unique_ptr 中定义了 operator bool。其中 _Mypair._Myval2
为底层指针。
explicit operator bool() const noexcept { |
unique_ptr 可以通过 get
方法获取底层指针。通过 get_deleter
获取 deleter。
_NODISCARD pointer get() const noexcept { |
unique_ptr 也可以想原始指针一样,使用 * 运算符进行解引用,使用 → 运算符进行操作,其底层实现是直接将符号作用于底层指针。
_NODISCARD add_lvalue_reference_t<_Ty> operator*() const noexcept /* strengthened */ { |
unique_ptr 还支持释放,重置和交换
// 释放底层指针的资源并返回资源 |