zoukankan      html  css  js  c++  java
  • C++智能指针管理类

    1.程序猿明白的进行内存释放

    对于c++程序猿,最头脑的莫过于对动态分配的内存进行管理了。c++在堆上分配的内存。须要程序猿负责对分配的内存进行释放。但有时内存的释放看起来并不件非常轻松的事,例如以下程序

    void func()
    {
        int *p = new int(0);
        if(一些推断)
        {
            return;
        }
        p = new int(1);
        delete p;
    }

    这个函数没有不论什么意义。仅仅为说明问题。func函数至少有三处问题。

    1.一旦if的推断条件成立,就会立刻运行返回语句。

    此时p所指向的内存将无法释放(这个新手一般都会犯的错误)。2. p = new int(1);语句。没有将原来分配在堆上的内存释放。造成原来那块内存永远不可能释放直到程序结束。3.这个问题并非那么明显。如果func()函数的代码非常长。

    void func()
    {
        int *p = new int(0);
        ...
        delete p;
    }

    在int *p = new int(0)和delete p之间有大量代码,而这中间的代码可能有代码已经运行过delete p,而且没有把p置0。此时p为悬挂指针。后面再运行delete p就会造成运行时错误。

    2.一个相似于boost shared_ptr的智能指针管理类

    以下是參照c++ primer及effective c++自己写的一个智能指针管理类。

    使用SmartPtr,就不再须要关心内存的释放问题了。

    #ifndef SMARTPTR_INCLUDE_H
    #define SMARTPTR_INCLUDE_H
    #include <iostream>
    using namespace std;
    namespace commutil
    {
        template<typename T>
        class SmartPtr
        {
        public:
            SmartPtr(T *p = 0);
            SmartPtr(const SmartPtr &);
            SmartPtr &operator=(const SmartPtr &);
            ~SmartPtr();
            T &operator *();
            T *operator ->();
        private:
            void decreaseRef();
            T *m_p;
            int *m_useCnt;
        };
        template<typename T>
        SmartPtr<T>::SmartPtr(T *p=0):m_p(p)
        {
            //调用此构造函数时。默认引用数为1
            m_useCnt = new int(1);
        }
    
        template<typename T>
        SmartPtr<T>::SmartPtr(const SmartPtr &rhs)
        {
            //使用复制构造函数,创建新的对象,引用数加1
            this->m_p = rhs.m_p;
            this->m_useCnt = rhs.m_useCnt;
            (*m_useCnt)++;
        }
    
        template<typename T>
        SmartPtr<T>::~SmartPtr()
        {
            decreaseRef();
        }
    
        template<typename T>
        void SmartPtr<T>::decreaseRef()
        {
            if(--(*m_useCnt)==0)
            {//当引用数为0时,释放heap内存
                delete m_useCnt;
                delete m_p;
            }
        }
    
        template<typename T>
        T &SmartPtr<T>::operator *()
        {
            //重载解引用符
            return *m_p;
        }
    
        template<typename T>
        T *SmartPtr<T>::operator ->()
        {
            //重载 ->运算符,返回真实的对象指针
            return m_p;
        }
    
        template<typename T>
        SmartPtr<T> &SmartPtr<T>::operator=(const SmartPtr &anotherPtr)
        {
            if(this==&anotherPtr)
            {//防止自赋值
                return *this;
            }
            //使用赋值运算符。原来所指对象的引用数减1。
            decreaseRef();
            m_p = anotherPtr.m_p;
            m_useCnt = anotherPtr.m_useCnt;
            ++*m_useCnt;//指向新的对象,所指对象引用数加1
            return *this;
        }
    }
    #endif

    3.使用SmartPtr的案例分析

    3.1第一个SmartPtr的实例

    void func()
    {
        SmartPtr<int> autoPtr(new int(1));
    }

    在func中,定义了SmartPtr的一个对象autoPtr。并用new int(1)在堆上分配一块内存。将分配的内存首地址传给SmartPtr的构造函数。此时autoPtr的m_useCnt的值为1。

    当func运行完毕时,autoPtr对象超出其作用域,调用autoPtr的析构函数。在析构函数中调用私有的decreaseRef()函数,在decreaseRef()中将autoPtr的m_useCnt所内存值减1;此时m_useCnt所指内存值为0。运行delete m_useCnt;delete m_p;。至此在创建autoPtr对象时动态分配的内存被释放。

    3.2带有return语句的SmartPtr

    void func()
    {
        SmartPtr<int> autoPtr(new int(1));
        if(推断条件)
        {
            return;
        }
    }

    func()函数中的if推断条件成立时,运行return;语句后。autoPtr超出其作用范围,内存释放。分析见3.1

    3.3 SmartPtr对象管理新的SmartPtr对象

        SmartPtr<int> autoPtr1(new int(0));
        SmartPtr<int> autoPtr2(new int(1));
        autoPtr1 = autoPtr2;

    运行autoPtr1 = autoPtr2,因为SmartPtr类重载了赋值运算符。实际运行的是SmartPtr的operator=()函数,在上面这个赋值语句中,先autoPtr1的m_useCnt所指内存值减1。此时m_useCnt所指内存值变为0,运行m_p所指内存释放。

    使用SmartPtr攻克了1中所提的第二个问题。而且全部内存管理与释放工作都由SmartPtr进行。程序猿不须要再关心什么时候进行内存释放。

    3.4SmartPtr共享对象资源

    与auto_ptr不同,SmartPtr使用引用计数机制保证多个SmartPtr对象能够管理同一个对象资源

    void func()
    {
        SmartPtr<int> autoPtr1(new int(0));
        SmartPtr<int> autoPtr2(autoPtr1);
    }

    运行SmartPtr autoPtr2(autoPtr1)调用SmartPtr的拷贝构造函数,在拷贝构造函数中将autoPtr2的m_useCnt及m_p分别指向autoPtr1所指向的内存,再将m_useCnt的引用数加1。

    func()函数结束时。两次调用SmartPtr的析构函数。两次析构函数后引用数为0。删除m_useCnt。m_p所指内存。

    3.5SmartPtr訪问类成员函数

    class A
    {
    public:
        void print()
        {
            cout<<"A"<<endl;
        }
    };
    
    void func()
    {
        A *obj = new A();
        obj->print();
        delete obj;
    }

    SmartPtr重载了->运算符。能够将SmartPtr对象像指针一样訪问成员函数。

    void func()
    {
        SmartPtr<A> autoPtr(new A());
        autoPtr->print();
    }

    4.小结

    SmartPtr类实现了内存对象”自管理”。使用SmartPtr程序猿不再须要关心动态内存的释放问题。与shared_ptr不同。SmartPtr类仅仅能管理指针类型的资源,而且SmartPtr不支持自己定义的资源释放函数。

  • 相关阅读:
    PAT 1008--------数组元素的循环右移,你需要记住的
    PAT1049-----枚举法,找规律题,注意降低时间复杂度
    PAT1048----你需要了解并记住的解题思路
    C++中几个输入函数的用法和区别(cin、cin.get()、cin.getline()、getline()、gets()、getchar()))
    PAT1040----关于数学题目的解法新思路值得借鉴,字符的配对
    PAT1029-----介绍字符串的解题思路和部分知识点
    PAT1027-----等差数列的问题或数学问题
    PAT1026----四舍五入的思路,%2d的一些知识
    / 已阅 /PAT1017-------高精度计算,问题的所有可能情况
    LeetCode 无重复字符的最长子串
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7079936.html
Copyright © 2011-2022 走看看