title: C++ 智能指针实现之unique_ptr
description: 智能指针本质上就是利用 RAII 资源管理功能,本文介绍实现 C++中智能指针的 unique_ptr。
date: 2024-01-11T16:40:00+08:00
categories:
- C++
tags: - C++
- 智能指针
keywords: - unique_ptr
- C++智能指针
- C++智能指针原理
image: bg.webp
hidden: false
comments: true
lastmod: 2024-01-11T18:19:07+08:00
前言
智能指针本质上并不神秘,其实就是 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_; };
目前这个
- 它不能用
* 运算符解引用 - 它不能用
-> 运算符指向对象成员 - 它不能像指针一样用在布尔表达式里
不过,这些问题也相当容易解决,加几个成员函数就可以:
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++ 的规则,如果我提供了移动构造函数而没有手动提供拷贝构造函数,那后者自动被禁用。
子类指针向基类指针的转换
一个
不过,只需要额外加一点模板代码,就能实现这一行为。在我们目前给出的实现里,只需要增加一个构造函数即可。
template <typename U> unique_ptr(unique_ptr<U>&& other) { ptr_ = other.release(); }
这样,我们自然而然利用了指针的转换特性:现在
验证
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 相对比较简单,因为不涉及引用计数,只需要一个对象只能被单个
《现代 C++编程实战》