C++ 11:std::auto_ptr介绍,以及boost::scoped_ptr

本文根据众多互联网博客内容整理后形成,引用内容的版权归原始作者所有,仅限于学习研究使用。

1 std::auto_ptr

1.1 auto_ptr介绍

       智能指针能保证,无论在何种情况下,只要自己被摧毁,就一定连带释放其所指资源。auto_ptr是这样的一种指针:它是“它所指向的对象”的拥有者。auto_ptr要求一个对象只能有一个拥有者,严禁一物二主。不再需要delete,也不再需要catch了。不用担心忘掉delete动作,担心程序异常结束时内存遗失或者资源遗失,只要有智能指针auto_ptr.

       c++标准库提供的auto_ptr一种帮助程序员防止“被抛出异常时发生资源泄漏”的智能指针。

auto_ptr在头文件#include<memory>

  auto_ptr没有所有的指针算术(包括++)运算。auto_ptr不允许你使用一般指针惯用的赋值初始化方式。 例如:

    std::auto_ptr<ClassA> ptr1(new ClassA); //对的

    std::auto_ptr<ClassA> ptr2 =new ClassA;  //错的

2.auto_ptr拥有权的转移

  std::auto_ptr<ClassA> ptr1(new ClassA);//ptr1拥有了那个new出来的对象。

  std::auto_ptr<ClassA> ptr2(ptr1);  //ptr2拥有了那个new出来的对象,ptr1=NULL不再拥有它,即拥有权的转移;

                   //这样对象就只会在ptr2被摧毁的时候被delete一次。

下面的赋值操作和上面的差不多:

  std::auto_ptr<ClassA> ptr1(new ClassA);

  std::auto_ptr<ClassA> ptr2;

  ptr2=ptr1; //拥有权从ptr1转移到ptr2

如果ptr2被赋值之前正拥有另一个对象,赋值时将会delete掉那个对象。

  std::auto_ptr<ClassA> ptr1(new ClassA);

  std::auto_ptr<ClassA> ptr2(new ClassA);

  ptr2=ptr1; //拥有权从ptr1转移到ptr2,ptr2的原来的对象被delete掉。

  拥有权的转移,实质上并非只是被简单拷贝而已。只要发生了拥有权转移,先前的拥有者就失去了拥有权,结果拥有者一旦交出拥有权,就两手空空,只剩下一个null指针在手了。

  只有auto_ptr可以拿来当做另一个auto_ptr的初值,普通指针是不行的。例如:

    std::auto_ptr<ClassA> ptr ;//可以定义空的auto_ptr指针,对于智能指针,因为构造函数有默认值0

    ptr =new ClassA;    //错误,

    ptr =std::auto_ptr<ClassA> (new ClassA);//正确被auto_ptr对象或者auto_ptr指针赋值,

  3)某函数是数据的终点,auto_ptr以传值方式被当作一个参数传递给某函数时。被调用端的参数获得了这个auto_ptr的拥有权,如果函数不再将它传递出去,它所指的对象就会在函数退出时被删除。如:void sink(std::auto_ptr<ClassA> );

  4) 某函数是数据的起点。

       std::auto_ptr<ClassA> f()
    {
      std::auto_ptr<ClassA> ptr(new ClassA);
      //........
      return ptr;
    }

    void g()
    {
      std::auto_ptr<ClassA> p;
      for (int i =0; i <10; i++) {
        p=f();  //p获得f()返回对象的拥有权。每当f()被调用时,它都new一个对象,然后把该对象连同其拥有权一起返回给调用端。
        //一旦循环再次执行这个赋值动作,p原先拥有的对象被删除。当离开g()时,p也被摧毁。
      }
        }

     auto_ptr的语义本身就包含了拥有权,所以如果你无意转交你的拥有权,就不要在参数列表值中使用auto_ptr,也不要以它作为返回值。

   下面的例子本来是想将auto_ptr所指对象的值打印出来,却可能引发了错误:

   //a bad example
#include <iostream>
#include <memory>
using namespace std;

template<class T>
void bad_print(std::auto_ptr<T> p)
{
  if (NULL ==p.get())
    cout <<"NULL";
  else
    cout << *p; 
}

int main()
{
  auto_ptr<int> p(new int);
  *p =42;
  bad_print(p); //bad_print调用结束后,p所指向的对象被删除了。
  *p =18;//执行期错误,用g++编译时出现Segmentation fault (core dumped)(段错误)
  return 0;
}
 

//a bad example
#include <iostream>
#include <memory>
using namespace std;

template<class T>
void bad_print(std::auto_ptr<T> p)
{
  if (NULL ==p.get())
    cout <<"NULL";
  else
    cout << *p; 
}

int main()
{
  const auto_ptr<int> p(new int);
  *p =42;
  bad_print(p);  //编译期错误,无法变更constant reference的拥有权
  *p =18;    //ok
  return 0;
}
 

