C++简单实现shared_ptr的代码

 更新时间:2022年09月29日 09:59:26   作者:梦想是优秀社畜  
智能指针用于资源管理,为了保证资源的操作得到顺利的执行防止资源泄露,因此大多数实现都以noexcept在参数列表后声明为不抛出异常,这篇文章主要介绍了C++简单实现shared_ptr的代码,需要的朋友可以参考下

一、一些说明

1.智能指针用于资源管理,为了保证资源的操作得到顺利的执行防止资源泄露,因此大多数实现都以noexcept在参数列表后声明为不抛出异常。

2.对于有些明确不需要更改调用对象内容的成员函数,用const在参数列表后修饰,常量成员函数不能改变调用它的对象的内容。

3.在用原始指针构造智能指针的参数构造前用explicit声明,防止隐式的转化。

3.swap函数内部调用标准命名空间的swap()函数,必须以std::显示指定是标准命名空间

4.做笔记使用,如果有不足或者错误的地方还请补充或者指正!!多谢!!!

二、基本的构造和析构成分

SharedPtr()noexcept = default // 默认构造
explicit SharedPtr(T* ptr) noexcept // 参数构造
SharedPtr(const SharedPtr& ptr)noexcept // 拷贝构造
SharedPtr(SharedPtr&& ptr)noexcept // 移动构造
~SharedPtr() noexcept // 析构函数

三、运算符重载

SharedPtr& operator=(const SharedPtr& rhs)noexcept  // 拷贝赋值运算符重载
SharedPtr& operator=(SharedPtr&& rhs)noexcept  // 移动赋值运算符重载
T& operator->() const noexcept // 指针运算符重载
T& operator*() const noexcept // 解引用运算符重载

四、实现的shared_ptr函数接口

T* get() const noexcept // 获取原始指针对象
int use_count() const noexcept // 获取引用计数
void reset()noexcept // 重置指针
bool unique() noexcept // 判断是否只有一个使用者
void swap() noexcept // 交换对象内容

五、CPP代码

#include<iostream>
using namespace std;
 
// 引用计数类
class RefCount {
private:
	int count{1};
public:
	int use_count() const noexcept { return count; }
	void dec_count()noexcept { --count; }
	void inc_count()noexcept { ++count; }
};
 
// shared_ptr类模板
template<class T>
class SharedPtr {
private:
	T* origin_ptr{ nullptr }; // 原始指针
	RefCount* refCount{ nullptr }; // 引用计数
public:
	SharedPtr()noexcept = default; // 默认构造
	SharedPtr(nullptr_t)noexcept :SharedPtr() {} // 空指针构造
	explicit SharedPtr(T* ptr)noexcept :origin_ptr(ptr), refCount(nullptr){ // 用原始指针作为参数构造
		if (origin_ptr != nullptr) refCount = new RefCount();
	}
	SharedPtr(const SharedPtr& ptr)noexcept :origin_ptr(ptr.origin_ptr), refCount(ptr.refCount) { // 拷贝构造
		if(origin_ptr != nullptr) refCount->inc_count();
	}
	SharedPtr(SharedPtr&& ptr)noexcept :origin_ptr(ptr.origin_ptr), refCount(ptr.refCount) { // 移动构造
		ptr.origin_ptr = nullptr;
		ptr.refCount = nullptr;
	}
	~SharedPtr()noexcept { // 析构函数
		if (origin_ptr != nullptr && refCount->use_count() == 0) {
			delete origin_ptr;
			delete refCount;
			origin_ptr = nullptr;
			refCount = nullptr;
		}
	}
	SharedPtr& operator=(const SharedPtr& rhs)noexcept { // 拷贝赋值运算符重载
		SharedPtr(rhs).swap(*this);
		return *this;
	}
	SharedPtr& operator=(SharedPtr&& rhs)noexcept { // 移动赋值运算符重载
		SharedPtr(move(rhs)).swap(*this);
		return *this;
	}
	T& operator->() const noexcept{ // 指针运算符重载
		return origin_ptr;
	}
	T& operator*() const noexcept { // 解引用运算符重载
		return *origin_ptr;
	}
 
public: // 以下是提供的接口函数
	T* get()const noexcept { // get()函数获取原始指针对象
		return origin_ptr;
	}
	int use_count() const noexcept { // use_count()函数获取引用计数
		return origin_ptr == nullptr ? 0 : refCount->use_count();
	}
	void reset()noexcept { // reset()重置指针
		SharedPtr().swap(*this);
	}
	void reset(nullptr_t) noexcept {
		reset();
	}
	void reset(T* ptr)noexcept {
		SharedPtr(ptr).swap(*this);
	}
	bool unique()noexcept { // unique() 查看是否资源独有
		return refCount == nullptr ? false : (refCount->use_count() == 1);
	}
	void swap(SharedPtr& rhs) noexcept{ // swap()交换对象内容
		std::swap(origin_ptr, rhs.origin_ptr); // 这里必须显示指定是std命名空间!
		std::swap(refCount, rhs.refCount);
	}
};
int main() {
	int* a = new int(2);
	SharedPtr<int>p(a); //测试构造函数
	cout << p.use_count() << endl;
	cout << *p << endl;
	SharedPtr<int>q(p); //测试拷贝构造
	cout << p.use_count() << endl;
	cout << q.use_count() << endl;
	SharedPtr<int>n = p; //测试赋值运算符
	cout << p.use_count() << endl;
	cout << n.use_count() << endl;
	SharedPtr<int>m = move(p); //测试右值引用赋值运算符
	cout << q.use_count() << endl;
	// cout << p.get_count() << endl;
	q.reset();
	cout << q.get() << endl;
	return 0;
}

