内存管理是一个比较繁琐的问题,C++中有两个实现方案: 垃圾回收机制智能指针。垃圾回收机制因为性能等原因不被C++的大佬们推崇, 而智能指针被认为是解决C++内存问题的最优方案。

1. 智能指针定义

一个智能指针就是一个C++的对象, 这对象的行为像一个指针,但是它却可以在其不需要的时候自动删除。注意这个“其不需要的时候”, 这可不是一个精确的定义。这个不需要的时候可以指好多方面:局部变量退出函数作用域、类的对象被析构……。所以boost定义了多个不同的智能指针来管理不同的场景。

shared_ptr<T> 内部维护一个引用计数器来判断此指针是不是需要被释放。是boost中最常用的智能指针了。
scoped_ptr<t> 当这个指针的作用域消失之后自动释放
intrusive_ptr<T> 也维护一个引用计数器,比shared_ptr有更好的性能。但是要求T自己提供这个计数器。
weak_ptr<T> 弱指针,要和shared_ptr 结合使用
shared_array<T> 和shared_ptr相似,但是访问的是数组
scoped_array<T> 和scoped_ptr相似,但是访问的是数组

2. Boost::scoped_ptr<T>

2.1 定义

scoped_ptr 是boost中最简单的智能指针。scoped_ptr的目的也是很简单, 当一个指针离开其作用域时候,释放相关资源。特别注意的一定就是scoped_ptr 不能共享指针的所有权不能转移所有权。也就是说这个内存地址就只能给的声明的变量用,不能给其他使用。

2.2 特点

(1)scoped_ptr的效率和空间的消耗内置的指针差不多

(2)scoped_ptr不能用于管理数组对象,不能指向一块能够动态增长的内存区域(用scoped_array代替)

(3)scoped_ptr不能转换所有权,因此不能作为函数的返回值

(4)scoped_ptr不能共享所有权,因此不能用于stl的容器中(用shared_ptr代替)

2.3 使用原则

(1)在可能有异常抛出的作用域使用指针

(2)函数里有几条控制路径

(3)动态分配对象的生存期应被限制于特定的作用域内

(4)异常安全非常重要时(总应如此!)

2.4 例子

 class test
{
public:
void print()
{
cout << "test print now" <<endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
boost::scoped_ptr<test> x(new test);
x->print();
return ;
}

3.Boost::shared_ptr<T>

3.1 定义

  boost::shared_ptr是可以共享所有权的智能指针.

3.2 特点

(1)boost::shared_ptr在内部维护一个引用计数器, 当有一个指针指向这块内存区域引用计数+1, 反之-1, 如果没有任何指针指向这块区域, 引用计数器为0,释放内存区域

(2)boost::shared_ptr可以共享和转移所有权

(3)boost::shared_ptr可以被标准库的容器所使用

(4)boost::shared_ptr是线程安全的,这点在多线程程序中也非常重要

(5)boost::shared_ptr不能指向一块动态增长的内存(用share_array代替)

3.3 使用原则

(1)避免对shared_ptr所管理的对象的直接内存管理操作,以免造成该对象的重释放

(2)shared_ptr并不能对循环引用的对象内存自动管理(这点是其它各种引用计数管理内存方式的通病)

(3)不要构造一个临时的shared_ptr作为函数的参数

3.4 例子

 int _tmain(int argc, _TCHAR* argv[])
{
boost::shared_ptr<test> ptr_1(new test);
ptr_1->print();//引用计数为1
boost::shared_ptr<test> ptr_2 = ptr_1;
ptr_2->print();//引用计数为2
ptr_1->print();// 引用计数还是为2
return ;
}

4. Boost::intrusive_ptr<T>

4.1 定义

  boost::intrusive_ptr一种“侵入式”的引用计数指针,它实际并不提供引用计数功能,而是要求被存储的对象自己实现引用计数功能,并提供intrusive_ptr_add_refintrusive_ptr_release函数接口供boost::intrusive_ptr调用。

4.2 使用原则

(1)你需要把 this当作智能指针来使用

(2)已有代码使用或提供了插入式的引用计数

(3)智能指针的大小必须与裸指针的大小相等

4.3 例子

  下面通过一个具体的例子来说明boost::intrusive_ptr的用法,首先实现一个基类intrusive_ptr_base,定义intrusive_ptr_add_ref和intrusive_ptr_release函数来提供引用计数功能。

