你说你会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; }