C++ 智能指针unique_ptr原理与自定义实现

分析

智能指针最常用的,就是shared_ptr和unique_ptr。第一个,我在另一个文章已介绍(https://blog.csdn.net/newchenxf/article/details/116019535)。本文来单独说以下unique_ptr。

先说其特点
独享它指向的对象。也就是说,同时只有一个unique_ptr指向同一个对象,当这个unique_ptr被销毁时,指向的对象也随即被销毁
这也是它和shared_ptr不一样的地方。它不需要做引用计数,也不可以被第二个人引用。只有它自己。如果想引用?没用的,编译都编译不过!

再说其典型用途

  1. 作为一个类的成员变量,这个变量只在本类使用,不会被赋值给其他类,也不会作为参数传递给某个函数
  2. 在一个函数作为局部变量,使用完就不用再管,函数结束,自动释放托管资源

最后说原理:
a) 构造时传入托管对象的指针,析构时delete对象
b) 禁用赋值函数

a) 是为了要对得其智能指针的名号,即,帮你主动删除托管对象。

b) 是为了要对得起unique的名号,唯一啊唯一,那就禁用赋值,不能再让别人引用它!

	unique_ptr(const unique_ptr<T>&) noexcept = delete;
	unique_ptr& operator = (const unique_ptr&) noexcept = delete;

b怎么实现呢,如上,很简单,把两种赋值函数,都设置为delete。
使用"=delete"修饰,表示函数被定义为deleted,也就意味着这个成员函数不能再被调用,否则编译就会出错。

当然了,虽然赋值禁用了,但是愿意交出控制权,交给赋值的人,那还是可以允许的。

unique_ptr(unique_ptr&& move) noexcept
	{
        std::cout << "construct for unique_ptr&&" << std::endl;
		move.swap(*this);
	}
	unique_ptr& operator=(unique_ptr&& move) noexcept
	{
		move.swap(*this);
		return *this;
	}

这参数move的类型,是一个右值引用,其实就是std::move的返回值。举个例子就明白了

unique_ptr<Test> tPtr1(new Test());
unique_ptr<Test> tPtr3(std::move(tPtr1));

这种情况下,是可以编译过的,只不过,tPtr1的资源,如其成员变量,将无法再调用,用的话就崩溃。因为它已经全部交给tPtr3。

代码

自己实现代码,可能不够严谨,但是作为理解智能指针的原理,很有用哈。
两个文件,一个头文件,一个main。
编译:
g++ main.cpp -o out
执行
./out

头文件UniquePtr.h:

#include <utility>
#include<iostream>


/****
 * 智能指针unique_ptr的简单实现
 * 
 * 特点:独享它指向的对象。也就是说,同时只有一个unique_ptr指向同一个对象,当这个unique_ptr被销毁时,指向的对象也随即被销毁
 * 
 * 典型用途:
 * 1. 在一个函数定义一个A* ptr = new A(), 结束还需要用delete,而用unique_ptr,就不需要自己调用delete
 * 2. 作为一个类的变量,这个变量只在本类使用,不会被其他类调用,也不会作为参数传递给某个函数
 * */
template<typename T>
class unique_ptr
{
private:
	T * ptr_resource = nullptr;

public:
    //explicit构造函数是用来防止隐式转换, 即不允许写成unique_ptr<T> tempPtr = T;
    //std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能.
    //move之后,raw_resource内部的资源将不能再被raw_resource使用
	explicit unique_ptr(T* raw_resource) noexcept : ptr_resource(std::move(raw_resource)) {}
	unique_ptr(std::nullptr_t) : ptr_resource(nullptr) {}

	unique_ptr() noexcept : ptr_resource(nullptr) {}

	//析构时, 释放托管的对象资源
	~unique_ptr() noexcept
	{
		delete ptr_resource;
	}
	// Disables the copy/ctor and copy assignment operator. We cannot have two copies exist or it'll bypass the RAII concept.
    //重要,禁止两种拷贝的赋值方式
    //使用"=delete"修饰,表示函数被定义为deleted,也就意味着这个成员函数不能再被调用,否则就会出错。
	unique_ptr(const unique_ptr<T>&) noexcept = delete;
	unique_ptr& operator = (const unique_ptr&) noexcept = delete;
	
public:
    //&& 是右值引用,见https://zhuanlan.zhihu.com/p/107445960
	// 允许移动语义。虽然无法复制unique_ptr,但可以安全地移动。
    //例子:unique_ptr<Test> tPtr3(std::move(tPtr1));
	unique_ptr(unique_ptr&& move) noexcept
	{
        std::cout << "construct for unique_ptr&&" << std::endl;
		move.swap(*this);
	}
	// ptr = std::move(resource)
	unique_ptr& operator=(unique_ptr&& move) noexcept
	{
        std::cout << "operator= for unique_ptr&&" << std::endl;

		move.swap(*this);
		return *this;
	}

