zoukankan      html  css  js  c++  java
  • 【原/转】【boost】智能指针使用规则以及介绍

    智能指针机制跟Objective-C里面的retainCount引用计数有着相同的原理,当某个对象的引用计数为0是执行delete操作,类似于autorelease

    初学者在使用智能指针时,很多情况下可以把它当做标准C++中的T*来理解。比如:

    typedef boost::shared_ptr<CMyLargeClass>  CMyLargeClassPtr;
    
    std::vector<CMyLargeClassPtr> vec;
    
    vec.push_back( CMyLargeClassPtr(new CMyLargeClass("bigString")) );
    

    这里的CMyLargeClassPtr可以用CMyLargeClass *来理解,但是在使用上还是有一些区别的,请看下面:

    先看一个例子:

    许多容器类,包括STL,都需要拷贝操作(例如,我们插入一个存在的元素到list,vector,或者container。)当拷贝操作是非常销毁资源的时候(这些操作时必须的),典型的操作就是使用容器指针。

    std::vector<CMyLargeClass *> vec;

    vec.push_back( new CMyLargeClass("bigString") );

     

    将内存管理的任务抛给调用者,我们能够使用shared_ptr来实现。

    typedef boost::shared_ptr<CMyLargeClass>  CMyLargeClassPtr;

    std::vector<CMyLargeClassPtr> vec;

    vec.push_back( CMyLargeClassPtr(new CMyLargeClass("bigString")) );

    使用智能指针的一些操作会产生错误(突出的事那些不可用的引用计数器,一些对象太容易释放,或者根本释放不掉)。Boost增强了这种安全性,处理了所有潜在存在的危险,所以我们要遵循以下几条规则使我们的代码更加安全。

    下面几条规则是你应该必须遵守的:

    规则一:赋值和保存 —— 对于智能指针来说,赋值是立即创建一个实例,并且保存在那里。现在智能指针拥有一个对象,你不能手动释放它,或者取走它,这将帮助你避免意外地释放了一个对象,但你还在引用它,或者结束一个不可用的引用计数器。

    规则二:_ptr<T> 不是T* —— 恰当地说,不能盲目地将一个T* 和一个智能指针类型T相互转换。意思是:

    ·         当创建一个智能指针的时候需要明确写出 __ptr<T> myPtr(new T)。

    ·         不能将T*赋值给一个智能指针。

    ·         不能写ptr = NULL,应该使用ptr.reset()。

    ·         重新找回原始指针,使用ptr.get(),不必释放这个指针,智能指针会去释放、重置、赋值。使用get()仅仅通过函数指针来获取原始指针。

    ·         不能通过T*指向函数指针来代表一个__ptr<T>,需要明确构造一个智能指针,或者说将一个原始指针的所有权给一个指针指针。(见规则三)

    ·         这是一种特殊的方法来认定这个智能指针拥有的原始指针。不过在Boost:smart pointer programming techniques 举例说明了许多通用的情况。

    规则三:非循环引用 —— 如果有两个对象引用,而他们彼此都通过一个一个引用指针计数器,那么它们不能释放,Boost 提供了weak_ptr来打破这种循环引用(下面介绍)。

    规则四:非临时的 share_ptr —— 不能够造一个临时的share_ptr来指向它们的函数,应该命名一个局部变量来实现。(这可以使处理以外更安全,Boost share_ptr best practices 有详细解说)。

    7、 循环引用

    引用计数器是一种便利的资源管理机制,它有一个基本回收机制。但循环引用不能够自动回收,计算机很难检测到。一个最简单的例子,如下:

    struct CDad;

    struct CChild;

    typedef boost::shared_ptr<CDad>   CDadPtr;

    typedef boost::shared_ptr<CChild>  CChildPtr;

    struct CDad : public CSample

    {

       CChildPtr myBoy;

    };

    struct CChild : public CSample

    {

     CDadPtr myDad;

    };

    // a "thing" that holds a smart pointer to another "thing":

    CDadPtr   parent(new CDadPtr);

    CChildPtr child(new CChildPtr);

    // deliberately create a circular reference:

    parent->myBoy = child;

    child->myDad = dad;

    // resetting one ptr...

    child.reset();

             parent 仍然引用CDad对象,它自己本身又引用CChild。整个情况如下图所示:

    如果我们调用dad.reset(),那么我们两个对象都会失去联系。但这种正确的离开这个引用,共享的指针看上去没有理由去释放那两个对象,我们不能够再访问那两个对象,但那两个对象的确还存在,这是一种非常严重的内存泄露。如果拥有更多的这种对象,那么将由更多的临界资源不能正常释放。

           如果不能解决好共享智能指针的这种操作,这将是一个严重的问题(至少是我们不可接受的)。因此我们需要打破这种循环引用,下面有三种方法:

    A、   当只剩下最后一个引用的时候需要手动打破循环引用释放对象。

    B、   Dad的生存期超过Child的生存期的时候,Child需要一个普通指针指向Dad

    C、  使用boost::weak_ptr打破这种循环引用。

    方法AB并不是一个完美的解决方案,但是可以在不使用weak_ptr的情况下让我们使用智能指针

    ============================================================

    更多详细内容请看博客http://blog.csdn.net/dongguan131/article/details/6683843

    与shared_ptr相类似的是scoped_ptr、auto_ptr。boost::scoped_ptrstd::auto_ptr非常类似,是一个简单的智能指针,二者都能够保证在离开作用域后对象被释放。

    #include <string>
    #include <iostream>
    #include <boost/scoped_ptr.hpp>
    
    class implementation
    {
    public:
        ~implementation() { std::cout <<"destroying implementation
    "; }
        void do_something() { std::cout << "did something
    "; }
    };
    
    void test()
    {
        boost::scoped_ptr<implementation> impl(new implementation());
        impl->do_something();
    }
    
    void main()
    {
        std::cout<<"Test Begin ... 
    ";
        test();
        std::cout<<"Test End.
    ";
    }
    Scoped_ptr

    该代码的输出结果是:

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

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

    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类。
  • 相关阅读:
    Ubuntu 16.04出现:Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/...
    Ubuntu 16.04出现:Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/...
    Ubuntu 16.04出现:Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/...
    Ubuntu 16.04出现:Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/...
    eclipse c++ 经常使用快捷键
    eclipse c++ 经常使用快捷键
    eclipse c++ 经常使用快捷键
    eclipse c++ 经常使用快捷键
    Java集合的遍历方式
    阿里五年老将的创业故事
  • 原文地址:https://www.cnblogs.com/wengzilin/p/3554685.html
Copyright © 2011-2022 走看看