关键字const并非意味你不能更改auto_ptr所拥有的对象,而是意味着你不能更改auto_ptr的拥有权。例如:

  std::auto_ptr<int> f()

  {

    const std::auto_ptr<int> p(new int);

    std::auto_ptr<int>  q(new int);

    *p =42;//ok,改变值

    bad_print(p);//编译期错误,不能变更拥有权

    *p =*q;  //ok;

    p =q;  //编译期错误

    return p;  //编译期错误

  }

  如果使用const auto_ptr作为参数,对新对象的任何赋值操作都将导致编译期错误。就其常数特性而言,const auto_ptr比较类似常数指针(T* const p),

  而非指向常数的指针(const T* p),尽管语法上更像后者。

1.3 auto_ptr的正确运用:

  1)auto_ptr之间不能共享拥有权,即不要让两个auto_ptr指向同一个对象。

    一个auto_ptr千万不能指向另一个auto_ptr(或其它对象)所拥有的对象,尽管语法上没问题,很多情况下可能也不会发生错误。但是,当一个指针删除该对象后,另一个指针突然间指向一个已被摧毁的对象,那么,如果再使用那个指针进行读写操作,就会引发一场灾难。

  2)并不存在针对array而设计的auto_ptrs

  auto_ptr不能指向array,因为auto_ptr是透过delete而非delete[ ]来释放其所拥有的对象。注意,c++标准程序库并未针对array而设计的auto_ptr。标准程序库另提供了数个容器类别,用来管理数据群。

  3)auto_ptrs绝非一个“四海通用”的智能型指针

   并非任何适用智能指针的地方都适用auto_ptr,特别请注意的是,它不是引用计数型指针——这种指针保证,如果有一组智能型指针指向同一个对象,那么当且仅当最后一个智能型指针被销毁时,该对象才会被销毁。

  4)auto_ptrs不满足STL容器对其他元素的要求

   auto_ptr并不满足STL标准容器对于元素的最基本要求,因为在拷贝和赋值动作之后,原本的auto_ptr和新产生的auto_ptr并不相等。是的,拷贝和赋值之后,原本的auto_ptr会交出拥有权,而不是拷贝给新的auto_ptr。因此绝对不要将auto_ptr作为标准容器的元素。

        5)不要使用auto_ptr对象保存指向静态分配对象的指针,否则,当auto_ptr对象本身被撤销的时候,它将试图删除指向非动态分配对象的指针,导致未定义的行为。

       使用一个 std::auto_ptr 的限制很多,还不能用来管理堆内存数组,如此多的限制就很容易导致问题。所以说它是一个带有缺陷的设计,是一个“弃儿”。由于 std::auto_ptr 引发了诸多问题,一些设计并不是非常符合 C++ 编程思想,所以C++引入了下面 boost 库的智能指针,boost 智能指针可以解决如上问题。

1.4 总结:

std::auto_ptr 可用来管理单个对象的内存,但是,请注意如下几点:

1)    首先auto_ptr智能指针是个封装好的类;

2)    尽量不要使用“operator=”。如果使用了,请不要再使用先前对象;

3)    std::auto_ptr 最好不要当成参数传递(读者可以自行写代码确定为什么不能);

4)    采用栈上的指针去管理堆上的内容,所以auto_ptr所管理的对象必须是new出来的,也不能是malloc出来的。(原因:在auto_ptr的实现机制中,采用的是delete 掉一个指针,该delete一方面是调用了指针所指对象的析构函数(这也是为什么采用智能指针,new了一个对象,但是不用delete的原因),另一方面释放了堆空间的内存。)

 

2 boost::scoped_ptr

2.1 介绍

boost::scoped_ptr和std::auto_ptr非常类似,是一个简单的智能指针,它能够保证在离开作用域后对象被自动释放。下列代码演示了该指针的基本应用:

#include <string>
#include <iostream>
#include <boost/scoped_ptr.hpp>

class implementation
{
public:
    ~implementation() { std::cout <<"destroying implementation\n"; }
    void do_something() { std::cout << "did something\n"; }
};

void test()
{
    boost::scoped_ptr<implementation> impl(new implementation());
    impl->do_something();
}

void main()
{
    std::cout<<"Test Begin ... \n";
    test();
    std::cout<<"Test End.\n";
}

 

该代码的输出结果是:

Test Begin ...
did something
destroying implementation
Test End.

可以看到:当implementation类离其开impl作用域的时候,会被自动删除,这样就会避免由于忘记手动调用delete而造成内存泄漏了。

