程序员人生 网站导航

C++智能指针简单剖析

栏目:php教程时间:2014-12-13 08:50:28

导读

最近在补看《C++ Primer Plus》第6版,这的确是本好书,其中关于智能指针的章节解析的非常清晰,1解我之前的多处困惑。C++面试进程中,很多面试官都喜欢问智能指针相干的问题,比如你知道哪些智能指针?shared_ptr的设计原理是甚么?如果让你自己设计1个智能指针,你如何完成?等等……。而且在看开源的C++项目时,也能随处看到智能指针的影子。这说明智能指针不但是面试官爱问的题材,更是非常有实用价值。

下面是我在看智能指针时所做的笔记,希望能够解决你对智能指针的1些困扰。

目录

  1. 智能指针背后的设计思想
  2. C++智能指针简单介绍
  3. 为何摒弃auto_ptr?
  4. unique_ptr为什么优于auto_ptr?
  5. 如何选择智能指针?

正文

1. 智能指针背后的设计思想

我们先来看1个简单的例子:

void remodel(std::string & str) { std::string * ps = new std::string(str); ... if (weird_thing()) throw exception(); str = *ps; delete ps; return; }

当出现异常时(weird_thing()返回true),delete将不被履行,因此将致使内存泄漏。
如何避免这类问题?有人会说,这还不简单,直接在throw exception();之前加上delete ps;不就好了。是的,你本应如此,问题是很多人都会忘记在适当的地方加上delete语句(连上述代码中最后的那句delete语句也会有很多人忘记吧),如果你要对1个庞大的工程进行review,看是不是有这类潜伏的内存泄漏问题,那就是1场灾害!
这时候我们会想:当remodel这样的函数终止(不论是正常终止,还是由于出现了异常而终止),本地变量都将自动从栈内存中删除―因此指针ps占据的内存将被释放,如果ps指向的内存也被自动释放,那该有多好啊。
我们知道析构函数有这个功能。如果ps有1个析构函数,该析构函数将在ps过期时自动释放它指向的内存。但ps的问题在于,它只是1个常规指针,不是有析构凼数的类对象指针。如果它指向的是对象,则可以在对象过期时,让它的析构函数删除指向的内存。

这正是 auto_ptr、unique_ptr和shared_ptr这几个智能指针背后的设计思想。我简单的总结下就是:将基本类型指针封装为类对象指针(这个类肯定是个模板,以适应不同基本类型的需求),并在析构函数里编写delete语句删除指针指向的内存空间。

因此,要转换remodel()函数,应按下面3个步骤进行:

  • 包括头义件memory(智能指针所在的头文件);
  • 将指向string的指针替换为指向string的智能指针对象;
  • 删除delete语句。

下面是使用auto_ptr修改该函数的结果:

# include <memory> void remodel (std::string & str) { std::auto_ptr<std::string> ps (new std::string(str)); ... if (weird_thing ()) throw exception(); str = *ps; // delete ps; NO LONGER NEEDED return; }

2. C++智能指针简单介绍

STL1共给我们提供了4种智能指针:auto_ptr、unique_ptr、shared_ptr和weak_ptr(本文章暂不讨论)。
模板auto_ptr是C++98提供的解决方案,C+11已将将其摒弃,并提供了另外两种解决方案。但是,虽然auto_ptr被摒弃,但它已使用了好多年:同时,如果您的编译器不支持其他两种解决力案,auto_ptr将是唯1的选择。

使用注意点

  • 所有的智能指针类都有1个explicit构造函数,以指针作为参数。比如auto_ptr的类模板原型为:
    templet<class T> class auto_ptr { explicit auto_ptr(X* p = 0) ; ... };

    因此不能自动将指针转换为智能指针对象,必须显式调用:

    shared_ptr<double> pd; double *p_reg = new double; pd = p_reg; // not allowed (implicit conversion) pd = shared_ptr<double>(p_reg); // allowed (explicit conversion) shared_ptr<double> pshared = p_reg; // not allowed (implicit conversion) shared_ptr<double> pshared(p_reg); // allowed (explicit conversion)
  • 对全部3种智能指针都应避免的1点:
    string vacation("I wandered lonely as a cloud."); shared_ptr<string> pvac(&vacation); // No
    pvac过期时,程序将把delete运算符用于非堆内存,这是毛病的。

使用举例

#include <iostream> #include <string> #include <memory> class report { private: std::string str; public: report(const std::string s) : str(s) { std::cout << "Object created. "; } ~report() { std::cout << "Object deleted. "; } void comment() const { std::cout << str << " "; } }; int main() { { std::auto_ptr<report> ps(new report("using auto ptr")); ps->comment(); } { std::shared_ptr<report> ps(new report("using shared ptr")); ps->comment(); } { std::unique_ptr<report> ps(new report("using unique ptr")); ps->comment(); } return 0; }

3. 为何摒弃auto_ptr?

先来看下面的赋值语句:

auto_ptr< string> ps (new string ("I reigned lonely as a cloud.”); auto_ptr<string> vocation; vocaticn = ps;

上述赋值语句将完成甚么工作呢?如果ps和vocation是常规指针,则两个指针将指向同1个string对象。这是不能接受的,由于程序将试图删除同1个对象两次――1次是ps过期时,另外一次是vocation过期时。要避免这类问题,方法有多种:

  • 定义
------分隔线----------------------------
------分隔线----------------------------

最新技术推荐