智能指针的原理是基于RAII:申请资源即初始化。一个动态分配的对象由智能指针管理,在任何情况下动态分配的内存都能得到正确释放,从而将开发人员从这项任务中解放了出来。
作用域指针:
boost::scoped_ptr,一个作用域指针独占一个动态分配的对象,不能将一个作用域指针所包含的对象传递给另一个作用域指针。
一经初始化,智能指针 boost::scoped_ptr 所包含的对象,可以通过类似于普通指针的接口来访问。 这是因为重载了相关的操作符 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> > 相反。
- 大小: 6.8 KB
- 大小: 5.4 KB
分享到:
相关推荐
An+Introduction+to+Boost.chm Beyond+the+C+++Standard+Library(中文版).pdf Boost And STL学习资料 .chm boost graph library.pdf Boost程序库完全开发指南.pdf ...智能指针的标准之争 Boost_vs_Loki.pdf
AnIntroduction+to+Boost.chm Beyond+the+C+++Standard+Library(中文版).pdf Beyond_STL An Introduction to Boost中文.chm Boost And STL学习资料 .chm boost graph library....智能指针的标准之争 Boost_vs_Loki.pdf
最近项目中使用boost库的智能指针,感觉智能指针还是蛮强大的,在此贴出自己学习过程中编写的测试代码,以供其他想了解boost智能指针的朋友参考,有讲得不正确之处欢迎指出讨论。当然,使用boost智能指针首先要编译...
Boost库1.55.0版本的源码,可用以学习智能指针,stl标准容器等...,是刨析源码加以学习的最好资料。
boost向来有准标准库之称,很多新特性例如智能指针等都是先在boost中实现,后来被吸收到标准库之中。而且boost实现了日志、算法、日期、地理、数学、线程协程等各种实用工具,所以如果要学习C++,boost是必须要学习...
5个智能指针,学习智能指针必读,一份不错的参考是来自CUJ的文章: Smart Pointers in Boost,哦,这篇文章可以查到,CUJ是提供在线浏览的。中文版见笔者在《Dr.Dobb's Journal软件研发杂志》第7辑上的译文。 ...
boost库,chm版,中文,有示例,有...含智能指针、函数对象、事件处理、字符串处理、多线程、异步输入输出、进程间通讯、文件系统、日期与时间、序列化、词法分析器、容器、数据结构、错误处理、类型转换操作符; 解压
条款17:以独立语句将newed对象置入智能指针 store newed objects in smart pointers in standalone statements. 4. 设计与声明 designs and declarations 条款18:让接口容易被正确使用,不易被误用 make ...
5个智能指针学习智能指针必读份不错参考是来自CUJ文章: Smart Poers in Boost,哦这篇文章可以查到CUJ是提供在线浏览中文版见笔者在Dr.Dobb\'s Journal软件 Software研发杂志第7辑上译文 Boost总体来说是实用价值很...