C++ 简单实现shared_ptr

共享指针

管理指针的存储,提供有限的垃圾回收工具,并可能与其他对象共享该管理。

shared_ptr类型的对象都能够获得指针的所有权并共享该所有权:一旦它们获得所有权,当最后一个所有者释放该所有权时,指针的所有者组就负责删除该所有者。

shared_ptr对象在自身被销毁后,或者一旦其值因赋值操作或显式调用 shared_ptr::reset 而发生更改,就会释放其共同拥有的对象的所有权。一旦通过指针共享所有权的所有shared_ptr对象都释放了此所有权,则将删除托管对象(通常通过调用 ::delete,但在构造时可能会指定不同的删除程序)。

shared_ptr对象只能通过复制其值来共享所有权:如果从同一个(非shared_ptr)指针构造(或创建)两个shared_ptr,则它们都将拥有该指针而不共享它,当其中一个对象释放它(删除其托管对象)并使另一个指向无效位置时,会导致潜在的访问问题。

此外,shared_ptr对象可以通过指针共享所有权,同时指向另一个对象。此功能称为别名,通常用于指向成员对象,同时拥有它们所属的对象。因此,shared_ptr可能与两个指针相关:

  • 存储的指针,它是它所指向的指针,也是它使用运算符* 取消引用的指针。
  • 拥有的指针(可能是共享的),这是所有权组负责在某个时刻删除的指针,并且它被视为一种用途。

通常,存储的指针和拥有的指针引用同一对象,但别名shared_ptr对象(使用别名构造函数及其副本构造的对象)可能引用不同的对象。

不拥有任何指针的shared_ptr称为空shared_ptr。不指向任何对象的shared_ptr称为 null shared_ptr,不应取消引用。请注意,空shared_ptr不一定是 null shared_ptr,null shared_ptr不一定是空shared_ptr。

shared_ptr对象通过运算符 * 和 -> 提供对它们所指向的对象的访问,从而复制有限的指针功能。出于安全原因,它们不支持指针算术。

相关类weak_ptr能够与shared_ptr对象共享指针,而无需拥有它们。

成员函数

	(构造函数)构造shared_ptr(公共成员函数)
	(析构函数)销毁shared_ptr(公共成员函数)
	operator=	shared_ptr赋值(公共成员函数)
	swap	交换内容(公共成员函数)
	reset	重置指针(公共成员函数)
	get	获取指针(公共成员函数)
	operator*	取消引用对象(公共成员函数)
	operator->	取消引用对象成员(公共成员函数)
	operator[]  (C++17)提供到被存储数组的带下标访问(公开成员函数)
	use_count	返回计数(公共成员函数)
	unique(C++20前)	检查是否唯一(公共成员函数)
	operator bool	检查是否不为空(公共成员函数)
	owner_before	基于所有者的共享指针排序(公共成员函数模板)

非成员函数
	swap	交换shared_ptr对象的内容(函数模板)
	relational operators	关系运算符 ==, !=, <, <=, >, >= (函数模板 )
	ostream operator<<	将存储的指针的值输出到输出流(函数模板)

具体功能:
	make_shared,make_shared_for_overwrite(C++20)	创建管理一个新对象的共享指针(函数模板)
	allocate_shared,allocate_shared_for_overwrite(C++20)	创建管理一个用分配器分配的新对象的共享指针(函数模板)
	static_pointer_cast,dynamic_pointer_cast,const_pointer_cast,reinterpret_pointer_cast    (C++17)应用 static_cast、dynamic_cast、const_cast 或 reinterpret_cast 到被存储指针(函数模板)
	get_deleter	返回指定类型中的删除器,若其拥有(函数模板)

cpp

#include <utility>
#include <cstddef>

class ref_count
{
public:
    int use_count() const noexcept { return count_; }
    void inc_ref() noexcept { ++count_; }
    int dec_ref() noexcept { return --count_; }

private:
    int count_{1};
};

template <typename T>
class Shared_ptr
{
public:
    constexpr Shared_ptr() noexcept = default;
    constexpr Shared_ptr(nullptr_t) noexcept : Shared_ptr() {}
    explicit Shared_ptr(T *ptr) : ptr_{ptr}
    {
        if (ptr_ != nullptr)
        {
            rep_ = new ref_count{};
        }
    }
    Shared_ptr(const Shared_ptr &rhs) noexcept : ptr_{rhs.ptr_}, rep_{rhs.rep_}
    {
        if (ptr_ != nullptr)
        {
            rep_->inc_ref();
        }
    }
    Shared_ptr(Shared_ptr &&rhs) noexcept : ptr_{rhs.ptr_}, rep_{rhs.rep_}
    {
        rhs.ptr_ = nullptr;
        rhs.rep_ = nullptr;
    }
    ~Shared_ptr() noexcept
    {
        if (rep_ != nullptr && rep_->dec_ref() == 0)
        {
            delete ptr_;
            delete rep_;
        }
    }