 /**
* intrusive_ptr_base基类,提供intrusive_ptr_add_ref()和intrusive_ptr_release()函数来提供引用计数功能;
* 使用boost::intrusive_ptr指针存储的用户类类型必须继承自intrusive_ptr_base基类。
*/
#include <ostream>
#include <boost/checked_delete.hpp>
#include <boost/detail/atomic_count.hpp> template<class T>
class intrusive_ptr_base {
public:
/**
* 缺省构造函数
*/
intrusive_ptr_base(): ref_count() {
std::cout << " Default constructor " << std::endl;
} /**
* 不允许拷贝构造,只能使用intrusive_ptr来构造另一个intrusive_ptr
*/
intrusive_ptr_base(intrusive_ptr_base<T> const&): ref_count() {
std::cout << " Copy constructor..." << std::endl;
} /**
* 不允许进行赋值操作
*/
intrusive_ptr_base& operator=(intrusive_ptr_base const& rhs) {
std::cout << " Assignment operator..." << std::endl;
return *this;
} /**
* 递增引用计数(放到基类中以便compiler能找到,否则需要放到boost名字空间中)
*/
friend void intrusive_ptr_add_ref(intrusive_ptr_base<T> const* s) {
std::cout << " intrusive_ptr_add_ref..." << std::endl;
assert(s->ref_count >= );
assert(s != );
++s->ref_count;
} /**
* 递减引用计数
*/
friend void intrusive_ptr_release(intrusive_ptr_base<T> const* s) {
std::cout << " intrusive_ptr_release..." << std::endl;
assert(s->ref_count > );
assert(s != );
if (--s->ref_count == )
boost::checked_delete(static_cast<T const*>(s)); //s的实际类型就是T,intrusive_ptr_base<T>为基类
} /**
* 类似于shared_from_this()函数
*/
boost::intrusive_ptr<T> self() {
return boost::intrusive_ptr<T>((T*)this);
} boost::intrusive_ptr<const T> self() const {
return boost::intrusive_ptr<const T>((T const*)this);
} int refcount() const {
return ref_count;
} private:
///should be modifiable even from const intrusive_ptr objects
mutable boost::detail::atomic_count ref_count; };

  用户类类型需要继承intrusive_ptr_base基类,以便具有引用计数功能。

 #include <iostream>
#include <string>
#include <boost/intrusive_ptr.hpp>
#include "intrusive_ptr_base.hpp" /**
* 用户类类型继承自intrusive_ptr_base,该实现方式类似于boost::enable_shared_from_this<Y>
*/
class Connection : public intrusive_ptr_base< Connection > {
public:
/**
* 构造函数,调用intrusive_ptr_base< Connection >的缺省构造函数来初始化对象的基类部分
*/
Connection(int id, std::string tag):
connection_id( id ), connection_tag( tag ) {} /**
* 拷贝构造函数,只复制自身数据,不能复制引用计数部分
*/
Connection(const Connection& rhs):
connection_id( rhs.connection_id ), connection_tag( rhs.connection_tag) {} /**
* 赋值操作,同样不能复制引用计数部分
*/
const Connection operator=( const Connection& rhs) {
if (this != &rhs) {
connection_id = rhs.connection_id;
connection_tag = rhs.connection_tag;
} return *this;
} private:
int connection_id;
std::string connection_tag;
}; int main() {
std::cout << "Create an intrusive ptr" << std::endl;
boost::intrusive_ptr< Connection > con0 (new Connection(, "sss") ); //调用intrusive_ptr_add_ref()递增引用计数
std::cout << "Create an intrusive ptr. Refcount = " << con0->refcount() << std::endl; boost::intrusive_ptr< Connection > con1 (con0); //调用intrusive_ptr_add_ref()
std::cout << "Create an intrusive ptr. Refcount = " << con1->refcount() << std::endl;
boost::intrusive_ptr< Connection > con2 = con0; //调用intrusive_ptr_add_ref()
std::cout << "Create an intrusive ptr. Refcount = " << con2->refcount() << std::endl; std::cout << "Destroy an intrusive ptr" << std::endl; return ;
}

程序运行输出:
Create an intrusive ptr
Default constructor 
intrusive_ptr_add_ref...
Create an intrusive ptr. Refcount = 1
intrusive_ptr_add_ref...
Create an intrusive ptr. Refcount = 2
intrusive_ptr_add_ref...
Create an intrusive ptr. Refcount = 3
Destroy an intrusive ptr
intrusive_ptr_release...
intrusive_ptr_release...
intrusive_ptr_release...

4.4 boost::intrusive_ptr与boost::shared_ptr区别

