手撕shared_ptr 共享指针

最下面有完整的代码,不想看的可以直接跳过

        今天让我们来手撕c++的共享指针shared_ptr,共享指针的是通过引用计算和RAII思想来实现的,当我们将指针赋值给其他shared ptr时内部有一个计数变量会加1,当shared ptr触发析构时这个计数就会减1,当计数为0时就会自动的释放管理的资源

        这个就是shared ptr的基本原理下面我们开始实现,首先来介绍构造函数和成员变量,我们的类需要通用型肯定是要用模板的,所以我们的指针就用到了模板来满足对所有指针类型的支持,引用计数需要用原子变量来保证并发安全

template<typename T>
class SharedPtr{
public:
	explicit  SharedPtr() noexcept : _ptr(nullptr), _count(new std::atomic<size_t>(0)){}
	explicit  SharedPtr(T* ptr) : _ptr(ptr) { _count = (_ptr == nullptr) ? new std::atomic<size_t>(0) : new std::atomic<size_t>(1); }
	SharedPtr(const SharedPtr<T>& other):_count(other._count), _ptr(other._ptr) {
		if (_ptr != nullptr) (*_count)++;
	}
	SharedPtr(SharedPtr<T>&& other) noexcept : _count(other._count), _ptr(other._ptr) {
		other._count = nullptr;
		other._ptr = nullptr;
	}
private:
	std::atomic<size_t>* _count;
	T* _ptr;
};

        explicit关键字的作用是防止隐式转换,防止普通指针在不经意间转换成shared ptr,使用模板T作为我们内部的指针,用std::atomic原子变量来实现引用变量,这个使用指针的目的是确保多个shared ptr管理同一个资源时,他们的行为一致性,拷贝构造需要注意的是进行自增,移动构造需要注意将另一方置空

        下面我们再来介绍拷贝赋值和移动赋值,以及一些运算符重载

    SharedPtr<T>& operator=(const SharedPtr<T>& other) {
		if (this == &other) return *this;
		reset();

		_count = other._count;
		_ptr = other._ptr;
		if (other._ptr != nullptr) (*_count)++;
		return *this;
	}
	SharedPtr<T>& operator=(SharedPtr<T>&& other) noexcept {
		if (this == &other) return *this;
		reset();

		_count = other._count;
		_ptr = other._ptr;
		other._count = nullptr;
		other._ptr = nullptr;
		return *this;
	}

	T& operator*() const noexcept {
		if (_ptr == nullptr) throw std::runtime_error("nullptr dereference");
		return *_ptr;
	}
	T* operator->() const noexcept {
		if (_ptr == nullptr) throw std::runtime_error("nullptr dereference");
		return _ptr;
	}
	bool operator==(const SharedPtr<T>& other) const noexcept {
		return _ptr == other._ptr;
	}
	bool operator!=(const SharedPtr<T>& other) const noexcept {
		return _ptr!= other._ptr;
	}

*与->要确保能发出异常,reset函数的作用是重新调整自己后面我们再介绍,赋值操作一定要调用这个函数,保证能清除原来的自己

我们最后来介绍析构还有MakeShared函数

private:
    // 允许 MakeShared 访问私有构造函数
    template<typename U, typename... Args>
    friend SharedPtr<U> MakeShared(Args&&... args);
    SharedPtr(T* ptr, std::atomic<size_t>* count) noexcept : _ptr(ptr), _count(count) {}

    void reset() noexcept {
        if (_ptr != nullptr) {
            if (--(*_count) == 0) {
                _ptr->~T();
                ::operator delete(static_cast<void*>(_count));  // 释放引用计数和对象的整块内存
            }
        }
        else if (_count != nullptr) {
            ::operator delete(static_cast<void*>(_count));  // 释放引用计数和对象的整块内存
        }
        _ptr = nullptr;
        _count = nullptr;
    }


