维基百科上面对于「智能指针」是这样描述的:

智能指针(英语: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++」理解智能指针的更多相关文章

  1. 深入理解智能指针之shared_ptr(一)

    本文基于C++标准库源码分析shared_ptr,旨在搞清楚shared_ptr是什么,线程安全性等,目标能够安全的使用智能指针. (一)shared_ptr是一个类. 首先可以确定的是shared_ ...

  2. 「数据结构」:模拟指针(simulated pointer)

    模拟指针,也就是清华严老师<数据结构-C语言描述>中的静态链表,静态链表的引用是使用一段连续的存储区还模拟指针的功能,可以有效的利用一段连续内存进行一定范围内可变的子链表的空间分配,此数据 ...

  3. 「Flink」理解流式处理重要概念

    什么是流式处理呢? 这个问题其实我们大部分时候是没有考虑过的,大多数,我们是把流式处理和实时计算放在一起来说的.我们先来了解下,什么是数据流. 数据流(事件流) 数据流是无边界数据集的抽象 我们之前接 ...

  4. C++11中智能指针的原理、使用、实现

    目录 理解智能指针的原理 智能指针的使用 智能指针的设计和实现 1.智能指针的作用 C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理.程序员自己管理堆内存可以提高了程序 ...

  5. C++智能指针 原理、使用与实现

    目录 理解智能指针的原理 智能指针的使用 智能指针的设计和实现 1.智能指针的作用 C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理.程序员自己管理堆内存可以提高了程序 ...

  6. 【入门必看】不理解「对象」?很可能有致命bug:简单的Python例子告诉你

    简介:越来越多的人要在学习工作中用到『编程』这个工具了,其中很大一部分人用的是Python.大部分人只是做做简单的科研计算.绘图.办公自动化或者爬虫,但-- 这就不需要理解「指针与面向对象」了吗? 在 ...

  7. 36氪首发 | 「myShape」完成千万级人民币 Pre-A轮融资,推出 AI 智能健身私教

    无需任何可穿戴设备. 36氪获悉,myShape(原Shapejoy)已于近期完成千万级人民币的Pre-A轮融资,由天奇阿米巴领投,远洋集团.七熹资本以及老股东跟投.过去 myShape 曾获得元迅资 ...

  8. 后盾网lavarel视频项目---Laravel 安装代码智能提示扩展「laravel-ide-helper」

    后盾网lavarel视频项目---Laravel 安装代码智能提示扩展「laravel-ide-helper」 一.总结 一句话总结: laravel-ide-helper作用是:代码提示 larav ...

  9. C++11智能指针的深度理解

    平时习惯使用cocos2d-x的Ref内存模式,回过头来在控制台项目中觉得c++的智能指针有点生疏,于是便重温一下.首先有请c++智能指针们登场: std::auto_ptr.std::unique_ ...

随机推荐

  1. caffe调试小结2

    1.裁剪  crop size227,在训练时候采用随机裁剪,在测试时候只裁剪中间部分

  2. html种种

    DIV+CSS如何让文字垂直居中?--https://zhidao.baidu.com/question/69214815.html

  3. 把url参数转化成一个对象返回

    var readUrlToParams = function () { var url = location.href; var nameValue; var paraString = url.sub ...

  4. BZOJ 4742: [Usaco2016 Dec]Team Building

    4742: [Usaco2016 Dec]Team Building Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 21  Solved: 16[Su ...

  5. hibernate- Hibernate中多对多的annotation的写法(中间表可以有多个字段)

    http://blog.csdn.net/liuxianbing119/article/details/7283769

  6. mysqlbinlog

    一.描述 转换二进制日志为易读的文本格式或用于管道后恢复数据 二.用法 -d, --database=name 仅列出指定数据库的条目 --start-datetime=name 从指定时间开始读取事 ...

  7. LINQ函数

    LINQ函数虽然和LINQ语句实现了同样的功能,但LINQ函数使用起来更加快捷.学过数据库的感觉LINQ语句都不难,但语句比较长. 会LINQ函数,才算会LINQ. 1.Where(),结果过滤 Li ...

  8. Codeforces 划水

    Codeforces 566F 题目大意:给定$N$个数,任意两个数之间若存在一个数为另一个数的因数,那么这两个数存在边,求图中最大团. 分析:求一个图最大团为NP-Hard问题,一般不采用硬方法算. ...

  9. Android SharedPreferences存储

    原创文章,转载请注明出处:http://www.cnblogs.com/baipengzhan/p/Android_SharedPreferences.html 一 概念 SharedPreferen ...

  10. HTML DOM prompt() 方法

    1. 定义和用法 prompt() 方法用于显示可提示用户进行输入的对话框. 语法 prompt(text,defaultText) 参数 描述 text 可选.要在对话框中显示的纯文本(而不是 HT ...