2.2 boost::scoped_ptr特点:

      boost::scoped_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件#include<boost/smart_ptr.hpp> 便可以使用。scoped_ptr 跟 auto_ptr 一样,可以方便的管理单个堆内存对象,特别的是,scoped_ptr 独享所有权,避免了auto_ptr恼人的几个问题。

      scoped_ptr是一种简单粗暴的设计,它本质就是防拷贝,避免出现管理权的转移。这是它的最大特点,所以他的拷贝构造函数和赋值运算符重载函数都只是声明而不定义,而且为了防止有的人在类外定义,所以将函数声明为protected。但这也是它最大的问题所在,就是不能赋值拷贝,也就是说功能不全。但是这种设计比较高效、简洁。没有 release() 函数,不会导致先前的内存泄露问题。

      boost::scoped_ptr的实现和std::auto_ptr非常类似,都是利用了一个栈上的对象去管理一个堆上的对象,从而使得堆上的对象随着栈上的对象销毁时自动删除。不同的是,boost::scoped_ptr有着更严格的使用限制——不能拷贝。这就意味着:boost::scoped_ptr指针是不能转换其所有权的

  1. 不能转换所有权
    boost::scoped_ptr所管理的对象生命周期仅仅局限于一个区间(该指针所在的"{}"之间),无法传到区间之外,这就意味着boost::scoped_ptr对象是不能作为函数的返回值的(std::auto_ptr可以)。

  2. 不能共享所有权
    这点和std::auto_ptr类似。这个特点一方面使得该指针简单易用。另一方面也造成了功能的薄弱——不能用于stl的容器中。

  3. 不能用于管理数组对象
    由于boost::scoped_ptr是通过delete来删除所管理对象的,而数组对象必须通过deletep[]来删除,因此boost::scoped_ptr是不能管理数组对象的,如果要管理数组对象需要使用boost::scoped_array类。

2.3 boost::scoped_ptr的常用操作:

可以简化为如下形式:

namespace boost {

    template<typename T> class scoped_ptr : noncopyable {
    public:
        explicit scoped_ptr(T* p = 0); 
        ~scoped_ptr(); 

        void reset(T* p = 0); 

        T& operator*() const; 
        T* operator->() const; 
        T* get() const; 

        void swap(scoped_ptr& b); 
    };

    template<typename T> 
    void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); 
}

 

它的常用操作如下:

成员函数

功能

operator*()

以引用的形式访问所管理的对象的成员

operator->()

以指针的形式访问所管理的对象的成员

reset()

释放所管理的对象,管理另外一个对象

swap(scoped_ptr& b)

交换两个boost::scoped_ptr管理的对象

 

下列测试代码演示了这些功能函数的基本使用方法。

#include <string>
#include <iostream>

#include <boost/scoped_ptr.hpp>
#include <boost/scoped_array.hpp>

#include <boost/config.hpp>
#include <boost/detail/lightweight_test.hpp>

void test()
{
    // test scoped_ptr with a built-in type
    long * lp = new long;
    boost::scoped_ptr<long> sp ( lp );
    BOOST_TEST( sp.get() == lp );
    BOOST_TEST( lp == sp.get() );
    BOOST_TEST( &*sp == lp );

    *sp = 1234568901L;
    BOOST_TEST( *sp == 1234568901L );
    BOOST_TEST( *lp == 1234568901L );

    long * lp2 = new long;
    boost::scoped_ptr<long> sp2 ( lp2 );

    sp.swap(sp2);
    BOOST_TEST( sp.get() == lp2 );
    BOOST_TEST( sp2.get() == lp );

    sp.reset(NULL);
    BOOST_TEST( sp.get() == NULL );

}

void main()
{
    test();
}

 

2.4 scoped_ptr使用特点总结:

      1)与auto_ptr类似,采用栈上的指针去管理堆上的内容,从而使得堆上的对象随着栈上对象销毁时自动删除;

      2)scoped_ptr有着更严格的使用限制——不能拷贝,这也意味着scoped_ptr不能转换其所有权,所以它管理的对象不能作为函数的返回值,对象生命周期仅仅局限于一定区间(该指针所在的{}区间,而std::auto_ptr可以);

      3)由于防拷贝的特性,使其管理的对象不能共享所有权,这与std::auto_ptr类似,这一特点使该指针简单易用,但也造成了功能的薄弱。

 

3 boost::scoped_ptr和std::auto_ptr的选取:

boost::scoped_ptr和std::auto_ptr的功能和操作都非常类似,如何在他们之间选取取决于是否需要转移所管理的对象的所有权(如是否需要作为函数的返回值)。如果没有这个需要的话,大可以使用boost::scoped_ptr,让编译器来进行更严格的检查,来发现一些不正确的赋值操作。

 

参考:

https://www.cnblogs.com/TianFang/archive/2008/09/15/1291050.html

https://www.cnblogs.com/ckings/p/3663028.html

https://www.cnblogs.com/33debug/p/6832726.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值