zoukankan      html  css  js  c++  java
  • 你说你会C++? —— 智能指针

    智能指针的设计初衷是:
         C++中没有提供自己主动回收内存的机制,每次new对象之后都须要手动delete。稍不注意就memory leak。

    智能指针能够解决上面遇到的问题。

    C++中常见的智能指针包含(共七种):
         std::auto_ptr
         boost::scoped_ptr
         boost::shared_ptr
         boost::intrusive_ptr
         boost::scoped_array
         boost::shared_array
         boost::weak_ptr

        事实上,智能指针并非指针,它不过一个栈对象而已。
    在栈对象的生命周期结束时,智能指针调用析构函数释放其管理的堆内存。
    全部的智能指针都重载了'operator->'操作符,用来返回其管理对象的引用。从而能够
    运行所管理对象的一些操作。


    两个方法:
         - get()
              訪问智能指针包括的裸指针引用
         - reset()
              若传递參数为空或NULL 则智能指针会释放当前管理的内存。

              若传递參数为一个对象 则智能指针会释放当前管理的内存,管理新传入的对象。

    假定有以下这个ManagedObj类。
    class ManagedObj
    {
    public:
        ManagedObj(int val = 0):m_val(val)
        {
            cout<<"Obj : "<<m_val<<endl;
        }
    
        ~ManagedObj()
        {
            cout<<"~Obj : "<<m_val<<endl;
        }
    
        void testFun()
        {
            cout<<"testFun : "<<m_info<<endl;
        }
    
    public:
        string m_info;
        int m_val;
    };

    -> std::auto_ptr

    属于STL
    在namespace std中
    加入头文件 #include <memory>就可以使用。
    auto_ptr一般用于管理单个堆内存对象。

    看以下这个样例:
    // std::auto_ptr
    void testAutoPtr1()
    {
        auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));
    
        if (atPtr.get())    // 推断智能指针是否为空
        {
            atPtr->testFun();
    
            atPtr.get()->m_info += " 1st append";    // get() 返回裸指针的引用
            atPtr->testFun();
    
            (*atPtr).m_info += " 2nd append";        // operator* 返回智能指针管理的对象
            (*atPtr).testFun();
        }
    }
    执行结果为:

    OK 好像没什么问题。

    我们接着測试:
    void testAutoPtr2()
    {
        auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));
    
        if (atPtr.get())    // 推断智能指针是否为空
        {
            auto_ptr<ManagedObj> atPtr2;
    
            atPtr2 = atPtr;               // 原因在这行代码
            atPtr2->testFun();
            atPtr->testFun();          // 崩溃在这行代码
        }
    }
    执行结果为:

    调试发现 最后一行代码出bug了。
    为什么呢?
    事实上是atPtr2剥夺了atPtr的内存管理全部权,导致atPtr悬空。

    继续測试。

    void testAutoPtr3()
    {
        auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));
    
        if (atPtr.get())    // 推断智能指针是否为空
        {
            atPtr.release();
        }
    }
    执行结果为:

    好像又出bug了。
    我们并没有看到析构函数被调用的迹象,内存泄漏了。。
    (逗我呢 怎么这么多bug?

    )


    别着急 第三个測试函数正确的写法应该是这种。
    void testAutoPtr3()
    {
        auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));
    
        if (atPtr.get())    // 推断智能指针是否为空
        {
            //ManagedObj* temp = atPtr.release();
            //delete temp;
    
            // 或者
            atPtr.reset();
        }
    }

    这才是我们想要看到的结果。


    所以我们也发现。auto_ptr的release()函数仅仅是让出内存的管理权,并没有真正的释放内存。

    总结:
         auto_ptr一般用于管理单个堆内存对象。

    可是须要注意:

         a. 切记使用 "operator="。万一真用了,就不要再使用旧的对象了。

         b. release()方法不会释放内存 只不过让出全部权。

    上面的auto_ptr使用起来确实不是非常方便,并且我们还有注意避开它的使用缺陷。

    因此就出现了boost中的一些智能指针,以下一一介绍。

    -> boost::scoped_ptr

    属于boost库
    在namespace boost中
    加入头文件 #include<boost/smart_ptr.hpp> 就可以使用。


    关于boost库的安装教程 可參考:

    与auto_ptr类似。scoped_ptr也可管理单个堆内存的对象,
    可是它独享全部权,因此也就避免了auto_ptr的缺陷

    OK。上代码測试。

    // boost::scoped_ptr
    void testScopedPtr()
    {
    	boost::scoped_ptr<ManagedObj> scPtr(new ManagedObj(1, " initialize"));
    
    	if (scPtr.get())
    	{
    		scPtr->testFun();
    
    		scPtr.get()->m_info += " 1st append";	// get() 返回裸指针的引用
    		scPtr->testFun();
    
    		(*scPtr).m_info += " 2nd append";		// operator* 返回智能指针管理的对象
    		(*scPtr).testFun();
    
    		//scPtr.release();				// error scoped_ptr 没有成员release
    		
    		//boost::scoped_ptr<ManagedObj> scPtr2;
    		//scPtr2 = scPtr;					// error scoped_ptr<T>::operator=(const scoped_ptr<ManagedObj> &)不可訪问
    	}
    }
    执行结果为:

    注意凝视部分的代码。
    scoped_ptr没有release()函数  屏蔽了operator=操作,
    因此不会出现auto_ptr中出现的内存泄漏和崩溃问题。
    但这也带来了还有一个新问题。我们无法复制智能指针。

    因此出现了接下来的shared_ptr。

    -> boost::shared_ptr

    属于boost库
    在namespace boost中
    加入头文件 #include<boost/smart_ptr.hpp> 就可以使用。


    因为scoped_ptr不同意赋值 拷贝 独享内存的全部权。
    这里的shared_ptr是专门解决智能指针的内存全部权共享这个问题的。
    其内部使用了引用计数。

    来,測试吧。

    // boost::shared_ptr
    void testSharedPtr(boost::shared_ptr<ManagedObj> ptr)
    {
        ptr->testFun();
        cout<<"shared_ptr use_count: "<<ptr.use_count()<<endl;
    }
    
    void testSharedPtr1()
    {
        boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, " initialize"));
    
        if (shPtr.get())
        {
            shPtr->testFun();
    
            shPtr.get()->m_info += " 1st append";    // get() 返回裸指针的引用
            shPtr->testFun();
    
            (*shPtr).m_info += " 2nd append";        // operator* 返回智能指针管理的对象
            (*shPtr).testFun();
        }
    
        cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl;
        testSharedPtr(shPtr);
        cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl;
         //shPtr.release(); // error shared_ptr 没有成员release
    }
    执行结果为:

    能够看到。调用testSharedPtr()函数运行了一次复制操作,智能指针内部的引用计数+1。
    在该函数调用结束后,引用计数自己主动-1。

    上面介绍的几种智能指针都针对单个对象的内存管理。

    事实上智能指针也可管理数组,接下来介绍boost::scoped_array和boost::shared_array。


    -> boost::scoped_array

    属于boost库
    在namespace boost中
    加入头文件 #include<boost/smart_ptr.hpp> 就可以使用。

    boost::scoped_array用于管理动态数组。也是独享全部权的。


    上代码:
    // boost::scoped_array
    void testScopedArray()
    {
        boost::scoped_array<ManagedObj> scArr(new ManagedObj[2]);        // 动态数组初始化
    
        if (scArr.get())
        {
            scArr[0].testFun();
    
            scArr.get()[0].m_info += " [0] 1st append";
            scArr[0].testFun();
    
            //(*scArr)[0].m_info += " 2st append";    // error scoped_array没有重载operator*
            //(*scArr)[0].testFun();
    
            //scArr[0].release();                        // error scoped_array 没有成员release
    
            boost::scoped_array<ManagedObj> scArr2;
            //scArr2 = scArr;                // error operator=不可訪问 屏蔽了operator= 禁止拷贝
        }
    }
    执行结果为:

    boost::scoped_array的使用和boost::scoped_ptr大致几乎相同。
    都禁止拷贝 独享全部权。

    注意:
         boost::scoped_array没有重载operator*操作符

    -> boost::shared_array

    属于boost库
    在namespace boost中
    加入头文件 #include<boost/smart_ptr.hpp> 就可以使用。


    内部使用了引用计数,解决參数传递和拷贝赋值时的问题。


    以下给出代码測试:
    // boost::shared_array
    void testSharedArray(boost::shared_array<ManagedObj> pArr)
    {
        cout<<"shared_arr use_count: "<<pArr.use_count()<<endl;
        boost::shared_array<ManagedObj> pTempArr;
        pTempArr = pArr;
        cout<<"shared_arr use_count: "<<pArr.use_count()<<endl;
    }
    
    void testSharedArray1()
    {
        boost::shared_array<ManagedObj> shArr(new ManagedObj[2]);
    
        if (shArr.get())
        {
            shArr[0].testFun();
    
            shArr.get()[0].m_info += " [0] 1st append";
            shArr[0].testFun();
            shArr[1].testFun();
    
            shArr.get()[0].m_info += " [1] 1st append";
            shArr[1].testFun();
            //(*shArr)[0].m_info += " [0] 2nd append";    // error 没有重载operator*操作符
        }
    
        cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl;
        testSharedArray(shArr);
        cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl;
    }
    执行结果为:


    -> boost::weak_ptr

    属于boost库
    在namespace boost中
    加入头文件 #include<boost/smart_ptr.hpp> 就可以使用。

    若我们只关心是否能使用对象。而不关心内部的引用计数。
    boost::weak_ptr 是 boost::shared_ptr 的观察者(Observer)对象,
    意味着boost::weak_ptr仅仅对boost::shared_ptr引用。可是并不更新
    其引用计数。被观察的shared_ptr失效后,weak_ptr对应也失效。


    上測试代码:
    // boost::weak_ptr
    void testWeakPtr()
    {
        boost::weak_ptr<ManagedObj> wPtr;
        boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, "initialize"));
    
        cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl;
        wPtr = shPtr;
        cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl;
    }
    执行结果为:

    我们发现赋值操作并没有更改引用计数。
    boost::weak_ptr很多其它用于以下这样的场合:
         在基类中定义一个weak_ptr,用于观察其子类中的shared_ptr。这样基类仅仅要看自己的weak_ptr
    是否为空就知道子类有没对自己赋值,而不影响子类中shared_ptr的引用计数。从而减少复杂度。更好管理对象。

    -> boost::intrusive_ptr

    属于boost库
    在namespace boost中
    加入头文件 #include<boost/smart_ptr.hpp> 就可以使用。

    intrusive_ptr是一种插入式智能指针,其内部没有引用计数,
    须要自己增加引用计数,否则会编译错误。



    OK 到这儿为止,七种智能指针基本上都大致介绍完了。


    參考:

    http://blog.csdn.net/xt_xiaotian/article/details/5714477


    完整的代码在这里

    #include <iostream>
    #include <memory>
    #include <string>
    #include <boost/smart_ptr.hpp>			// add header file
    
    // namespace 
    using namespace std;
    using namespace boost;
    
    // heap obj class
    class ManagedObj
    {
    public:
    	ManagedObj(int val = 0, string info = "")
    		:m_val(val), m_info(info)
    	{
    		cout<<"Obj : "<<m_val<<m_info<<endl;
    	}
    
    	~ManagedObj()
    	{
    		cout<<"~Obj : "<<m_val<<m_info<<endl;
    	}
    
    	void testFun()
    	{
    		cout<<"testFun : "<<m_info<<endl;
    	}
    
    public:
    	string m_info;
    	int m_val;
    };
    
    // std::auto_ptr
    void testAutoPtr1()
    {
    	auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));
    	
    	if (atPtr.get())	// 推断智能指针是否为空
    	{
    		atPtr->testFun();
    
    		atPtr.get()->m_info += " 1st append";	// get() 返回裸指针的引用
    		atPtr->testFun();
    
    		(*atPtr).m_info += " 2nd append";		// operator* 返回智能指针管理的对象
    		(*atPtr).testFun();
    	}
    }
    
    void testAutoPtr2()
    {
    	auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));
    
    	if (atPtr.get())	// 推断智能指针是否为空
    	{
    		auto_ptr<ManagedObj> atPtr2;
    
    		atPtr2 = atPtr;
    		atPtr2->testFun();
    		atPtr->testFun();
    	}
    }
    
    void testAutoPtr3()
    {
    	auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));
    
    	if (atPtr.get())	// 推断智能指针是否为空
    	{
    		//ManagedObj* temp = atPtr.release();
    		//delete temp;
    		
    		// 或者
    		atPtr.reset();
    	}
    }
    
    // boost::scoped_ptr
    void testScopedPtr()
    {
    	boost::scoped_ptr<ManagedObj> scPtr(new ManagedObj(1, " initialize"));
    
    	if (scPtr.get())
    	{
    		scPtr->testFun();
    
    		scPtr.get()->m_info += " 1st append";	// get() 返回裸指针的引用
    		scPtr->testFun();
    
    		(*scPtr).m_info += " 2nd append";		// operator* 返回智能指针管理的对象
    		(*scPtr).testFun();
    
    		//scPtr.release();				// error scoped_ptr 没有成员release
    		
    		//boost::scoped_ptr<ManagedObj> scPtr2;
    		//scPtr2 = scPtr;					// error scoped_ptr<T>::operator=(const scoped_ptr<ManagedObj> &)不可訪问
    	}
    }
    
    // boost::shared_ptr
    void testSharedPtr(boost::shared_ptr<ManagedObj> ptr)
    {
    	ptr->testFun();
    	cout<<"shared_ptr use_count: "<<ptr.use_count()<<endl;
    }
    
    void testSharedPtr1()
    {
    	boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, " initialize"));
    
    	if (shPtr.get())
    	{
    		shPtr->testFun();
    
    		shPtr.get()->m_info += " 1st append";	// get() 返回裸指针的引用
    		shPtr->testFun();
    
    		(*shPtr).m_info += " 2nd append";		// operator* 返回智能指针管理的对象
    		(*shPtr).testFun();
    	}
    
    	cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl;
    	testSharedPtr(shPtr);
    	cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl;
    
    	//shPtr.release();	// error shared_ptr 没有成员release
    }
    
    // boost::scoped_array
    void testScopedArray()
    {
    	boost::scoped_array<ManagedObj> scArr(new ManagedObj[2]);		// 动态数组初始化
    
    	if (scArr.get())
    	{
    		scArr[0].testFun();
    
    		scArr.get()[0].m_info += " [0] 1st append";
    		scArr[0].testFun();
    
    		//(*scArr)[0].m_info += " 2st append";	// error scoped_array没有重载operator*
    		//(*scArr)[0].testFun();
    		
    		//scArr[0].release();						// error scoped_array 没有成员release
    
    		boost::scoped_array<ManagedObj> scArr2;
    		//scArr2 = scArr;				// error operator=不可訪问 屏蔽了operator= 禁止拷贝
    	}
    }
    
    // boost::shared_array
    void testSharedArray(boost::shared_array<ManagedObj> pArr)
    {
    	cout<<"shared_arr use_count: "<<pArr.use_count()<<endl;
    
    	boost::shared_array<ManagedObj> pTempArr;
    	pTempArr = pArr;
    	cout<<"shared_arr use_count: "<<pArr.use_count()<<endl;
    }
    
    void testSharedArray1()
    {
    	boost::shared_array<ManagedObj> shArr(new ManagedObj[2]);
    
    	if (shArr.get())
    	{
    		shArr[0].testFun();
    
    		shArr.get()[0].m_info += " [0] 1st append";
    		shArr[0].testFun();
    		shArr[1].testFun();
    
    		shArr.get()[1].m_info += " [1] 1st append";
    		shArr[1].testFun();
    		//(*shArr)[0].m_info += " [0] 2nd append";	// error 没有重载operator*操作符
    	}
    
    	cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl;
    	testSharedArray(shArr);
    	cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl;
    }
    
    // boost::weak_ptr
    void testWeakPtr()
    {
    	boost::weak_ptr<ManagedObj> wPtr;
    	boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, "initialize"));
    
    	cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl;
    	wPtr = shPtr;
    	cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl;
    }
    
    // boost::intrusive_ptr
    void testIntrusivePtr()
    {
    	//boost::intrusive_ptr<ManagedObj> intPtr(new ManagedObj[1], false);	// error 要自己加入引用计数
    }
    
    // main function
    int main(void)
    {
    	// -----std::auto_ptr-----
    	//testAutoPtr();
    	//testAutoPtr2();
    	//testAutoPtr3();
    
    	// -----boost::scoped_ptr-----
    	//testScopedPtr();
    
    	// -----boost::shared_ptr-----
    	//testSharedPtr1();
    
    	// -----boost::scoped_array-----
    	//testScopedArray();
    
    	// -----boost::shared_array-----
    	//testSharedArray1();
    
    	// -----boost::weak_ptr-----
    	testWeakPtr();
    
    	return 0;
    }


  • 相关阅读:
    簇索引与非簇索引在查询中的应用与分析(转载) dodo
    都是防火墙惹的祸:sqlserver连接服务器数据库提示注册失败 dodo
    C#中如何将字符串转换成流,同时如何将流转换成字符串? dodo
    dataType参数不能为空,未处理的“System.ArgumentNullException”类型的异常出现在 mscorlib.dll 中 dodo
    错误(Client does not support authentication protocol requested by server; consider upgrading MySQL client)? dodo
    php分页函数(转) dodo
    c#2.0匿名方法三(转) dodo
    ASP.NET 2.0 中改进的缓存功能(转) dodo
    解决apache服务器默认编码为西欧编码的问题 dodo
    常用的正则表达式 dodo
  • 原文地址:https://www.cnblogs.com/llguanli/p/8287476.html
Copyright © 2011-2022 走看看