  使用boost::shared_ptr用户类本身不需要具有引用计数功能,而是由boost::shared_ptr来提供;使用boost::shared_ptr的一大陷阱就是用一个raw pointer多次创建boost::shared_ptr,这将导致boost::shared_ptr析构时该raw pointer被多次销毁当。即不能如下使用:

 int *a = new int();
boost::shared_ptr ptr1(a);
boost::shared_ptr ptr2(a);  //错误!

  boost::intrusive_ptr完全具备boost::shared_ptr的功能,且不存在shared_ptr的问题,即可以利用raw pointer创建多个intrusive _ptr,其原因就在于引用计数的ref_count对象shared_ptr是放在shared_ptr结构里,而目标对象T通过继承intrusive_ptr_base引用计数作为T对象内部成员变量,就不会出现一个对象有两个引用计数器的情况出现。
  那么为什么通常鼓励大家使用shared_ptr,而不是intrusive_ptr呢, 在于shared_ptr不是侵入性的,可以指向任意类型的对象; 而intrusive_ptr所要指向的对象,需要继承intrusive_ptr_base,即使不需要引用计数成员也会被创建。

4.5 结论

  如果创建新类且需要进行传递,则继承intrusive_ptr_base使用intrusive_ptr

5. Boost::weak_ptr<T>

5.1 定义

weak_ptr 就是一个弱指针。weak_ptr 被shared_ptr控制, 它可以通过share_ptr的构造函数或者lock成员函数转化为share_ptr

5.2 特点

(1)weak_ptr的一个最大特点就是它共享一个share_ptr的内存

(2)无论是构造还是析构一个weak_ptr都不会影响引用计数器

5.3 弱引用与强引用

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

 boost::weak_ptr

 boost::weak_ptr<T>是boost提供的一个弱引用的智能指针,它的声明可以简化如下:

 namespace boost {

     template<typename T> class weak_ptr {
public:
template <typename Y>
weak_ptr(const shared_ptr<Y>& r); weak_ptr(const weak_ptr& r); ~weak_ptr(); T* get() const;
bool expired() const;
shared_ptr<T> lock() const;
};
}

  可以看到,boost::weak_ptr必须从一个boost::share_ptr或另一个boost::weak_ptr转换而来,这也说明,进行该对象的内存管理的是那个强引用的boost::share_ptrboost::weak_ptr只是提供了对管理对象的一个访问手段

  boost::weak_ptr除了对所管理对象基本访问功能(通过get()函数)外,还有两个常用的功能函数:expired()用于检测所管理的对象是否已经释放lock()用于获取所管理的对象的强引用指针

5.4 循环引用

5.4.1 循环定义

  引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象

5.4.2 循环引用例子

 #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;
} void main()
{
std::cout<<"begin test...\n";
test();
std::cout<<"end test.\n";
}

  运行该程序可以看到,即使退出了test函数后,由于parent和children对象互相引用,它们的引用计数都是1,不能自动释放,并且此时这两个对象再无法访问到。这就引起了c++中那臭名昭著的内存泄漏。

一般来讲,解除这种循环引用有下面有三种可行的方法

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

(2)当parent的生存期超过children的生存期的时候,children改为使用一个普通指针指向parent

(3)使用弱引用的智能指针打破这种循环引用

虽然这三种方法都可行,但方法1和方法2都需要程序员手动控制,麻烦且容易出错。这里主要介绍一下第三种方法和boost中的弱引用的智能指针boost::weak_ptr。

5.4.3 通过boost::weak_ptr来打破循环引用

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

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

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

5.5 使用原则

(1)要打破递归的依赖关系

(2)使用一个共享的资源不需要共享所有权

(3)避免悬空的指针

(4)shared_ptr构造weak_ptr时,weak_ptr所指内存为空会抛出异常,而weak_ptr的lock()成员不会抛出异常但会返回个空指针,根据自己需求选择

5.6 例子

 int _tmain(int argc, _TCHAR* argv[])
{
boost::shared_ptr<test> sharePtr(new test);;
boost::weak_ptr<test> weakPtr(sharePtr);
//weakPtr 就是用來保存指向這塊內存區域的指針的
//干了一大堆其他事情
boost::shared_ptr<test> sharePtr_2 = weakPtr.lock();
if (sharePtr_2)
sharePtr_2->print();
return ;
}

6. Boost::shared_array<T> 和Boost::scoped_array<T>

6.1 定义