	explicit operator bool() const noexcept
	{
		return this->ptr_resource;
	}
	// releases the ownership of the resource. The user is now responsible for memory clean-up.
	T* release() noexcept
	{
		return std::exchange(ptr_resource, nullptr);
	}
	// returns a pointer to the resource
	T* get() const noexcept
	{
		return ptr_resource;
	}
	// swaps the resources
	void swap(unique_ptr<T>& resource_ptr) noexcept
	{
		std::swap(ptr_resource, resource_ptr.ptr_resource);
	}
	// reset就删除老的,指向新的
	void reset(T* resource_ptr) noexcept(false)
	{
		// ensure a invalid resource is not passed or program will be terminated
		if (resource_ptr == nullptr)
			throw std::invalid_argument("An invalid pointer was passed, resources will not be swapped");

		delete ptr_resource;

		ptr_resource = nullptr;

		std::swap(ptr_resource, resource_ptr);
	}
public:
	// overloaded operators
	T * operator->() const noexcept
	{
		return this->ptr_resource;
	}
	T& operator*() const noexcept
	{
		return *this->ptr_resource;
	}
	// 额外说明noexcept
    //noexcept C++11关键字, 告诉编译器,函数中不会发生异常,有利于编译器对程序做更多的优化
    //C++中的异常处理是在运行时而不是编译时检测的。为了实现运行时检测,编译器创建额外的代码,然而这会妨碍程序优化
};

主程序main.cpp

#include "UniquePtr.h"
/**
 * 简单的类,将被智能指针使用
 * */
class Test {
public:
	Test() {
		std::cout << "Test class construct" << std::endl;
	}
	~Test() {
		std::cout << "Test class destruct" << std::endl;
	}

	void printSomething() {
		std::cout << "Test printSomething " << std::endl;
	}

    void printResource() {
		std::cout << "Test printResource " << a << std::endl;
	}

    int getResource() {
        return a;
    }

private:
    int a = 10;
};

/**
 * 使用unique_ptr的类
 * */
class PUser {
public:
	PUser() {
        //初始化pTest
        pTest.reset(new Test());
		std::cout << "PUser construct " << std::endl;
	}
	~PUser() {
		std::cout << "PUser destruct" << std::endl;
	}

    //可以在类的各种函数,使用pTest,
	void userTest() {
		std::cout << "userTest " << pTest->getResource() << std::endl;
	}

private:
    //典型用法,在一个类中,作为一个类成员变量
    unique_ptr<Test> pTest;
};



/**
 * 主程序入口
 * */
int main(int argc, char* argv[]) {
    unique_ptr<Test> tPtr1(new Test());
    //以下这两句话,//编译就不通过,因为已经定义, unique_ptr& operator = (const unique_ptr&) noexcept = delete;
    //unique_ptr<Test> tPtr2 = tPtr1;
    //unique_ptr<Test> tPtr3(tPtr1);
    
    //以下两句话就允许,因为pPtr1做了控制权转移
    unique_ptr<Test> tPtr3(std::move(tPtr1));
    unique_ptr<Test> tPtr4 = std::move(tPtr3);

    //tPtr1->printResource();//这一句就崩溃,因为tPtr1非空,只不过资源完全不能用了
    tPtr1->printSomething();//这一句不崩溃,tPtr1虽然资源不能用,但是代码段可以调用,只要代码段没有使用到资源


    PUser* pUser = new PUser();
    pUser->userTest();

    return 0;
}

参考

https://github.com/laxodev/Unique-pointer-implementation

  • 16
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
unique_ptrC++11 中引入的智能指针,用于管理动态分配的对象。 unique_ptr 的特性是:它是唯一拥有(unique ownership)被管理对象的智能指针,也就是说,同一时间只能有一个 unique_ptr 指向一个对象。当 unique_ptr 被销毁时,它会自动释放所管理的对象内存。 下面是 unique_ptr 的简单实现: ```cpp template <typename T> class unique_ptr { public: unique_ptr(T* ptr = nullptr) : m_ptr(ptr) {} ~unique_ptr() { delete m_ptr; } unique_ptr(const unique_ptr&) = delete; unique_ptr& operator=(const unique_ptr&) = delete; unique_ptr(unique_ptr&& other) noexcept : m_ptr(other.m_ptr) { other.m_ptr = nullptr; } unique_ptr& operator=(unique_ptr&& other) noexcept { if (this != &other) { delete m_ptr; m_ptr = other.m_ptr; other.m_ptr = nullptr; } return *this; } T* get() const { return m_ptr; } T* operator->() const { return m_ptr; } T& operator*() const { return *m_ptr; } private: T* m_ptr; }; ``` 这是一个简化版本的 unique_ptr,它包含了基本的功能,如构造函数、析构函数、移动构造函数、移动赋值运算符,以及 get()、operator->() 和 operator*() 方法。 需要注意的是,这个实现并不完整,只是为了演示 unique_ptr 的基本原理和用法。实际使用时,应该考虑更多的细节,如空指针检查、自定义删除器等。另外,C++11 中已经提供了标准库中的 unique_ptr 实现,我们通常会使用标准库中的智能指针而不是自己实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

newchenxf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值