boost::smart_ptr学习笔记

scoped_ptr

scoped_ptr指针对象在其析构时或者显示调用reset方法时会确保其所指向的对象(使用new表达式创建的)会被删除。scoped_ptr指针不存在shared-ownership或transfer-of-ownership的问题,它指向对象的ownership是由其scope决定的;因为scoped_ptr指针的简单,它的效率几乎了原始指针类型一样,并且也没有额外的存储消耗。

scoped_ptr不能用于C++标准库中的容器类;并不适用用数组指针。

shared_ptr

shared_ptr指针对象使用内部计数器管理其所指向的动态分配对象,并保证当最后一个指向该动态分配对象的shared_ptr指针被销毁时(计数器为0)该动态分配对象会被删除。

shared_ptr可拷贝构造、可赋值,并可被C++标准库中的容器类使用。shared_ptr不是多线程安全的

weak_ptr

weak_ptr是一种可构造、可赋值以不增加引用计数来管理shared_ptr的指针,它可以方便的转回到shared_ptr指针,使用weak_ptr.lock函数就可以得到一个shared_ptr的指针,如果该指针已经被其它地方释放,它则返回一个空的shared_ptr,也可以使用weak_ptr.expired()来判断一个指针是否被释放。

weak_ptr可拷贝构造、可赋值,并可被C++标准库中的容器类使用。weak_ptr不仅可以解决多线程访问带来的安全问题,而且还可以解决循环引用问题,示例见后。

shared_ptr使用陷阱

1. shared_ptr多次引用同一数据

void shared_ptr_test1() {
int* pInt
= new int[100];

boost
::shared_ptr<int> sp1(pInt);
printf("sp1 use_count:%d\n", sp1.use_count());

// 一些其它代码之后…
boost
::shared_ptr<int> sp2(pInt);
printf("sp2 use_count:%d\n", sp2.use_count());
}
这种情况在实际中是很容易发生的,结果也是非常致命的,它会导致两次释放同一块内存,而破坏堆;正确得做法是使用sp1去初始化sp2。

运行输出为:

sp1 use_count:1
sp2 use_count:1
*** glibc detected *** double free or corruption (!prev): 0x08ea5008 ***

2. shared_ptr循环引用导致内存泄露

  class parent;
class child;

typedef boost::shared_ptr
<parent> parent_ptr;
typedef boost::shared_ptr
<child> child_ptr;

class parent
{
public:
~parent() {
cout
<< "~parent() is called" << endl;
}

public:
child_ptr children;
};

class child
{
public:
~child() {
cout
<< "~child() is called" << endl;
}

public:
parent_ptr parent;
};


int main() {
parent_ptr father(
new parent());
child_ptr son(
new child());

//父子互相引用
father->children = son;
son
->parent = father;
printf(
"father use_count:%d, son use_count:%d\n", father.use_count(), son.use_count());

return 0;
}

运行输出为:

father use_count:2, son use_count:2

在程序退出前,father的引用计数为2,son的计数也为2;退出时,shared_ptr所作操作就是简单的将计数减1,如果为0则释放,显然这个情况下,引用计数不为0,因为father和son指向的动态分配对象还有shared_ptr指向对方对象,于是造成father和son所指向的内存得不到释放,导致内存泄露。

使用weak_ptr即可解决这个问题,只要修改children类代码修改如下,即可打破循环引用:

class child
{
public:
~child() {
cout
<< "~child() is called" << endl;
}

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

运行输出为:

father use_count:1, son use_count:2
~parent() is called
~child() is called


3.使用shared_ptr包装this指针带来的问题

shared_ptr_this_err.cpp
  class tester {
public:
~tester() {
std::cout
<< "~tester() is called\n";
}

boost::shared_ptr
<tester> sget() {
return boost::shared_ptr<tester>(this);
}
};

int main() {
tester t;
boost::shared_ptr
<tester> sp = t.sget();
return 0;
}
运行输出为:

~tester() is called
~tester() is called

也导致了两次释放t对象破坏堆栈,一次是出栈时对象析构,一次就是shared_ptr析构。若有这种需要,可以使用下面方法:

      class tester : public boost::enable_shared_from_this<tester> {
public:
~tester() {
std::cout
<< "~tester() is called\n";
}

boost::shared_ptr
<tester> sget() {
return shared_from_this();
}
};

int main() {
boost::shared_ptr
<tester> sp(new tester);
boost::shared_ptr
<tester> sp2 = sp->sget();
return 0;
}

4.在多线程程序中使用shared_ptr应注意的问题

shared_ptr_thread1.cpp
class tester {
public:
tester() {}
~tester() {}
};

void fun(boost::shared_ptr<tester> sp) {
boost::shared_ptr
<tester> tmp = sp;
}

int main() {
boost::shared_ptr
<tester> sp1(new tester);

boost::thread t1(boost::bind(
&fun, sp1));
boost::thread t2(boost::bind(
&fun, sp1));

t1.join();
t2.join();
return 0;
}

这个代码带来的问题很显然,由于多线程同是访问智能指针,并将其赋值到其它同类智能指针时,很可能发生两个线程同时在操作引用计数(但并不一定绝对发生),而导致计数失败或无效等情况,从而导致程序崩溃,如若不知根源,就无法查找这个bug,那就只能向上帝祈祷程序能正常运行。

可能一般情况下并不会写出上面这样的代码,但是下面这种代码与上面的代码同样,如下:

shared_ptr_thread2.cpp
class tester {
public:
tester() {}
~tester() {}

boost::shared_ptr
<int> m_spData;
};

tester gObject;

void fun(void) {
boost::shared_ptr
<int> tmp = gObject.m_spData;
}

int main() {
boost::thread t1(
&fun);
boost::thread t2(
&fun);
t1.join();
t2.join();
return 0;
}

情况是一样的。要解决这类问题的办法也很简单,使用boost.weak_ptr就可以很方便解决这个问题。第一种情况修改代码如下:

shared_ptr_thread.cpp
class tester {
public:
~tester() {
std::cout
<< "~tester() is called\n";
}
};

void fun(boost::weak_ptr<tester> wp) {
boost::shared_ptr
<tester> sp = wp.lock();

if (sp) {
//在这里可以安全的使用sp指针
} else {
std::cout
<< "shared_ptr is already released!" << std::endl;
}
}

int main() {
boost::shared_ptr
<tester> sp1(new tester);
boost::weak_ptr
<tester> wp(sp1);

boost::thread t1(boost::bind(
&fun, wp));
boost::thread t2(boost::bind(
&fun, wp));
t1.join();
t2.join();

return 0;
}

使用weak_ptr.lock()就可以得到一个shared_ptr的指针,如果该指针已经被其它地方释放,它则返回一个空的shared_ptr,也可以使用weak_ptr.expired()来判断一个指针是否被释放。

参考资料:

Boost.shared_ptr必须注意的一些地方

posted @ 2011-03-01 13:56  edwardlost  阅读(1347)  评论(0编辑  收藏  举报