zoukankan      html  css  js  c++  java
  • C++的优秀特性6:智能指针

    (转载请注明原创于潘多拉盒子)

      智能指针(Smart Pointer)是C++非常重要的特性。考虑如下一段使用简单指针(Plain Pointer)的代码:

    A* a = new A();
    B* b = new B();
    ......
    if (condition1)
    {
        // return之前必须delete所有new出来的对象
        delete a;
        delete b;
        return true;
    }
    ......
    if (condition2)
    {
        // return之前必须delete所有new出来的对象
        delete a;
        delete b;
        return true;
    }
    .......
    if (condition3)
    {
        // throw 之前必须delete所有new出来的对象
        delete a;
        delete b;
        throw std::exception("bad condition3");
    }
    
    // 有异常必须catch,在rethrow之前delete所有new出来的对象
    try
    {
          ......
    }
    catch (...)   // 三个点“...”用于此处捕捉所有异常
    {
        delete a;
        delete b;
        throw;    // rethrow
    }
    

      这种方式非常繁琐,也很容易犯错误(error-prone)。有没有一种方法,能管理new出来的对象,并且在return/throw/异常发生的时候自动释放这些对象呢?按照《C++的优秀特性3:构造函数和析构函数》的介绍,有一种借助构造函数和析构函数实现的模版类,可以实现这样的功能。这就是智能指针。比如标准库STL中实现了一种智能指针std::auto_ptr。有了这种智能指针,前面的程序就简单了:

    std::auto_ptr<A> a(new A());
    std::auto_ptr<B> b(new B());
    ......
    a->foo();  // 像普通指针一样使用
    (*b).bar();  // 像普通指针一样使用
    if (condition1) { // return之前无需delete所有new出来的对象 return true; } ...... if (condition2) { // return之前无需delete所有new出来的对象 return true; } ....... if (condition3) { // throw 之前无需delete所有new出来的对象 throw std::exception("bad condition3"); } // 有异常不处理的时候也不用catch和rethrow了

      一个智能指针指向一个对象叫做这个指针持有这个对象,也就是指针是这个对象的持有者。持有者负责在适当的时机释放持有的对象。常用的智能指针有3种:

    1. 单持有者,e.g. std::auto_ptr。单持有者保证每个new出来的对象只有一个持有者,单这种持有关系可以转移,转移之后原持有者不再持有对象。
    2. 局部持有者,e.g. boost::scoped_ptr。类似单持有者,单持有关系无法转移。这决定了这种持有关系不可能脱离当前的作用域(scope),因此得名。
    3. 共享持有者,e.g. boost::shared_ptr。一个对象可以被一个或多个这样的共享持有者持有,持有关系可以转移,转移之后原持有者继续持有该对象,直到原持有者被析构。当一个对象的最后一个持有者析构时,持有的对象被析构。

    在实际中,由于#1(单持有者)的持有关系可以转移,常常是造成麻烦,而应用场景跟#2(局部持有者)又很接近,因此Google C++规范中禁止使用auto_ptr,而建议使用scoped_ptr。共享持有者是很常用的,基本上适用于大多数的简单指针应用的情况。但是,由于shared_ptr是为共享持有关系设计的,因此为了和scoped_ptr区分,在scoped_ptr适用的场景中,不要适用shared_ptr。

      实际中常见的一种错误是,智能指针持有了不该持有的对象。比如:

    std::string name = "ZHANG San";
    std::auto_ptr<std::string> nameHolder(&name);    // 持有了不是new出来的对象,会导致segmentation fault
    

    这其实是一种混用持有关系的问题。这可以简单的归纳为以下几种情况:

    1. 直接定义在栈上或data区的变量,不需要delete的。
    2. new出来的对象(在堆上),但程序员自己delete的。
    3. 不同类型的智能指针持有的对象。

    这几种持有情况互相不能混用,否则会segmentation fault。

     
  • 相关阅读:
    WPF 柱状图显示数据
    WPF 寻找控件模板中的元素
    WPF 寻找数据模板中的元素
    WPF VisualTreeHelper的使用
    WPF依赖项属性不需要包装属性也可以工作
    WPF依赖属性对内存的使用方式
    WPF Binding Path妙用
    WPF Binding Path妙用代码实现
    WPF Binding妙处-既无Path也无Source
    WPF ListView的使用
  • 原文地址:https://www.cnblogs.com/bqzhao/p/3553946.html
Copyright © 2011-2022 走看看