    Shared_ptr &operator=(const Shared_ptr &rhs)
    {
        Shared_ptr{rhs}.swap(*this);
        return *this;
    }
    Shared_ptr &operator=(Shared_ptr &&rhs)
    {
        Shared_ptr{std::move(rhs)}.swap(*this);
        return *this;
    }
    void reset() noexcept
    {
        Shared_ptr{}.swap(*this);
    }
    void reset(nullptr_t) noexcept
    {
        reset();
    }
    void reset(T *ptr)
    {
        Shared_ptr{ptr}.swap(*this);
    }

    void swap(Shared_ptr &rhs) noexcept
    {
        std::swap(ptr_, rhs.ptr_);
        std::swap(rep_, rhs.rep_);
    }
    T *get() const noexcept
    {
        return ptr_;
    }

    long use_count() const noexcept
    {
        return rep_ == nullptr ? 0 : rep_->use_count();
    }
    bool unique() const noexcept
    {
        return rep_->use_count() == 1;
    }

    T &operator*() const noexcept
    {
        return *ptr_;
    }
    T &operator->() const noexcept
    {
        return ptr_;
    }

    explicit operator bool() const noexcept
    {
        return static_cast<bool>(ptr_);
    }

private:
    T *ptr_{nullptr};
    ref_count *rep_{nullptr};
};

template <typename T, typename... Args>
auto make_Shared(Args &&...args)
{
    return Shared_ptr<T>{new T(std::forward(args)...)};
}

到此这篇关于C++ shared_ptr实现的文章就介绍到这了,更多相关C++ shared_ptr实现内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言数据结构不挂科指南之队列详解

    C语言数据结构不挂科指南之队列详解

    这篇博客主要介绍一下队列的概念,并且采用 C 语言,编写两种存储实现方式:顺序存储和链式存储,当然还有常规的队列基本操作的实现算法
    2022-09-09
  • C++实现添加图片水印到PDF文档

    C++实现添加图片水印到PDF文档

    水印是显示在文档内容后面的淡色文字或图片,可以用于指示文档的状态(保密、草稿等),本文主要为大家介绍了如何使用 Spire.PDF for C++ 在 PDF 文档中添加图片水印,需要的可以参考下
    2023-11-11
  • C++使struct对象拥有可变大小的数组(详解)

    C++使struct对象拥有可变大小的数组(详解)

    下面小编就为大家带来一篇C++使struct对象拥有可变大小的数组(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • C语言实现简易五子棋

    C语言实现简易五子棋

    这篇文章主要为大家详细介绍了C语言实现简易五子棋,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-10-10
  • C++如何实现sha256算法

    C++如何实现sha256算法

    SHA-256算法由于其强大的安全性,已成为国际标准和许多安全协议的推荐算法,在密码存储、数字签名、区块链技术、SSL/TLS协议、数据完整性验证、系统安全审计等众多应用领域,SHA-256算法都至关重要,这篇文章主要介绍了C++如何实现sha256算法,需要的朋友可以参考下
    2024-07-07
  • c语言printf实现同一位置打印输出的实例

    c语言printf实现同一位置打印输出的实例

    下面小编就为大家带来一篇c语言printf实现同一位置打印输出的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • C语言中改变目录的相关操作函数详解

    C语言中改变目录的相关操作函数详解

    这篇文章主要介绍了C语言中改变目录的相关操作函数详解,分别是fchdir()函数和rewinddir()函数的使用方法,需要的朋友可以参考下
    2015-09-09
  • 如何使用Qt实现实时数据动态绘制的折线图效果

    如何使用Qt实现实时数据动态绘制的折线图效果

    使用Qt的QChartView和定时器,本教程详细介绍了如何动态绘制折线图,通过定时器触发数据点的动态添加和坐标轴范围的自动调整,实现了实时更新数据的动态折线图应用,程序结合QLineSeries或QSplineSeries绘制折线或样条曲线,配合动画效果,展现数据变化
    2024-10-10
  • 快速模式匹配算法(KMP)的深入理解

    快速模式匹配算法(KMP)的深入理解

    本篇文章是对快速模式匹配算法(KMP)进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C++深入探究类与对象之友元与运算符重载

    C++深入探究类与对象之友元与运算符重载

    友元就是让一个函数或者类,访问另一个类中的私有成员;打个比方,这相当于是说:朋友是值得信任的,所以可以对他们公开一些自己的隐私,运算符重载的实质就是函数重载或函数多态,运算符重载是一种形式的C++多态,目的在于让人能够用同名的函数来完成不同的基本操作
    2022-04-04

最新评论