zoukankan      html  css  js  c++  java
  • C++智能指针 unique_ptr、shared_ptr 的实现

    shared_ptr

    shared_ptr 是通过引用计数的方式实现的,这里结合一个单线程的参考实现进行解释。注意,这种实现并不是线程安全的。STL 的 std::shared_ptr 也不是线程安全的,两个成员变量的修改并不是一次操作完成的,使用时需要加锁。

    参考实现

    namespace smart_pointer {
    template<typename T>
    class SharedPtr {
      public:
        explicit SharedPtr (T* ptr = nullptr) : _ptr(ptr), _pcount(new int(1)) {
        }
    
        SharedPtr (const SharedPtr& s) : _ptr(s._ptr), _pcount(s._pcount) {
            *(this->_pcount)++;
        }
    
        ~SharedPtr () {
            if (--(*(this->_pcount)) == 0) {
                delete this->_ptr;
                delete this->_pcount;
                this->_ptr = nullptr;
                this->_pcount = nullptr;
            }
        }
    
        SharedPtr& operator= (const SharedPtr& s){
            if (this != &s) {
                if (--(*(this->_pcount)) == 0) {
                    delete this->_ptr;
                    delete this->_pcount;
                }
                this->_ptr = s._ptr;
                this->_pcount = s._pcount;
                *(this->_pcount)++;
            }
            return *this;
        }
    
        T& operator*() {
            return *(this->_ptr);
        }
    
        T* operator->() {
            return this->_ptr;
        }
    
        operator bool () {
            return this->_ptr;
        }
    
        T* get() const {
            return this->_ptr;
        }
    
        void swap(SharedPtr& s) {
            std::swap(this->_ptr, s._ptr);
            std::swap(this->_pcount, s._pcount);
        }
    
      private:
        T* _ptr;      // 指向底层对象的指针
        int* _pcount; // 指向引用计数的指针
    };
    
    template<typename T>
    void swap(SharedPtr<T>& a, SharedPtr<T>& b) {
        a.swap(b);
    }
    }; // namespace smart_pointer
    
    int main() {
        SharedPtr<int> p1, p2;
        std::swap(p1, p2);
        return 0;
    }
    

    代码说明

    1. 拷贝赋值的时候,要考虑当前所持有的对象是否应该被释放,然后再改变所指对象。
    2. 为了提供程序健壮性,保证 std::swap 在智能指针上能正确工作,需要特化智能指针类型。参考 Effictive C++ 第25款,对其进行特化。由于C++只允许对class template进行偏特化,不允许对function template偏特化,我们想把 SharedPtr<T>::swap 全部加入 std 是不可行的。解决办法是利用C++的名称查找规则,在SharedPtr的命名空间中提供类型匹配的swap,使编译器优先选择该函数使用。
    3. 这种实现方案,在每个底层对象之外,额外开辟了一个int的空间用于引用计数,并由多个指针共享访问。相比于 unique_ptr,shared_ptr 使用了两个指针,多一倍的内存开销。相比于单指针的实现方案,好处是在高频的对象访问上,减少了一次间接访问。

    单指针实现方案

    namespace smart_pointer {
    template<typename T>
    class SharedPtr {
      public:
        explicit SharedPtr (T* ptr = nullptr) : _pholder(new ObjectHolder(ptr)) {
        }
    
        SharedPtr (const SharedPtr& s) : _pholder(s._pholder) {
            this->_pholder->_pcount++;
        }
    
        ~SharedPtr () {
            if (--(this->_pholder->_pcount) == 0) {
                delete this->_pholder;
                this->_pholder = nullptr;
            }
        }
    
        SharedPtr& operator= (const SharedPtr& s){
            if (this != &s) {
                if (--(this->_pholder->_pcount) == 0) {
                    delete this->_pholder;
                }
                this->_pholder = s._pholder;
                this->_pholder->_pcount++;
            }
            return *this;
        }
    
        T& operator*() {
            return *(this->_pholder->_ptr);
        }
    
        T* operator->() {
            return this->_pholder->_ptr;
        }
    
        operator bool () {
            return this->_pholder->_ptr;
        }
    
        T* get() const {
            return this->_pholder->_ptr;
        }
    
        void swap(SharedPtr& s) {
            std::swap(this->_pholder, s._pholder);
        }
    
      private:
        class ObjectHolder {
            friend class SharedPtr; // 内部类可以任意访问外部类的成员,外部类只有作为友元才能访问内部类私有成员
          public:
            explicit ObjectHolder(T* ptr = nullptr) : _ptr(ptr), _pcount(1) {
            }
            ~ObjectHolder() {
                delete _ptr;
            }
          private:
            T* _ptr;      // 指向底层对象的指针
            int _pcount;  // 引用计数,注意不是指针了
        }; // 每个对象由一个ObjectHolder持有
        ObjectHolder* _pholder; // 指向计数对象的指针
    };
    
    template<typename T>
    void swap(SharedPtr<T>& a, SharedPtr<T>& b) {
        a.swap(b);
    }
    }; // namespace smart_pointer
    

    unique_ptr

    unique_ptr 使用了 RAII 管理资源,保证了出现异常的情况下,资源也能正确释放。

    参考实现

    namespace smart_pointer {
    
    template <typename T> 
    class PointerDeleter{
      public:
        void operator() (const T *_ptr) {
            if(_ptr) {
                delete _ptr;
                _ptr = nullptr;
            }
        }
    };
    
    template <typename T, typename Deleter = PointerDeleter<T> >
    class UniquePtr {
      public:
        explicit UniquePtr (T *ptr = nullptr) : _ptr(ptr) {
        }
    
        ~UniquePtr () {
            Deleter()(this->_ptr);
        }
    
        // non-copyable
        UniquePtr (const UniquePtr &p) = delete;
        UniquePtr& operator= (const UniquePtr &p) = delete;
    
        // move constructor
        UniquePtr (UniquePtr &&p) : _ptr(p._ptr) {
            p._ptr = nullptr;
        }
    
        // move assignment
        UniquePtr& operator= (UniquePtr &&p) {
            std::swap(this->_ptr, p._ptr);
            return *this;
        }
    
        T& operator*() {
            return *(this->_ptr);
        }
    
        T* operator->() {
            return this->_ptr;
        }
    
        operator bool () {
            return this->_ptr;
        }
    
        T* get() const {
            return this->_ptr;
        }
    
        T* release() {
            T *pointer = this->_ptr;
            this->_ptr = nullptr;
            return pointer; 
        }
    
        void reset (T *ptr) {
            UniquePtr<T, Deleter>().swap(*this);
            this->_ptr = ptr;
        }
    
        void swap(UniquePtr &p) {
            std::swap(this->_ptr, p._ptr);
        }
    
      private:
        T *_ptr;
    };
    
    template<typename T>
    void swap(UniquePtr<T>& a, UniquePtr<T>& b) {
        a.swap(b);
    }
    }; // namespace smart_pointer
    

    代码说明

    UniquePtr 需要注意的是保证不会被复制,可以采用的办法有两种。以前的解决办法是把拷贝构造函数和拷贝赋值操作符设为private禁止访问,新的解决办法是将两个函数标记为 delete。我们这里采用的是新标准。

    参考

    1. 拓跋阿秀 | 《逆袭进大厂》第二弹之C++进阶篇59问59答(超硬核干货)
    2. lizhentao0707 | 智能指针的原理及实现
    3. Simon | 手写shared_ptr, weak_ptr, unique_ptr aka. github.com/xiaoguangcong
  • 相关阅读:
    shell学习(11)- seq
    bash快捷键光标移动到行首行尾等
    shell学习(10)- if的使用
    Python 执行 Shell 命令
    查看 jar 包加载顺序
    Linux 中的 sudoers
    Ubuntu 开机启动程序
    指定 Docker 和 K8S 的命令以及用户
    Spark on K8S(Standalone)
    Spark on K8S (Kubernetes Native)
  • 原文地址:https://www.cnblogs.com/zhcpku/p/14448246.html
Copyright © 2011-2022 走看看