「C++」理解智能指针
2013-07-19 11:00
495 查看
维基百科上面对于「智能指针」是这样描述的:
智能指针(英语:Smart pointer)是一种抽象的数据类型。在程序设计中,它通常是经由类型模板(class template)来实做,借由模板(template)来达成泛型,通常借由类型(class)的解构函数来达成自动释放指针所指向的存储器或对象。
简单的来讲,智能指针是一种看上去类似指针的数据类型,只不过它更加智能,懂的完成内存泄露,垃圾回收等一系列看上去很智能的工作。如你所看到的那样,借助 C++ RAII(Resource acquisition is initialization) 特性,在类型(class)的析构函数时来完成自动释放指针所指向对象的目的。
1、什么是智能指针?
先看看一个最简单的例子 auto_ptr:
首先它拥有指针最基本的 2 个特性:deferencing(operator *) 和 indirection(operator ->). 于是下面的代码
可以写成:
这样我们新申请的 MyClass 可以完全由智能指针 p 接管,p 知道何时去释放这块内存,而不需要程序员去操心。
2、为什么要用智能指针?
使用智能指针的好处是显而易见的,正如上面所举例,可以有效的防止因为程序员粗心而引发的内存泄露问题。当然,智能指针所能达到的效果还远不止于此,它可以使你的程序更加安全、高效。当上面的 void foo() 函数出现异常的时候,我们不得不修改程序成为下面的样子:
可以想象,当程序逻辑越来越复杂的时候,传统的代码将会变得更加臃肿不堪。从美观的角度来说,这样的代码或许缺少点艺术性在里面,那么还是用智能指针吧,代码依然如此简洁、优雅。
再看看下面这个场景:
当出现访问异常的时候,可能要耗费程序员很多精力去排查这类问题,因为 delete p 之后 p 可能依然指向某块内存(悬挂的)但是却是无效的指针。下面看看 auto_ptr 处理 operator = 的做法:
可以看出,auto_ptr 把 q 指向 p 指向的内存,并且 p 指针赋值为 null 了。不同类型的智能指针针对类似问题解决的方案是不同的:
a. copied_ptr: q 指向的内存是 p 指向内存的一个拷贝。
b. owned_ptr: 让 p 和 q 指向同一块内存,只不过把 clean up 的责任转交给了 q。
c. counted_ptr: 维护一个所申请内存块的计数 count,当 q = p 时 count 加 1,当 count 为 0 时释放内存。
d. linked_ptr: 所有的智能指针组成一个双向链表,但是所有的指针都是指向同一块内存,当出现 q = p 时把 q 加入到这个双向链表中。
e. cow_ptr: Copy-On-Write 机制,本质上是 counted_ptr or linked_ptr,仅当有意图要写内存时才为 q 重新开辟新的内存。
上面的代码展示了 Copy-On-Write 机制产生的时机,这也解释了为什么智能指针会比普通指针更加高效的原因。同样的手法在 string 类中也出现过:
3、选择哪种智能指针?
关于 counted_ptr 有 2 种不同的实现方法,intrusive(侵入式)和 non-intrusive(非侵入式):
关于 linked_ptr,在多线程环境下容易引起死锁问题:
下面给出了一个总结,什么时候应该应用什么样的智能指针:
Local variables auto_ptr
Class members Copied pointer
STL Containers Garbage collected pointer (e.g. reference counting/linking)
Explicit ownership transfer Owned pointer
Big objects Copy on write
「参考资料」
http://en.wikipedia.org/wiki/Smart_pointer http://ootips.org/yonat/4dev/smart-pointers.html
智能指针(英语:Smart pointer)是一种抽象的数据类型。在程序设计中,它通常是经由类型模板(class template)来实做,借由模板(template)来达成泛型,通常借由类型(class)的解构函数来达成自动释放指针所指向的存储器或对象。
简单的来讲,智能指针是一种看上去类似指针的数据类型,只不过它更加智能,懂的完成内存泄露,垃圾回收等一系列看上去很智能的工作。如你所看到的那样,借助 C++ RAII(Resource acquisition is initialization) 特性,在类型(class)的析构函数时来完成自动释放指针所指向对象的目的。
1、什么是智能指针?
先看看一个最简单的例子 auto_ptr:
template <class T> class auto_ptr { T* ptr; public: explicit auto_ptr(T* p = 0) : ptr(p) {} ~auto_ptr() {delete ptr;} T& operator*() {return *ptr;} T* operator->() {return ptr;} // ... };
首先它拥有指针最基本的 2 个特性:deferencing(operator *) 和 indirection(operator ->). 于是下面的代码
void foo() { MyClass* p(new MyClass); p->DoSomething(); delete p; }
可以写成:
void foo() { auto_ptr<MyClass> p(new MyClass); p->DoSomething(); }
这样我们新申请的 MyClass 可以完全由智能指针 p 接管,p 知道何时去释放这块内存,而不需要程序员去操心。
2、为什么要用智能指针?
使用智能指针的好处是显而易见的,正如上面所举例,可以有效的防止因为程序员粗心而引发的内存泄露问题。当然,智能指针所能达到的效果还远不止于此,它可以使你的程序更加安全、高效。当上面的 void foo() 函数出现异常的时候,我们不得不修改程序成为下面的样子:
void foo() { MyClass* p; try { p = new MyClass; p->DoSomething(); delete p; } catch (...) { delete p; throw; } }
可以想象,当程序逻辑越来越复杂的时候,传统的代码将会变得更加臃肿不堪。从美观的角度来说,这样的代码或许缺少点艺术性在里面,那么还是用智能指针吧,代码依然如此简洁、优雅。
再看看下面这个场景:
MyClass* p(new MyClass); MyClass* q = p; delete p; p->DoSomething(); // p is now dangling p = NULL; // p is no longer dangling q->DoSomething(); // q is still dangling
当出现访问异常的时候,可能要耗费程序员很多精力去排查这类问题,因为 delete p 之后 p 可能依然指向某块内存(悬挂的)但是却是无效的指针。下面看看 auto_ptr 处理 operator = 的做法:
template <class T> auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<T>& rhs) { if (this != &rhs) { delete ptr; ptr = rhs.ptr; rhs.ptr = NULL; } return *this; }
可以看出,auto_ptr 把 q 指向 p 指向的内存,并且 p 指针赋值为 null 了。不同类型的智能指针针对类似问题解决的方案是不同的:
a. copied_ptr: q 指向的内存是 p 指向内存的一个拷贝。
b. owned_ptr: 让 p 和 q 指向同一块内存,只不过把 clean up 的责任转交给了 q。
c. counted_ptr: 维护一个所申请内存块的计数 count,当 q = p 时 count 加 1,当 count 为 0 时释放内存。
d. linked_ptr: 所有的智能指针组成一个双向链表,但是所有的指针都是指向同一块内存,当出现 q = p 时把 q 加入到这个双向链表中。
e. cow_ptr: Copy-On-Write 机制,本质上是 counted_ptr or linked_ptr,仅当有意图要写内存时才为 q 重新开辟新的内存。
const X& operator*() const throw() {return *itsPtr;} const X* operator->() const throw() {return itsPtr.get();} const X* get() const throw() {return itsPtr.get();} X& operator*() {copy(); return *itsPtr;} X* operator->() {copy(); return itsPtr.get();} X* get() {copy(); return itsPtr.get();} private: counted_ptr<X> itsPtr; void copy() // create a new copy of itsPtr { if (!itsPtr.unique()) { X* old_p = itsPtr.get(); itsPtr = counted_ptr<X>(new X(*old_p)); } }
上面的代码展示了 Copy-On-Write 机制产生的时机,这也解释了为什么智能指针会比普通指针更加高效的原因。同样的手法在 string 类中也出现过:
string s("Hello"); string t = s; // t and s shared the same 'hello' t += " there!"; // now a new buffer allocated for t
3、选择哪种智能指针?
关于 counted_ptr 有 2 种不同的实现方法,intrusive(侵入式)和 non-intrusive(非侵入式):
关于 linked_ptr,在多线程环境下容易引起死锁问题:
下面给出了一个总结,什么时候应该应用什么样的智能指针:
Local variables auto_ptr
Class members Copied pointer
STL Containers Garbage collected pointer (e.g. reference counting/linking)
Explicit ownership transfer Owned pointer
Big objects Copy on write
「参考资料」
http://en.wikipedia.org/wiki/Smart_pointer http://ootips.org/yonat/4dev/smart-pointers.html
相关文章推荐
- c++智能指针的理解
- 关于C++智能指针的理解auto_ptr
- 深入理解C++中的智能指针auto_ptr
- 理解C++智能指针
- c++ 智能指针初步理解
- C++智能指针的理解与实现
- C++智能指针的理解与实现
- 理解和使用C++中的智能指针
- C++ 智能指针——简单实现以及循环引用问题
- C++中智能指针的实现
- C++ 智能指针详解
- C++智能指针auto_ptr的原理及使用
- c++智能指针关联容器应用实例:文本查询程序
- C++箴言:将new出来的对象存入智能指针
- C++中智能指针的设计和使用
- 智能指针和引用计数以及String的C++实现
- c++智能指针的创建
- 对C++中指针做函数形参的理解
- C++智能指针分类及使用
- c++ 一种智能指针的实现