Boost学习1-智能指针_C/C++_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > C/C++ > Boost学习1-智能指针

Boost学习1-智能指针

 2010/12/8 10:17:00  dongtianzhe  http://silverbullet1985.javaeye.com  我要评论(0)
  • 摘要:智能指针的原理是基于RAII:申请资源即初始化。一个动态分配的对象由智能指针管理,在任何情况下动态分配的内存都能得到正确释放,从而将开发人员从这项任务中解放了出来。作用域指针:boost::scoped_ptr,一个作用域指针独占一个动态分配的对象,不能将一个作用域指针所包含的对象传递给另一个作用域指针。一经初始化,智能指针boost::scoped_ptr所包含的对象,可以通过类似于普通指针的接口来访问。这是因为重载了相关的操作符operator*(),operator->(
  • 标签:学习 智能指针 指针
智能指针的原理是基于RAII:申请资源即初始化。一个动态分配的对象由智能指针管理,在任何情况下动态分配的内存都能得到正确释放,从而将开发人员从这项任务中解放了出来。

作用域指针:

boost::scoped_ptr,一个作用域指针独占一个动态分配的对象,不能将一个作用域指针所包含的对象传递给另一个作用域指针。

一经初始化,智能指针 boost::scoped_ptr 所包含的对象,可以通过类似于普通指针的接口来访问。 这是因为重载了相关的caozuofu.html" target="_blank">操作符 operator*(),operator->() 和 operator bool() 。 此外,还有 get() 和 reset() 方法。 前者返回所含对象的地址,后者用一个新的对象来重新初始化智能指针。 在这种情况下,新创建的对象赋值之前会先自动释放所包含的对象。

boost::scoped_ptr 的析构函数中使用 delete 操作符来释放所包含的对象。 这对 boost::scoped_ptr 所包含的类型加上了一条重要的限制: boost::scoped_ptr 不能用动态分配的数组来做初始化,因为这需要调用 delete[] 来释放。

作用域数组:

boost::scoped_array,作用域数组的使用方式与作用域指针相似。 关键不同在于,作用域数组的析构函数使用 delete[] 操作符来释放所包含的对象。 因为该操作符只能用于数组对象,所以作用域数组必须通过动态分配的数组来初始化。

共享指针:

boost::shared_ptr,智能指针 boost::shared_ptr 基本上类似于 boost::scoped_ptr。 关键不同之处在于 boost::shared_ptr 不一定要独占一个对象。 它可以和其他 boost::shared_ptr 类型的智能指针共享所有权。 在这种情况下,当引用对象的最后一个智能指针销毁后,对象才会被释放。

shared_ptr相对于标准库中auto_ptr来说,有一项重要的改进,可以再容器中存放shared_ptr对象。但是你不能在标准容器中存储 std::auto_ptr,因为它们在拷贝的时候传递了所有权。
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <vector>

using namespace std;

int main()
{
    typedef vector< boost::shared_ptr<int> > ShareV;
    ShareV v1;
    v1.push_back(boost::shared_ptr<int>(new int(1)));
    v1.push_back(boost::shared_ptr<int>(new int(2)));

    for (ShareV::const_iterator it = v1.begin(); it != v1.end(); ++it) {
        cout << **it << endl;
    }
    return 0;
}


共享数组:

boost::shared_array,共享数组的行为类似于共享指针。 关键不同在于共享数组在析构时,默认使用 delete[] 操作符来释放所含的对象。 因为这个操作符只能用于数组对象,共享数组必须通过动态分配的数组的地址来初始化。

弱指针:

boost::weak_ptr,弱指针只有在配合共享指针一起使用时才有意义。一个强引用当被引用的对象活着的话,这个引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。boost::share_ptr就是强引用。

相对而言,弱引用当引用的对象活着的时候不一定存在。仅仅是当它存在的时候的一个引用。弱引用并不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。

weak_ptr的lock函数返回一个引向weak_ptr所观察的资源的 shared_ptr ,如果可以的话。如果没有这样指针(即 weak_ptr 引向的是空指针),shared_ptr 也将引向空指针。否则,shared_ptr所引向的资源的引用计数将正常地递增。
#include <iostream>
#include <boost/weak_ptr.hpp>
#include <pthread.h>

using namespace std;


void * reset(void *arg)
{
    boost::shared_ptr<int> *sh = static_cast<boost::shared_ptr<int> *>(arg);
    sleep(1);
    sh->reset(); // 重置
}

void * print(void *arg)
{
    boost::weak_ptr<int> *w = static_cast<boost::weak_ptr<int> *>(arg);
    boost::shared_ptr<int> sh = w->lock(); // 如果已经被重置了,则返回空;如果没有被重置,则返回一个shared_ptr并且引用计数加1,这样永远都不会出错了
    if (sh)
        cout << *sh << endl;
}


int main()
{
    boost::shared_ptr<int> i(new int(4));
    boost::weak_ptr<int> w(i);

    int err = 0;
    pthread_t tid1, tid2;
    
    err = pthread_create(&tid1, NULL, reset, &i);
    if(err != 0) {
        cerr << "can't create pthread 1" << endl;
        exit(1);
    }

    err = pthread_create(&tid2, NULL, print, &w);
    if( err != 0 ) {
        cerr << "can't create pthread 2" << endl;
        exit(1);
    }

    err = pthread_join(tid1, NULL);
    if (err != 0) {
        cerr << "can't join thread 1" << endl;
        exit(1);
    }

    err = pthread_join(tid2, NULL);
    if( err != 0 ) {
        cerr << "can't join thread 2" << endl;
        exit(1);
    }

    return 0;
}


那么weak_ptr有什么用呢?一个最大的用处就是管理循环引用的对象。下面我们看一个例子
#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>

class parent;
class children;

typedef boost::shared_ptr<parent> parent_ptr;
typedef boost::shared_ptr<children> children_ptr;

class parent
{
public:
    ~parent() { std::cout <<"destroying parent\n"; }

public:
    children_ptr children;
};

class children
{
public:
    ~children() { std::cout <<"destroying children\n"; }

public:
    parent_ptr parent;
};


void test()
{
    parent_ptr father(new parent());
    children_ptr son(new children);

    father->children = son;
    son->parent = father;
}

int main()
{
    std::cout<<"begin test...\n";
    test();
    std::cout<<"end test.\n";
    return 0;
}


注意看test函数,即使退出了test函数后,由于parent和children对象互相引用,它们的引用计数都是1,不能自动释放,并且此时这两个对象再无法访问到。这就引起了c++中那臭名昭著的内存泄漏。我们看一下内存布局图:



当test结束的时候,局部变量father和son会析构,father析构的时候,由于他持有对象的引用计数为2,所以仅仅将引用计数减1,son也一样,test函数结束后,内存布局如下图:



我们可以通过boost::weak_ptr来打破循环引用

由于弱引用不更改引用计数,类似普通指针,只要把循环引用的一方使用弱引用,即可解除循环引用。对于上面的那个例子来说,只要把children的定义改为如下方式,即可解除循环引用:

class children
{
public:
    ~children() { std::cout <<"destroying children\n"; }

public:
    boost::weak_ptr<parent> parent;
};


最后值得一提的是,虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案,如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。因此,不要认为只要使用了智能指针便能杜绝内存泄漏。毕竟,对于C++来说,由于没有垃圾回收机制,内存泄漏对每一个程序员来说都是一个非常头痛的问题。

介入式指针:

大体上,介入式指针的工作方式和共享指针完全一样。 boost::shared_ptr 在内部记录着引用到某个对象的共享指针的数量,可是对介入式指针来说,程序员就得自己来做记录。 对于框架对象来说这就特别有用,因为它们记录着自身被引用的次数。

指针容器:
在你见过 Boost C++ 库的各种智能指针之后,应该能够编写安全的代码,来使用动态分配的对象和数组。多数时候,这些对象要存储在容器里——如上所述——使用 boost::shared_ptr 和 boost::shared_array 这就相当简单了。
然而因为某些原因,实际情况中并不这么用。 第一,反复声明 boost::shared_ptr 需要更多的输入。 其次,将 boost::shared_ptr 拷进,拷出,或者在容器内部做拷贝,需要频繁的增加或者减少内部引用计数,这肯定效率不高。 由于这些原因,Boost C++ 库提供了 指针容器 专门用来管理动态分配的对象。
boost::ptr_vector 独占它所包含的对象,因而容器之外的共享指针不能共享所有权,这跟 std::vector<boost::shared_ptr<int> > 相反。


发表评论
用户名: 匿名