首页 > 代码库 > 智能指针
智能指针
1. 为什么要智能指针?
由于C++语言没有自动内存回收机制,程序员每次new出来的内存都要手动delete。但是有时候可能程序员会忘记delete,也可能是因为流程太复杂,最终没有delte,也可能是因为异常的存在,导致程序过早的退出,没有执行delete。
用只能指针可以有效的解决这些问题。std::auto_ptr,boost::scope_ptr,boost::scoped_array,boost::intrusive_ptr。
智能指针之所以能自动释放内存,是因为智能指针实际上是一个栈对象,并非指针类型,在栈对象结束时,智能指针通过析构函数释放所有它管理的堆内存。所有稚嫩指针都重载了“operator->”操作符,直接返回对象的引用,用以操作对象。访问只能指针原来的方法是用“.”操作符,
访问智能指针包含的裸指针可以用get()函数。由于智能指针是一个对象,所以if(my_smart_object)永远为真,要判断智能指针的裸指针是否为空,需要判断: if(my_smart_object.get())。
智能指针包含reset()方法,如果不传递参数(或者传递NULL),则智能指针会释放前管理的内存,如果传递一个对象,则智能指针会释放当前对象,来管理新传入的对象。
定义一个测试用的辅助类:
class Simple {public: Simple(int param = 0){ number = param; cout << "Simple: " << number << endl; } ~Simple() { cout << "~Simple: " << number << endl; } void Speaker() { cout << "Saying: " << info_exted.c_str() << endl; } std::string info_exted; int number;};
2. 正确使用auto_ptr
先看下正确使用auto_ptr指针的例子:
// Use auto_ptr correctlyvoid testAutoPtr() { auto_ptr<Simple> my_memory(new Simple(1)); // Create an instance of Simple and memory allocated from heap; // using the auto_ptr my_memory to arrange it. if (my_memory.get()) { // Check if the Simple instance was correctly created. my_memory->Speaker(); // Initial speaker: info_extend has nothing my_memory.get()->info_exted = "Addition"; // Use the get() function to reteive the bare poiter and to add information to info_extend. my_memory->Speaker(); // Saying: Addition (*my_memory).info_exted += " other"; my_memory->Speaker(); // Saying: Addition other my_memory->info_exted += " other again!"; my_memory->Speaker(); // Saying: Addition other other again! }}
首先,在testAutoPtr中,首先定义了一个auto_ptr对象,并且使用它来管理一块Simple对象的内存。在进行其他操作之前,先检查new Simple(1)是否成功,既是检查原始指针是否为空。通过一些列的操作来判断是否正确。然后进行各种操作,最后我们没用显示的使用delete来释放simpl对象内存,但是从输出可以看的到,析构函数还是被调用了的,因为在退出my_memory的作用域时,释放了其管理的堆内存。如果在testAutoPtr()中有异常抛出会发生什么呢? 可以这样修改一下testAutoPtr:
// Use auto_ptr correctlyvoid testAutoPtr() { auto_ptr<Simple> my_memory(new Simple(1)); if (my_memory.get()) { my_memory->Speaker(); my_memory.get()->info_exted = "Addition"; my_memory->Speaker(); (*my_memory).info_exted += " other"; throw new exception("This is an exception!"); // throw an exception here my_memory->Speaker(); my_memory->info_exted += " other again!"; my_memory->Speaker(); }}int main(){ try { testAutoPtr(); } catch (...) { cout << "Exception catched! " << endl; }}
最后程序运行的输出如下:
Simple: 1Syaing:Saying: Addition~Simple: 1Exception catched!
可见,智能指针的确自动调用了Simple类的析构函数。
3. auto_ptr出错的情况
auto_ptr不支持赋值函数,但是确没有明确的禁止使用“=”操作符。
// Failure in using auto_ptrvoid testAutoPtr2() { auto_ptr<Simple> my_memory(new Simple(1)); if (my_memory.get()) { auto_ptr<Simple> my_memory2; my_memory2 = my_memory; // error begins from here cout << my_memory2->number << endl; my_memory2->Speaker(); my_memory->Speaker(); // execution crashed here }}
使用my_memory2 = my_memory后,my_memroy2完全剥夺了my_memory对Simple对象内存的所有权,导致my_memory被悬空,从而再之后使用时出错。
所以:切记,在使用auto_ptr时,最好不要使用"operator="操作符!!!
再看另一个例子:
void testAutoPtr3() { auto_ptr<Simple> my_memory(new Simple(1)); if (my_memory.get()) { my_memory.release(); }}
结果是指只有构造函数被调用,而析构函数并没有被调用,造成了内存泄漏!!!这个错误的根源就是对release函数的误解,这里的release函数其实只是释放对内存的使用权,而不是释放内存空间。假如我们不想等到自动内存对象在离开作用域时才释放内存空间,不想其一直占用着内存,正确的代码如下:
void testAutoPtr3() { auto_ptr<Simple> my_memory(new Simple(1)); if (my_memory.get()) { Simple *temp_memory = my_memory.release(); delete temp_memory; }}
或者:
void testAutoPtr3() { auto_ptr<Simple> my_memory(new Simple(1)); if (my_memory.get()) { my_memory.reset(); // release the inner memory }}
4. auto_ptr总结
- 尽量不要使用”operator=“,如果使用了,请不要再使用先前的对象。
- 记住release()函数不会释放对象,仅仅是归还所有权。
- 最好不用当成参数传递。
- 由于不能使用=操作符,由其管理的对象不能放入std::vector等容器中。
正是由于这些限制的存在,boost中改进了自动指针,实现了boost::scope_ptr,boost::scoped_array,boost::intrusive_ptr智能指针。关于这些智能指针的使用,可以参考《C++ 智能指针详解》。
最常用的智能指针:
- std::auto_ptr,有很多问题,不能用赋值操作符,不支持拷贝构造函数,但是复制和赋值时又不会报错。因为不能被复制,所以不能放入容器中。
- C++11引入的unique_ptr,也不支持复制和赋值,但是好在如果直接赋值会出错,实在要复制的话可以使用std::move。
- C++11或者boost中有shared_ptr,是基于引用计数的智能指针。可随意复制和赋值,知道内存的引用计数为0时,这个内存才被释放。
5. 自己实现auto_ptr
有些BT级别的面试官可能会要求你自己写一个auto_ptr类,简单的来讲,一个auto_ptr必须包含构造函数、拷贝构造函数、析构函数、重载的"operater*"、重载的"operater->" 。这里给出两个版本,目前都还没测试是否可用,有时间再测试一下。
第一个,不适用引用计数,简单的智能指针:
template<typename T>class m_auto_ptr {public: m_auto_ptr(T *pt = 0) : _ptr(pt) { } ~m_auto_ptr() { delete _ptr; } T& operator*() { if (_ptr) return *_ptr; else throw new exception("smart pointer points to NULL"); } T* operator->() { if (_ptr) return _ptr; else throw new exception("smart pointer points to NULL"); }private: T *_ptr;};
第二个版本,使用引用计数的方法,如下:
template<typename T>class SmartPointer {public: SmartPointer(T *pt = 0) : _ptr(pt), _ref_count(new int) { if (pt) *_ref_count = 1; else *_ref_count = 0; } SmartPointer(const SmartPointer& src) { if (&src != this) { _ptr = src._ptr; _ref_count = src._ref_count;; (*_ref_count)++; } } ~SmartPointer() { (*_ref_count)--; if ((*_ref_count) == 0) { delete _ref_count; delete _ptr; } } T* get() { if (_ptr) return _ptr; else throw new exception("smart pointer points to NULL"); } T& operator*() { if (_ptr) return *_ptr; else throw new exception("smart pointer points to NULL"); } T* operator->() { if (_ptr) return _ptr; else throw new exception("smart pointer points to NULL"); } SmartPointer& operator=(const SmartPointer& src) { if (&src =http://www.mamicode.com/= this) return *this; else { releaseCount(); _ptr = src._ptr; _ref_count = src._ref_count; (*_ref_count)++; return *this; } }private: T *_ptr; int *_ref_count; void releaseCount() { if (_ptr) { (*_ref_count)--; if ((*_ref_count) == 0) { delete _ptr; delete _ref_count; } } }};