鱼C论坛

 找回密码
 立即注册
查看: 2023|回复: 0

[技术交流] 简易版shared_ptr实现

[复制链接]
发表于 2014-7-11 11:34:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
至于什么事shared_ptr......百度百科是这样说的
shared_ptr的作用有如同指针,但会记录有多少个shared_ptrs共同指向一个对象。这便是所谓的引用计数(reference counting)。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。
auto_ptr由于它的破坏性复制语义,无法满足标准容器对元素的要求,因而不能放在标准容器中;如果我们希望当容器析构时能自动把它容纳的指针元素所指的对象删除时,通常采用一些间接的方式来实现,显得比较繁琐。boost库中提供了一种新型的智能指针shared_ptr,它解决了在多个指针间共享对象所有权的问题,同时也满足容器对元素的要求,因而可以安全地放入容器中。
好像很厉害的样子,而且shared_ptr可以在每次reset指针时重新设置删除器,所以删除器的存储也是一个小小的难点
其实如果你的C++编译器支持C++11的话,可以这么实现(这也是我的实现):
#ifndef SHAREDPTR_H
#define SHAREDPTR_H

#include <functional> // std::function
#include <cstddef> // std::size_t

template <class T>
class SharedPtr {
public:
    explicit SharedPtr(T* ptr) :
        Ptr(ptr) {}
    SharedPtr() = default;
    template <class D> SharedPtr(T* ptr, D d) :
        Ptr(ptr), Del(new std::function<void (T*)>(d)) {}
    SharedPtr(const SharedPtr& shared) :
        Use(shared.Use), Ptr(shared.Ptr), Del(shared.Del) { ++*Use; } // 拷贝构造: 递增引用计数
    SharedPtr(SharedPtr&& shared) : // 移动构造函数
        Use(shared.Use), Ptr(shared.Ptr), Del(shared.Del)
    { shared.Ptr = nullptr; shared.Del = nullptr; shared.Use = new std::size_t(1); } // 将移后源置为安全可析构状态

    T* operator->() { return Ptr; }
    const T* operator->() const { return Ptr; }
    T& operator*() { return *Ptr; }
    const T& operator*() const { return *Ptr; }
    explicit operator bool() const { return Ptr; }
    SharedPtr& operator=(const SharedPtr& rhs);

    T* Get() noexcept { return Ptr; }
    const T* Get() const noexcept { return Ptr; }
    void ReSet(T*) noexcept;
    template <class D> void ReSet(T*, D) noexcept;
    std::size_t UseCount() const { return *Use; }
    bool Unique() const { return *Use == 1; } // 是否"独占"指针

    ~SharedPtr() noexcept;
private:
    std::function<void (T*)>* Del = nullptr; // 删除器
    T* Ptr = nullptr;
    std::size_t* Use = new std::size_t(1); // 计数器
};

template <class T>
SharedPtr<T>& SharedPtr<T>::operator=(const SharedPtr& rhs)
{
    ++*(rhs.Use); // 防止自赋值: 先递增右运算对象的使用计数
    if (!*--Use) { // 自身"独占"这一指针
        Del ? (*Del)(Ptr) : delete Ptr;
        delete Use;
        delete Del;
    }
    Use = rhs.Use;
    Del = rhs.Del;

    return *this;
}

template <class T>
SharedPtr<T>::~SharedPtr() noexcept
{
    if (!--*Use) {
        delete Use;
        Del ? (*Del)(Ptr) : delete Ptr;
        delete Del;
    }
}

template <class T>
void SharedPtr<T>::ReSet(T* ptr) noexcept
{
    if (!--*Use) { // 自己已经是最后一个持有该指针的只能指针
        Del ? (*Del)(Ptr) : delete Ptr; // 释放空间
        *Use = 1;
    }
    else Use = new std::size_t(1); // 重新分配计数器
    Ptr = ptr;
}

template <class T>
template <class D>
void SharedPtr<T>::ReSet(T* ptr, D d) noexcept
{
    ReSet(ptr);
    delete Del;
    Del = new std::function<void (T*)>(d); // 重新设置删除器
}

#endif // SHAREDPTR_H
如果你的编译器不支持C++11,也就是没有std::function呢?
请参考boost库中any库的实现:
好吧还是说说什么是any
any是一个完全泛化的类,你可以用它存储任意类型的数据,比如:
any a = 1; // 用any存储int
any b = 'c'; // 用any存储char
如果要输出a,b的值要肿么办呢?可以:
cout << any_cast<int>(a); // 输出a
cout << any_cast<char>(b); // 输出b
any就有点像Python里的变量,而且any还可以变换自己存储的类型,例如我前面已经用a存储了一个int,接下来我还可以用它存储char,string等等
a = 'a';
string s("abc");
a = s;
这都是允许的,并且any不是一个模板类
我们可以借鉴any库实现的思路实现对shared_ptr删除器的存储
上any的主要实现代码
//摘自”boost/any.hpp”
class any
{
        public:
        class placeholder    //泛型数据容器holder的非泛型基类   
        {                    
                public: // structors
                virtual ~placeholder() //虚析构函数,为保证派生类对象能用基类指针析构
                {}
                public: // queries
                virtual const std::type_info & type() const = 0; //提供关于型别的信息
                virtual placeholder * clone() const = 0;  //复制容器
        };
        template<typename ValueType>
        class holder : public placeholder   //
        {
                public: // structors
                holder(const ValueType & value) //
                : held(value)
                {}
                public: // queries
                virtual const std::type_info & type() const
                {
                        return typeid(ValueType);  //typeid返回std::typeinfo对象引用,后者包含任意
                        //对象的型别信息如name,还提供operator==操作符
                        //你可以用typeid(oneObj)==typeid(anotherObj)来比
                        //两个对象之型别是否一致
                }
                virtual placeholder * clone() const
                {
                        return new holder(held);  //改写虚函数,返回自身的复制体
                }
                public: // representation
                ValueType held;  //数据保存的地方
        };//类定义结束
        
        placeholder * content;   //指向泛型数据容器holder的基类placeholder的指针
        template<typename ValueType>
        any(const ValueType & value)
        : content(new holder<ValueType>(value)) //模板构造函数,动态分配数据容器并调用其构
        //造函数
        {}
        ...
        template<typename ValueType>
        any & operator=(const ValueType & rhs)    //与模板构造函数一样,但使用了swap惯用手法
        {
                any(rhs).swap(*this); //先创建一个临时对象any(rhs),再调用下面的swap函数进行底层
                //数据交换,注意与*this交换数据的是临时对象,所以rhs的底层
                //数据并未被更改,只是在swap结束后临时对象拥有了*this的底
                //层数据,而此时*this也拥有了临时对象构造时所拥有的rhs的数
                //据的副本。然后临时对象由于生命期的结束而被自动析构,*this
                //原来的底层数据随之烟消云散。
                return *this;
        }
        any & swap(any & rhs)   //swap函数,交换底层数据
        { 
                std::swap(content, rhs.content); //只是简单地将两个指针的值互换
                return *this;
        }
        ~any()  //析构函数
        {
                delete content; //释放容器,用的是基类指针,这就是placeholder需要一个虚
                //析构函数的原因
        }
        ...
};
看懂了吧,鱼油们可以根据any的实现原理,实现不用c++11支持的shared_ptr哦!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-24 16:51

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表