程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

如何实现自己的C++ unique_ptr?

balukai 2025-08-02 17:30:57 文章精选 5 ℃

unique_ptr简介

只要用了C++11以及以上,我都推荐你尽可能的使用智能指针代替裸指针,今天我们要讲的是独占所有权的unique_ptr。unique_ptr是通过指针占有并管理另一对象,并在uique_ptr离开作用域时释放该对象的智能指针。

在下列两者之一发生时发生智能指针释放关联对象的资源

  • unique_ptr离开了作用域
  • 通过operator=或reset()赋值另一指针给管理的unique_ptr对象。

unique_ptr独占所有权意味着无法通过复制的方式获取unique_ptr管理的对象指针,因为复制意味着内部对象指针有两个副本,互相不知道所指对象资源是否已释放,只能通过移动的方式获取对象的所有权,具体表现为unique_ptr的复制构造函数和复制运算符函数都是delete。

当创建unique_ptr类实例变量时若不指定对象删除器,则使用默认的删除器。

实现自己的unique_ptr

根据unique_ptr基本功能,自定义unique_ptr类模板声明如下:

#ifndef UNIQUE_PTR_H
#define UNIQUE_PTR_H

namespace cpp_lib{

class Deletor{
public:
Deletor() = default;

template <typename U>
void operator()(U *p) noexcept{
if(p){
delete p;
}
}
};

template <typename T, typename U = Deletor>
class unique_ptr{
public:
explicit unique_ptr(); //显示构造函数
explicit unique_ptr(T* p); //显示构造函数
explicit unique_ptr(unique_ptr&& p); //移动构造函数
 void operator=(unique_ptr&& p); //移动赋值运算符
 ~unique_ptr(); //析构函数

 unique_ptr(const unique_ptr& p) = delete; //不支持复制构造函数
 unique_ptr operator=(const unique_ptr& p) = delete; //不支持复制运算符函数
 unique_ptr operator=(T* p) = delete; //不支持裸指针赋值

 T* release();

 void reset(T* p = nullptr) noexcept;

 void swap(unique_ptr& rhs) noexcept;

 T* get() noexcept;

 U& get_deletor() noexcept;

 explicit operator bool() const noexcept;

 T& operator*() const noexcept;

 T* operator->() const noexcept;

private:
 T *ptr; //值指针
 U deletor; //删除器
};

}

#endif // UNIQUE_PTR_H

这里自定义了新的命名空间cpp_lib主要是为了防止和标准库里的unique_ptr冲突。

自定义unique_ptr构造函数定义如下:

template <typename T, typename U>
unique_ptr<T, U>::unique_ptr() : ptr(nullptr) { }

template <typename T, typename U>
unique_ptr<T, U>::unique_ptr(T *p) : ptr(p) { }

template <typename T, typename U>
unique_ptr<T, U>::unique_ptr(unique_ptr&& p){
ptr = std::forward<T*>(p.ptr);
deletor = std::forward<U>(p.deletor);
p.ptr = nullptr;
}

移动构造函数中,被移动的对象已经失去了所有权,内部的对象指针一定要置为nullptr,否则错误使用会发生程序奔溃且调用operator bool()无法正确判断对象指针是否为空。

自定义unique_ptr移动赋值运算符函数定义:

template <typename T, typename U>
void unique_ptr<T, U>::operator=(unique_ptr&& p){
ptr = std::forward<T*>(p.ptr);
deletor = std::forward<U>(p.deletor);
p.ptr = nullptr;
}

和移动构造函数很相似,被移动的对象已经失去了所有权,内部的对象指针一定要置为nullptr,否则错误使用会发生程序奔溃且调用operator bool()无法正确判断对象指针是否为空。

自定义unique_ptr析构函数定义如下所示:

template <typename T, typename U>
unique_ptr<T, U>::~unique_ptr(){
if(ptr){
deletor(ptr);
}
ptr = nullptr;
}

在析构函数里,主要工作是释放对象的资源。

自定义unique_ptr其他成员函数定义如下:

template <typename T, typename U>
T* unique_ptr<T, U>::release(){
T* p = ptr;
ptr = nullptr;
return p;
}

template <typename T, typename U>
void unique_ptr<T, U>::reset(T* p) noexcept{
if(ptr){
deletor(ptr);
}
ptr = p;
}

template <typename T, typename U>
void unique_ptr<T, U>::swap(unique_ptr& rhs) noexcept{
std::swap(ptr, rhs.ptr);
std::swap(deletor, rhs.deletor);
}

template <typename T, typename U>
T* unique_ptr<T, U>::get() noexcept{
return ptr;
}

template <typename T, typename U>
U& unique_ptr<T, U>::get_deletor() noexcept{
return deletor;
}

template <typename T, typename U>
unique_ptr<T, U>::operator bool() const noexcept{
if(ptr){
return true;
}else{
return false;
}
}

template <typename T, typename U>
T& unique_ptr<T, U>::operator*() const noexcept{
return *ptr;
}

template <typename T, typename U>
T* unique_ptr<T, U>::operator->() const noexcept{
return ptr;
}

自定义unique_ptr测试

下面我们写一个小程序来测试下自己实现的unique_ptr,代码如下:

#include <iostream>
#include "unique_ptr.cpp"

class Widget{
public:
Widget(){
std::cout << "Widget::constructor" << std::endl;
}
~Widget(){
std::cout << "Widget::destructor" << std::endl;
}
void fun(){
std::cout << "Widget::fun" << std::endl;
}
};

class Deletor{
public:
template <typename T>
void operator()(T* p) noexcept{
std::cout << "delete pointer" << std::endl;
if(p){
delete p;
}
}
};

int main()
{
cpp_lib::unique_ptr<int> ptr(new int(1));

*ptr = 2;
if(ptr){
std::cout << "ptr is not null" << std::endl;
}

std::cout << *ptr << std::endl;
ptr.reset();
if(!ptr){
std::cout << "ptr is null" << std::endl;
}

cpp_lib::unique_ptr<Widget> w_ptr(new Widget());
w_ptr->fun();

cpp_lib::unique_ptr<Widget> w_ptr2;
w_ptr2.reset(new Widget());
w_ptr2->fun();
auto p = w_ptr.release();
p->fun();
delete p;

std::cout << "=========" << std::endl;
cpp_lib::unique_ptr<Widget> w_ptr3(new Widget());
cpp_lib::unique_ptr<Widget> w_ptr4;
w_ptr4.swap(w_ptr3);
w_ptr4->fun();

std::cout << "=========" << std::endl;
w_ptr3 = std::move(w_ptr4);
w_ptr3->fun();
std::cout << "#########" << std::endl;

std::cout << "=========" << std::endl;
cpp_lib::unique_ptr<Widget> w_ptr5(std::move(w_ptr3));
w_ptr5->fun();
std::cout << "#########" << std::endl;

cpp_lib::unique_ptr<Widget, Deletor> w_ptr6(new Widget());

return 0;
}

编译运行输出如下:

ptr is not null
2
ptr is null
Widget::constructor
Widget::fun
Widget::constructor
Widget::fun
Widget::fun
Widget::destructor
=========
Widget::constructor
Widget::fun
=========
Widget::fun
#########
=========
Widget::fun
#########
Widget::constructor
delete pointer
Widget::destructor
Widget::destructor
Widget::destructor

程序输出正确,且cpp_lib::unique_ptr离开作用于后自动调用析构函数释放所管理对象的资源。

Tags:

最近发表
标签列表