template<typename T, typename... Args>
SharedPtr<T> MakeShared(Args&&... args) {
    // 使用 std::aligned_storage 来确保内存对齐
    using StorageType = typename std::aligned_storage<sizeof(T) + sizeof(std::atomic<size_t>), alignof(T)>::type;
    void* mem = ::operator new(sizeof(StorageType));  // 一次性分配内存
    auto* count = new (mem) std::atomic<size_t>(1);   // 在已分配的内存上构造引用计数
    auto* obj = new (static_cast<void*>(static_cast<char*>(mem) + sizeof(std::atomic<size_t>))) T(std::forward<Args>(args)...);  // placement new构造对象

    return SharedPtr<T>(obj, count);  // 返回构造的 SharedPtr
}

reset函数会将引用计数减1并检测是否为0,为0后就清除当前的资源,MakeShared函数的作用是内存对齐并保证一次性创建内存空间

手撕shared ptr就这样完成了,希望能够帮助到大家,如果有不对的地方欢迎指正

#pragma once

template<typename T>
class SharedPtr{
public:
    explicit  SharedPtr() noexcept : _ptr(nullptr), _count(new std::atomic<size_t>(0)){}
    explicit  SharedPtr(T* ptr) : _ptr(ptr) { _count = (_ptr == nullptr) ? new std::atomic<size_t>(0) : new std::atomic<size_t>(1); }
    SharedPtr(const SharedPtr<T>& other):_count(other._count), _ptr(other._ptr) {
        if (_ptr != nullptr) (*_count)++;
    }
    SharedPtr(SharedPtr<T>&& other) noexcept : _count(other._count), _ptr(other._ptr) {
        other._count = nullptr;
        other._ptr = nullptr;
    }

    SharedPtr<T>& operator=(const SharedPtr<T>& other) {
        if (this == &other) return *this;
        reset();

        _count = other._count;
        _ptr = other._ptr;
        if (other._ptr != nullptr) (*_count)++;
        return *this;
    }
    SharedPtr<T>& operator=(SharedPtr<T>&& other) noexcept {
        if (this == &other) return *this;
        reset();

        _count = other._count;
        _ptr = other._ptr;
        other._count = nullptr;
        other._ptr = nullptr;
        return *this;
    }

    T& operator*() const noexcept {
        if (_ptr == nullptr) throw std::runtime_error("nullptr dereference");
        return *_ptr;
    }
    T* operator->() const noexcept {
        if (_ptr == nullptr) throw std::runtime_error("nullptr dereference");
        return _ptr;
    }
    bool operator==(const SharedPtr<T>& other) const noexcept {
        return _ptr == other._ptr;
    }
    bool operator!=(const SharedPtr<T>& other) const noexcept {
        return _ptr!= other._ptr;
    }


    T* get() const noexcept {
        return _ptr;
    }
    size_t use_count() const noexcept {
        return *_count;
    }

    ~SharedPtr() {
        reset();
    }

private:
    // 允许 MakeShared 访问私有构造函数
    template<typename U, typename... Args>
    friend SharedPtr<U> MakeShared(Args&&... args);
    SharedPtr(T* ptr, std::atomic<size_t>* count) noexcept : _ptr(ptr), _count(count) {}

    void reset() noexcept {
        if (_ptr != nullptr) {
            if (--(*_count) == 0) {
                _ptr->~T();
                ::operator delete(static_cast<void*>(_count));  // 释放引用计数和对象的整块内存
            }
        }
        else if (_count != nullptr) {
            ::operator delete(static_cast<void*>(_count));  // 释放引用计数和对象的整块内存
        }
        _ptr = nullptr;
        _count = nullptr;
    }
private:
    std::atomic<size_t>* _count;
    T* _ptr;
};

template<typename T, typename... Args>
SharedPtr<T> MakeShared(Args&&... args) {
    // 使用 std::aligned_storage 来确保内存对齐
    using StorageType = typename std::aligned_storage<sizeof(T) + sizeof(std::atomic<size_t>), alignof(T)>::type;
    void* mem = ::operator new(sizeof(StorageType));  // 一次性分配内存
    auto* count = new (mem) std::atomic<size_t>(1);   // 在已分配的内存上构造引用计数
    auto* obj = new (static_cast<void*>(static_cast<char*>(mem) + sizeof(std::atomic<size_t>))) T(std::forward<Args>(args)...);  // placement new构造对象

    return SharedPtr<T>(obj, count);  // 返回构造的 SharedPtr
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值