C++中智能指针unique_ptr的实现详解
前言
智能指针本质上并不神秘,其实就是 RAII 资源管理功能的自然展现而已。本文将介绍如何实现 C++中智能指针的 unique_ptr。
实现过程
首先,unique_ptr 能够包装任意类型,所以,要让这个类能够包装任意类型的指针,我们需要把它变成一个类模板。
template <typename T> class unique_ptr { public: explicit unique_ptr(T* ptr = nullptr) : ptr_(ptr) {} ~unique_ptr() { delete ptr_; } T* get() const { return ptr_; } private: T* ptr_; };
目前这个 unique_ptr
的行为还是和指针有点差异的:
- 它不能用
*
运算符解引用 - 它不能用
->
运算符指向对象成员 - 它不能像指针一样用在布尔表达式里
不过,这些问题也相当容易解决,加几个成员函数就可以:
template <typename T> class unique_ptr { public: … T& operator*() const { return *ptr_; } T* operator->() const { return ptr_; } operator bool() const { return ptr_; } }
拷贝构造和赋值
最简单的情况显然是禁止拷贝。我们可以使用下面的代码:
template <typename T> class unique_ptr { … unique_ptr(const unique_ptr&) = delete; unique_ptr& operator=(const unique_ptr&) = delete; … };
禁用这两个函数非常简单,但是,有时我们需要转移所有权,那么这个方案就不可行了。
这里,我们采用移动语义:
template <typename T> class unique_ptr { … unique_ptr(unique_ptr&& other) { ptr_ = other.release(); } unique_ptr& operator=(unique_ptr rhs) { rhs.swap(*this); return *this; } … };
- 把拷贝构造函数中的参数类型
unique_ptr&
改成了unique_ptr&&
;现在它成了移动构造函数。 - 把赋值函数中的参数类型
unique_ptr&
改成了unique_ptr
,在构造参数时直接生成新的智能指针,从而不再需要在函数体中构造临时对象。现在赋值函数的行为是移动还是拷贝,完全依赖于构造参数时走的是移动构造还是拷贝构造。 根据 C++ 的规则,如果我提供了移动构造函数而没有手动提供拷贝构造函数,那后者自动被禁用。
子类指针向基类指针的转换
一个 circle*
类是可以隐式转换成 shape*
类的,但上面的 unique_ptr<circle>
却无法自动转换成 unique_ptr<shape>
。
不过,只需要额外加一点模板代码,就能实现这一行为。在我们目前给出的实现里,只需要增加一个构造函数即可。
template <typename U> unique_ptr(unique_ptr<U>&& other) { ptr_ = other.release(); }
这样,我们自然而然利用了指针的转换特性:现在 unique_ptr<circle>
可以移动给 unique_ptr<shape>
,但不能移动给 unique_ptr<triangle>
。不正确的转换会在代码编译时直接报错。
验证
unique_ptr<shape> ptr1{create_shape(shape_type::circle)}; unique_ptr<shape> ptr2{ptr1}; // 编译出错 unique_ptr<shape> ptr3; ptr3 = ptr1; // 编译出错 ptr3 = std::move(ptr1); // OK,可以 unique_ptr<shape> ptr4{std::move(ptr3)}; // OK,可以
完整代码
#include <utility> template <typename T> class unique_ptr { public: explicit unique_ptr(T* ptr = nullptr) : ptr_(ptr) {} ~unique_ptr() { delete ptr_; } unique_ptr(unique_ptr&& other) { ptr_ = other.release(); } // 子类指针向基类指针的转换 template <typename U> unique_ptr(unique_ptr<U>&& other) { ptr_ = other.release(); } unique_ptr& operator=(unique_ptr rhs) { rhs.swap(*this); return *this; } T* release() { T* ptr = ptr_; ptr_ = nullptr; return ptr; } void swap(unique_ptr& rhs) { using std::swap; swap(ptr_, rhs.ptr_); } T* get() const { return ptr_; } T& operator*() const { return *ptr_; } T* operator->() const { return ptr_; } operator bool() const { return ptr_; } private: T* ptr_; };
总结
自行实现一个 unique_ptr 相对比较简单,因为不涉及引用计数,只需要一个对象只能被单个 unique_ptr
所拥有。
以上就是C++中智能指针unique_ptr的实现详解的详细内容,更多关于C++智能指针unique_ptr的资料请关注脚本之家其它相关文章!
最新评论