美文网首页
智能指针

智能指针

作者: 小松qxs | 来源:发表于2019-01-24 15:43 被阅读0次

    原理:

    智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放。方便管理堆内存。
    智能指针是利用RAII(资源获取即初始化)技术对普通指针进行封装,使智能指针实质是一个对象,行为表现的像一个指针。

    常用的智能指针:

    1、shared_ptr:
    基于引用计数的智能指针。可随意赋值,当内存引用计数为0时内存被释放。支持多个指针指向相同的对象。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。

    初始化:智能指针是模板类,可以指定类型,传入指针通过构造函数初始化。也可以使用make_shared函数初始化。不能将指针直接赋值给一个智能指针,因为智能指针是类,不能用指针赋值。
    例如:std::shared_ptr<int> p4 = new int(1);写法错误
    拷贝和赋值:拷贝使对象的引用计数加1,赋值使原对象(左侧)引用计数减1,新对象(右侧)引用计数加1,当计数为0时,自动释放内存。
    get函数:获取原始指针,不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存。

    #include <iostream>
    #include <memory>
    
    int main() {
        {
            int a = 10;
            std::shared_ptr<int> ptra = std::make_shared<int>(a);
            std::shared_ptr<int> ptra2(ptra); //copy
            std::cout << ptra.use_count() << std::endl;  // 2
    
            int b = 20;
            int *pb = &a;
            //std::shared_ptr<int> ptrb = pb;  // error 不能用指针赋值类
            std::shared_ptr<int> ptrb = std::make_shared<int>(b);
            ptra2 = ptrb; // assign ptra2由ptra位置变为指向ptrb
            pb = ptrb.get(); // 获取原始指针
    
            std::cout << ptra.use_count() << std::endl;  // 1
            std::cout << ptrb.use_count() << std::endl;  // 2
        }
    }
    

    2、unique_ptr:
    “唯一”拥有所指对象。同一时刻只能有一个unique_ptr指向给定对象,禁止拷贝语义、只有移动语义。在出现异常的情况下,动态资源能得到释放。

    创建:通过构造函数指定
    重新指定:reset方法
    释放所有权:release方法
    移动语义转移所有权:std::move

    #include <iostream>
    #include <memory>
    
    int main() {
        {
            std::unique_ptr<int> uptr(new int(10));  //绑定动态对象
            //std::unique_ptr<int> uptr2 = uptr;  //不能赋值,编译出错
            //std::unique_ptr<int> uptr2(uptr);  //不能拷贝
            std::unique_ptr<int> uptr2 = std::move(uptr); //转移所有权,uptr无效指针
            uptr2.release(); //释放所有权
        }
        /超过uptr的作用域,內存释放
    }
    

    3、weak_ptr:
    弱引用,只引用,不计数,没有重载operator*和->,对shared_ptr资源观测。针对shared_ptr互相引用形成环(循环引用),两个指针指向的内存都无法释放的问题。需要手动打破循环引用或使用weak_ptr。

    构造:从一个shared_ptr或者另一个weak_ptr对象构造。
    观测资源的引用计数:成员函数use_count()。
    被观测的资源是否存在:成员函数expired(),功能等价于use_count()==0,但更快。
    从被观测的shared_ptr获得一个可用的shared_ptr对象:成员函数lock(), 操作资源。当expired()==true的时候,lock()函数返回一个存储空指针的shared_ptr。
    注意:如果一块内存被shared_ptr和weak_ptr同时引用,所有shared_ptr析构后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前需要检查weak_ptr是否为空指针。

    #include <iostream>
    #include <memory>
    
    int main() {
        {
            std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);
            std::cout << sh_ptr.use_count() << std::endl;  // 1
    
            std::weak_ptr<int> wp(sh_ptr);
            std::cout << wp.use_count() << std::endl;  // 1
    
            if(!wp.expired()){
                std::shared_ptr<int> sh_ptr2 = wp.lock(); //get another shared_ptr
                *sh_ptr = 100;
                std::cout << wp.use_count() << std::endl;  // 2
            }
        }
        //delete memory
    }
    

    循环引用例子如下:
    错误例子:

    #include <iostream>
    #include <memory>
    
    class Child;
    class Parent;
    
    class Parent {
    private:
        std::shared_ptr<Child> ChildPtr;
    public:
        void setChild(std::shared_ptr<Child> child) {
            this->ChildPtr = child;
        }
        void doSomething() {
            if (this->ChildPtr.use_count()) {
    
            }
        }
        ~Parent() {
        }
    };
    
    class Child {
    private:
        std::shared_ptr<Parent> ParentPtr;
    public:
        void setPartent(std::shared_ptr<Parent> parent) {
            this->ParentPtr = parent;
        }
        void doSomething() {
            if (this->ParentPtr.use_count()) {
    
            }
        }
        ~Child() {
        }
    };
    
    int main() {
        std::weak_ptr<Parent> wpp;
        std::weak_ptr<Child> wpc;
        {  // {}代表作用域,出作用域以下p和c应该被销毁
            std::shared_ptr<Parent> p(new Parent);   // 1
            std::shared_ptr<Child> c(new Child);   // 1
            p->setChild(c);   // 2
            c->setPartent(p);   // 2
            wpp = p;
            wpc = c;
            std::cout << p.use_count() << std::endl; // 2
            std::cout << c.use_count() << std::endl; // 2
        }  // 出作用域时对象p和c没销毁,每个count只减少一次
        std::cout << wpp.use_count() << std::endl;  // 1
        std::cout << wpc.use_count() << std::endl;  // 1
        return 0;
    } 
    

    正确做法:

    #include <iostream>
    #include <memory>
    
    class Child;
    class Parent;
    
    class Parent {
    private:
        //std::shared_ptr<Child> ChildPtr;
        std::weak_ptr<Child> ChildPtr;
    public:
        void setChild(std::shared_ptr<Child> child) {
            this->ChildPtr = child;
        }
        void doSomething() {
            //new shared_ptr
            if (this->ChildPtr.lock()) {
    
            }
        }
        ~Parent() {
        }
    };
    
    class Child {
    private:
        std::shared_ptr<Parent> ParentPtr;
    public:
        void setPartent(std::shared_ptr<Parent> parent) {
            this->ParentPtr = parent;
        }
        void doSomething() {
            if (this->ParentPtr.use_count()) {
    
            }
        }
        ~Child() {
        }
    };
    
    int main() {
        std::weak_ptr<Parent> wpp;
        std::weak_ptr<Child> wpc;
        {
            std::shared_ptr<Parent> p(new Parent); // 1
            std::shared_ptr<Child> c(new Child); // 1
            p->setChild(c); // c = 1
            c->setPartent(p); // p = 2
            wpp = p;
            wpc = c;
            std::cout << p.use_count() << std::endl; // 2
            std::cout << c.use_count() << std::endl; // 1
        } // c只有1,1-->0,销毁c对象时调用child的析构函数,会对p销毁一次,而p对象本身会销毁一次,所以也是0
        std::cout << wpp.use_count() << std::endl;  // 0
        std::cout << wpc.use_count() << std::endl;  // 0
        return 0;
    }
    

    智能指针实现:

    #include <iostream>
    #include <memory>
    
    template<typename T>
    class SmartPointer {
    private:
        T* _ptr;
        size_t* _count;
    public:
        SmartPointer(T* ptr = nullptr) : _ptr(ptr) {
            if (_ptr) {
                _count = new size_t(1);
            } else {
                _count = new size_t(0);
            }
        }
    
        SmartPointer(const SmartPointer& ptr) {
            if (this != &ptr) {
                this->_ptr = ptr._ptr;
                this->_count = ptr._count;
                (*this->_count)++;
            }
        }
    
        SmartPointer& operator=(const SmartPointer& ptr) {
            if (this->_ptr == ptr._ptr) {
                return *this;
            }
    
            if (this->_ptr) {
                (*this->_count)--;
                if (*this->_count == 0) {
                    delete this->_ptr;
                    delete this->_count;
                }
            }
    
            this->_ptr = ptr._ptr;
            this->_count = ptr._count;
            (*this->_count)++;
            return *this;
        }
    
        T& operator*() {
            assert(this->_ptr == nullptr);
            return *(this->_ptr);
    
        }
    
        T* operator->() {
            assert(this->_ptr == nullptr);
            return this->_ptr;
        }
    
        ~SmartPointer() {
            (*this->_count)--;
            if (*this->_count == 0) {
                delete this->_ptr;
                delete this->_count;
            }
        }
    
        size_t use_count(){
            return *this->_count;
        }
    };
    
    int main() {
        {
            SmartPointer<int> sp(new int(10));
            SmartPointer<int> sp2(sp);
            SmartPointer<int> sp3(new int(20));
            std::cout << sp.use_count() << std::endl;
            std::cout << sp3.use_count() << std::endl;
            sp2 = sp3;
            std::cout << sp.use_count() << std::endl;
            std::cout << sp3.use_count() << std::endl;
        }
        //delete operator
    }
    

    相关文章

      网友评论

          本文标题:智能指针

          本文链接:https://www.haomeiwen.com/subject/gikyjqtx.html