众所周知,C++中对堆内存的申请与释放完全由用户来控制,这就造成用户在使用的时候经常造成内存泄漏、野指针、重复释放等常见的挂掉问题,所以我们有必要提供一套机制,使得用户只需申请相应的内存,不用管释放的问题,其实这属于著名的RAII(Resource Acquisition Is Initialization)技术 。在C++中这种技术称作“智能指针”,C++中的智能指针技术越来越受到广泛应用。下面简要介绍下智能指针。
从以上描述中可以看出,我们需要提供一套内存显式申请与隐式释放,并向用户屏蔽这些细节的机制,对于这种隐藏细节的做法,我们通常会用一个句柄类来封装。在这里句柄类就是我们提供给用户的智能指针类。
智能指针为何“智能”呢?这里就涉及到另一项重要技术”引用计数“,当有N个智能指针句柄类指向同一段内存,这时就会将这段内存的引用计数设置为N,每当其中的一个句柄离开作用域时会自动调用析构函数,将内存引用计数减一,这样当某个智能指针句柄类的引用计数为1时,表示这段内存只有该句柄指向它。在这个过程中,析构函数起很大的作用,RAII技术也是基于析构函数而实现的。
说了这么多,直接看代码:
/* * refcount.h 引用计数 * [email protected] */ #ifndef _REFCOUNT_H_ #define _REFCOUNT_H_ class RefCount { public: RefCount() : use(new size_t(1)){} RefCount(const RefCount &refcnt) : use(refcnt.use) { inc(); } ~RefCount() { if (decr() == 0) { delete use; } } RefCount & operator=(const RefCount &refcnt) { if (decr() == 0) { delete use; } use = refcnt.use; inc(); return *this; } void init() { use = new size_t(1); } bool only() { return *use == 1; } inline size_t inc() { return ++*use; } inline size_t decr() { return --*use; } bool reattach(const RefCount &refcnt) { ++*refcnt.use; if (only()) { delete use; use = refcnt.use; return true; } else { --*use; use = refcnt.use; return false; } } private: size_t *use; }; #endif // _REFCOUNT_H_
/* * smartpointer.h 智能指针 * [email protected] */ #ifndef _SMARTPOINTER_H_ #define _SMARTPOINTER_H_ #include "refcount.h" template <typename T> class SmartPtr { public: SmartPtr() : ptr_(new T) { refcnt_.init(); } SmartPtr(const T & obj) : ptr_(new T(obj)){} SmartPtr(T * pobj) : ptr_(pobj) { //refcnt_初始化时,*use = 1 if (pobj == nullptr) { refcnt_.decr(); } } SmartPtr(const SmartPtr &spnt) : ptr_(spnt.ptr_), refcnt_(spnt.refcnt_){ } ~SmartPtr() { if (refcnt_.only()) { delete ptr_; ptr_ = nullptr; } } SmartPtr & operator=(const SmartPtr &spnt) { //这一步相当于对refcnt_进行赋值 if (refcnt_.reattach(spnt.refcnt_)) { delete ptr_; } ptr_ = spnt.ptr_; return *this; } T * operator->() { return ptr_; } private: T * ptr_; RefCount refcnt_; }; #endif // _SMARTPOINTER_H_
/* * main.cpp 测试程序 * [email protected] */ #include "memleakcheck.h" #include "smartpointer.h" #include <iostream> #include <string> #include <boost/shared_ptr.hpp> int main(void) { { SmartPtr<int> sp(2); SmartPtr<int> sp2(sp); SmartPtr<int> sp3(3); sp = sp3; sp2 = sp3; } { SmartPtr<std::string> sp("123"); SmartPtr<std::string> sp2(sp); SmartPtr<std::string> sp3("abc"); sp = sp3; sp2 = sp3; } { SmartPtr<std::string> sp(new std::string("123")); SmartPtr<std::string> sp2(sp); SmartPtr<std::string> sp3(new std::string("abc")); sp = sp3; sp2 = sp3; } { boost::shared_ptr<char> pch(new char[10]); } _CrtDumpMemoryLeaks(); return 0; }
调试时,发现输出窗口并没有检测到内存泄漏,说明智能指针实现正确。main.cpp中还测试了Boost库中智能指针的使用,可以看到也是很方便的,目前在开源点云库PCL中随处都可见到Boost库中的智能指针。
以上代码见github:https://github.com/lming08/Ruminations/
参考资料:
http://www.cnblogs.com/zhangyunkui/archive/2009/11/13/1602514.html
http://blog.csdn.net/windows_nt/article/details/8652191
C++沉思录