前面提到过shared_ptr和scoped_ptr不能用于数组的内存(new []),所以shared_array和scoped_array就是他们的代替品。

6.2 例子

 int _tmain(int argc, _TCHAR* argv[])
{
const int size = ;
boost::shared_array<test> a(new test[]);
for (int i = ; i < size; ++i)
a[i].print();
return ;
}

7.std::auto_ptr

7.1 定义

  auto_ptr是C++标准库里的类,它接受一个类型形参的模板,为动态分配的对象提供异常安全。其实,它的核心思想是:用一个对象存储需要被自动释放的资源,然后依靠对象的析构函数来释放资源

7.2 特点

(1)auto_ptr的构造函数带explicit 关键字,必须使用初始化的直接形式创建auto_ptr对象

 auto_ptr<int> ap(new int());  //ok
auto_ptr<int> ap=new int(); //error

(2)auto_ptr 在析构函数中释放了动态分配的空间,因此能自动释放内存。下面函数只动态分配了内存,并没有显示释放。但是编译器保证在展开栈越过f之前运行pi的析构函数。

 void f() { auto_ptr<int> ap(new int()); } 

(3)auto_ptr重载了解引用操作符箭头操作符,支持了普通指针的行为。

(4)赋值时删除了左操作数指向的对象

 auto_ptr<int> ap1(new int());  auto_ptr<int> ap2;
ap2=ap1;

将ap1赋值给ap2后,删除了ap2原来指的对象;ap2置为指向ap1所指的对象;ap1为未绑定对象。可看代码。

(5)测试auto_ptr对象,可以调用get成员函数,该函数返回包含在auto_ptr对象中的基础指针。

 if(ap.get())  *ap=;   //ok
if(ap) *ap=; //error

7.3 使用原则

尽管auto_ptr类模板为处理动态分配的内存提供了安全性和便利性的尺度,但是也存在不少缺陷,接下来结合例子给出auto_ptr的一些缺陷。

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

 int a=;
auto_ptr<int> ap(&a); //编译没有问题,会导致未定义行为

(2)不要使两个auto_ptr对象指向同一对象

 auto_ptr<int> ap1(new int ());
auto_ptr<int> ap2(ap1.get());

(3)不要使用auto_ptr对象保存指向动态分配数组的指针。从源代码中可以看出,它用的是delete操作符,而不是delete [ ] 操作符

(4)不要auto_ptr对象存储在容器中。因为auto_ptr的复制赋值具有破坏性。不满足容器要求:复制或赋值后,两个对象必须具有相同值

7.4 例子

 #include <utility>
#include <iostream>
using namespace std; class A
{
public:
A() { id = ++count; cout << "create A" << id << "\n"; }
~A() { cout << "destroy A" << id << "\n"; }
private:
static int count;
int id;
}; int A::count = ; /*调用该函数会丢失掉所有权*/
void sink(auto_ptr<A> a)
{
cout << "Enter sink()\n";
} /*调用该函数会创建对象,并获取所有权*/
auto_ptr<A> create()
{
cout << "Enter create()\n";
auto_ptr<A> a(new A());
return a;
} int main(int argc, char *argv[])
{
auto_ptr<A> a1 = create();
auto_ptr<A> a2 = a1; /*转移所有权,此时a1无效了*/
auto_ptr<A> a3(new A());
cout << "Exit create()\n";
sink(a2);/*丢失所有权,会发现a2的释放在sink函数中进行*/
cout << "Exit sink()\n";
return ;
} 输出结果是:<br>Enter create()<br>create A1<br>create A2<br>Exit create()<br>Enter sink()<br>destroy A1<br>Exit sink()<br>destroy A2<br><br>

8. 使用智能指针的几个注意点

(1)声明一个智能指针的时候要立即给它实例化, 而且一定不能手动释放它。

(2)…_ptr<T> 不是T* 类型。所以:

a: 声明的时候要…_ptr<T> 而不是….._ptr<T*>。

b:不能把T* 型的指针赋值给它。

c: 不能写ptr=NULL, 而用ptr.reset()代替。

(3)不能循环引用

(4)不要声明临时的share_ptr, 然后把这个指针传递给一个函数

原文链接:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html

Boost智能指针使用总结的更多相关文章

  1. 基于C/S架构的3D对战网络游戏C++框架 _05搭建系统开发环境与Boost智能指针、内存池初步了解

    本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...

  2. Boost智能指针-基础知识

    简单介绍 内存管理一直是 C++ 一个比較繁琐的问题,而智能指针却能够非常好的解决问题,在初始化时就已经预定了删除.排解了后顾之忧.1998年修订的第一版C++标准仅仅提供了一种智能指针:std::a ...

  3. [转] Boost智能指针——scoped_ptr

    http://www.cnblogs.com/tianfang/archive/2008/09/15/1291050.html boost::scoped_ptr和std::auto_ptr非常类似, ...

  4. boost 智能指针intrusive_ptr

    boost::intrusive_ptr一种“侵入式”的引用计数指针,它实际并不提供引用计数功能,而是要求被存储的对象自己实现引用计数功能,并提供intrusive_ptr_add_ref和intru ...

  5. Boost智能指针——weak_ptr

    循环引用: 引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象.一个简单的例子如下: #include <string>#include <iost ...

  6. boost智能指针总结

    智能指针是一种具备指针类似行为的对象,当不在需要它的时候自动删除其引用的c++对象.直接点说就是自动析构C++对象. boost提供了6种智能指针,如下所示: scoped_ptr <boost ...

  7. Boost智能指针——scoped_ptr

    boost::scoped_ptr和std::auto_ptr非常类似,是一个简单的智能指针,它能够保证在离开作用域后对象被自动释放. 上一段代码,以及其输出: #include <string ...

  8. 智能指针剖析(上)std::auto_ptr与boost::scoped_ptr

    1. 引入 C++语言中的动态内存分配没有自动回收机制,动态开辟的空间需要用户自己来维护,在出函数作用域或者程序正常退出前必须释放掉. 即程序员每次 new 出来的内存都要手动 delete,否则会造 ...

  9. C++智能指针剖析(上)std::auto_ptr与boost::scoped_ptr

    1. 引入 C++语言中的动态内存分配没有自动回收机制,动态开辟的空间需要用户自己来维护,在出函数作用域或者程序正常退出前必须释放掉. 即程序员每次 new 出来的内存都要手动 delete,否则会造 ...

随机推荐

  1. PAT1055___排序神题

    题目意思比较简单,按财富,年龄,姓名来排序 看似挺普通的,但被坑了20多次TLE 首先排序只要一次,就是按题目规定的进行排序 然后在查询的时候,不是从头扫到尾看是否符合年龄的限制,而是记录这个年龄组在 ...

  2. webdriver常用API

    本章涉及Selenium WebDriver的所有接口. Recommended Import Style 推荐的导入风格如下: from selenium import webdriver 然后,你 ...

  3. flv格式详解+实例剖析

    简介 FLV(Flash Video)是现在非常流行的流媒体格式,由于其视频文件体积轻巧.封装播放简单等特点,使其很适合在网络上进行应用,目前主流的视频网站无一例外地使用了FLV格式.另外由于当前浏览 ...

  4. bzoj1729: [Usaco2005 dec]Cow Patterns 牛的模式匹配

    Description     约翰的N(1≤N≤100000)只奶牛中出现了K(1≤K≤25000)只爱惹麻烦的坏蛋.奶牛们按一定的顺序排队的时候,这些坏蛋总会站在一起.为了找出这些坏蛋,约翰让他的 ...

  5. Linux下干净卸载mysql详解

    转自:http://blog.csdn.net/tjcyjd/article/details/52189182 1.使用以下命令查看当前安装mysql情况 rpm -qa|grep -i mysql ...

  6. SQLSERVER出错提示:此上下文中不允许使用''。此处只允许使用常量、表达式或变量。不允许使用列名。

    在执行一段SQL语句时出现了这样的一段错误提示,在网上找了不少答案,都说的不是很详细,反复修改试验,最终解决了此问题.原SQl语句为: insert into shoufei(djbh,sflb,jk ...

  7. Integer-->String String-->Integer

    参考:http://blog.csdn.net/wangjolly/article/details/18354457 crane: String str="123";int a=0 ...

  8. python开发_python中的range()函数

    python中的range()函数的功能hen强大,所以我觉得很有必要和大家分享一下 就好像其API中所描述的: If you do need to iterate over a sequence o ...

  9. LR11中webservice协议的性能测试应用

    使用LR11对webservice协议的接口测试应用 脚本开发步骤:1.打开vuser generator,新建一个脚本,选择webservice协议:2.选择Manage Services(服务管理 ...

  10. JQueryDOM节点操作

    你一.JQueryDom节点操作 2.1查找节点 获取p节点 var $pDm=$("p"); 输出p节点的title属性 alert($pDm.attr("title& ...