智能指针 (合集)

22 篇文章 0 订阅

------------------------------------------------------------------------------------------------------------------------------

剖析C++标准库智能指针(std::auto_ptr)


         本文转自http://www.cppblog.com/yuanyajie/archive/2006/12/15/16489.html

 

1. Smart Pointer,中文名:智能指针, 舶来品?

         不可否认,资源泄露(resource leak)曾经是C++程序的一大噩梦.垃圾回收机制(Garbage Collection)一时颇受注目.然而垃圾自动回收机制并不能满足内存管理的即时性和可视性,往往使高傲的程序设计者感到不自在.况且,C++实现没有引入这种机制.在探索中,C++程序员创造了锋利的"Smart Pointer".一定程度上,解决了资源泄露问题.

         也许,经常的,你会写这样的代码:(//x拟为class:)

 

         是的,这里可能没什么问题.可在复杂、N行、m_PTRclassobj所指对象生命周期要求较长的情况下,你能保证你不会忘记delete m_PTRclassobj吗?生活中,我们往往不应该有太多的口头保证,我们需要做些真正有用的东西.还有一个更敏感的问题:异常.假如在#2方法执行期异常发生,函数执行终止,那么new出的对象就会泄露.于是,你可能会说:那么就捕获异常来保证安全性好了.
         你写这样的程式:

 

         哦!天哪!想象一下,你的系统,是否会象专为捕获异常而设计的. 一天,有人给你建议:"用Smart Pointer,那很安全.".你可以这样重写你的程序:

 

         OK!你不太相信.不用delete吗?是的.不用整天提心吊胆的问自己:"我全部delete了吗?",而且比你的delete策略更安全.

         然后,还有人告诉你,可以这样用呢:

         但不可这样用:

         预先提及所有权的问题,以便下面带着疑问剖析代码?

         //在上面的#5中,我要告诉你对象所有权转移了两次.
         //什么叫对象所有权呢?

2. std::auto_ptr的设计原理


         上面的一片正确用法,它们在干些什么?

         一片非法,它们犯了什么罪?

         一片什么所有权转移,它的内部机智是什么?

         哦!一头雾水?下面我们就来剖析其实现机制.

         基础知识:

         a.智能指针的关键技术:在于构造栈上对象的生命期控制堆上构造的对象的生命期.因为在智能指针的内部,存储着堆对象的指针,而且在构析函数中调用delete行为.大致机构如下:

 

         b.所有权转移之说

         上面曾有一非法的程式片段如下:

             auto_ptr<x> m_SMPTR1(new x(100));

             auto_ptr<x> m_SMPTR2 = m_SMPTR1;

             m_SMPTR2->print();

             //输出:100.

             m_SMPTR1->print();

             //!! 非法的.

         按常理来说,m_SMPTR->print();怎么是非法的呢?

         那是因为本来,m_SMPTR1维护指向new x(100)的指针,可是m_SMPTR2 = m_SMPTR1;auto_ptr内部机制使得m_SMPTR1将对象的地址传给m_SMPTR2,而将自己的对象指针置为0.那么自然m_SMPTR->print();失败.这里程序设计者要负明显的职责的.那么auto_ptr为什么采取这样的策略:保证所有权的单一性.亦保证了系统安全性.如果多个有全权的auto_ptr维护一个对象,那么在你消除一个auto_ptr时,将导致多个auto_ptr的潜在危险.

         下面我们以SGI-STL的auto_ptr设计为样本(去掉了无关分析的宏),来剖析其原理.

 

         //下面这片段用于类型转化,目前没有任何编译器支持具体技术细节不诉.   

 

         OK!就是这样了.
               正如上面原理介绍处叙说,
               你需要正视两大特性:
               1.构造栈对象的生命期控制堆上构造的对象的生命期
               2.通过release来保证auto_ptr对对象的独权.
      
         在我们对源码分析的基础上,重点看看:
         no系列错误在何处?
              no1.
                  我们看到构析函数template<class _Tp>
                                  ~auto_ptr() _STL_NOTHROW
                                 { delete _M_ptr; }
                  所以它不能维护数组,
                  维护数组需要操作:delete[] _M_ptr;
              no2.
                 先提部分vector和auto_ptr代码:
                 a.提auto_ptr代码

 

                  b.提vector代码

 

         从提取的vector代码,Part1可看出,push_back的操作行为.
         兵分两路,可是再向下看,你会发现,无一例外,都通过const _Tp& 进行拷贝行为,那么从auto_ptr提出的片段就派上用场了. 可你知道的,auto_ptr总是坚持对对象的独权.那必须修改原来维护的对象,而vector行为要求const _Tp&,这样自然会产生问题.一般编译器是可以发觉这种错误的.

         其实,STL所有的容器类都采用const _Tp&策略.
  
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 + 看了sutter和Josuttis的两篇文章中,都提及:                                                         +
 + STL容器不支持auto_ptr原因在于copy的对象只是获得所有权的对象,                      +
 + 这种对象不符合STL的要求.可是本人总感觉即时不是真正的复制对象,                    +
 + 但我用vector<auto_ptr<x> >的目的就在于维护对象,并不在乎                              +
 + 所谓的完全对象.而且我用自己写的Smart Pointer配合STL容器工作,                       +
 + 很正常.那需要注意的仅仅是const问题.                                                               +
 +                                                                                                                       +
 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


           no3.
           这个也是auto_ptr隐含的所有权问题引起的.const auto_ptr不允许修改.
           随便提及:const对象不代表对象一点不可以改变.在两种const语义下,都有方法修改对象或对象内部指针维护的对象或其它资源.

              no4.
           再看auto_ptr的构析函数.delete不可以消除栈上资源.

              no5.
           依赖传入对象指针的构造函数被声明为explicit,禁止隐式转换.

 

3.auto_ptr高级使用指南

           a.类成员auto_ptr,禁止构造函数以构建"完全对象"

 

         处于对构建于堆中的对象(new Structx)智能维护的需要.我们将programme1改造为programme2:不错,对象是可以智能维护了.对于包裹类(StructWrapper)你是否会有这样的构造或指派操作:
         StructWrapper m_SMPTRWrapper2(m_SMPTRWrapper1);
         StructWrapper mSMPTRWrapper2 = m_SMPTRWrapper1; 
         那么请注意:
         当你坦然的来一个:M_SMPTRWrapper1->Soperator1();的时候,系统崩溃了.不必惊讶,所有权还是所有权问题.问一下自己:当programme2默认拷贝构造函数作用时,又调用了auto_ptr的默认构造函数,那么auto_ptr所有的默认行为都遵循独权策略.对,就这样.m_SMPTRWrapper1的对象所有权转移给了m_SMPTRWrapper2.M_SMPTRWrapper1->Soperator1();那么操作变成了在NULL上的.哦!系统不崩溃才怪.那么你需要想,programme3那样利用auto_ptr的提领操作符自己的构造"完全对象".


         b.利用const关键字,防止不经意的权限转移
         从上面的叙述,你可看出,所有权转移到处可以酿成大祸.而对于一般应用来说,独权又是很好的安全性策略.那么我们就用const来修饰auto_ptr,禁止不经意的错误.
当然上面提及:并不代表auto_ptr是不可修改的.处于需要,从两种const语义,你都可实现修改.然,你还希望在函数传入传出auto_ptr那么你可传递auto_ptr的引用,那就万无一失了:          void fook(const auto_ptr<x>& m_PARAMin);在返回后赋予其它时,使用引用是不行的.你得用指针.因为引用无论作为lvalue还是rvaluev,都会调用构造或指派函数.

 

4.你是否觉得std::auto_ptr还不够完美

         在实践中,std::auto_ptr能满足你的需求吗?           
 
         Andrei Alexandrescu在一篇文章中,提及:有关Smart Pointer的技术就像巫术.Smart Pointer作为C++垃圾回收机制的核心,它必须足够强大的、具有工业强度和安全性.

         但为了可一劳永逸我们还需要披荆斩棘继续探索.

         下面在需求层面上,我们思索一下我们的智能指针还需要些什么?
       
 a. std::auto_ptr 能够处理数组吗?我们可以用智能指针来管理其它的资源吗?譬如一个线程句柄、一个文件句柄 and so on !
       
 b. 对于我们的对象真的永远实行独权政策吗?
         c. Our 智能指针还需要在继承和虚拟层面上发挥威力 !
         d. 往往,需要扩展Our 智能指针的功能成员函数来满足动态的需要 !
         e. 也许,你需要的还很多.

 

 



---------------------------------------------------------------------------------------------------------------------------------



 

 C++ Boost智能指针


转自 http://www.360doc.com/content/10/0511/11/1372330_27035365.shtml


正文

         智能指针能够使C++的开发简单化,主要是它能够像其它限制性语言(如C#、VB)自动管理内存的释放,而且能够做更多的事情。

 

1、 什么是智能指针

智能指针是一种像指针的C++对象,但它能够在对象不使用的时候自己销毁掉。

我们知道在C++中的对象不再使用是很难定义的,因此C++中的资源管理是很复杂的。各种智能指针能够操作不同的情况。当然,智能指针能够在任务结束的时候删除对象,除了在程序之外。

许多库都提供了智能指针的操作,但都有自己的优点和缺点。Boost库是一个高质量的开源的C++模板库,很多人都考虑将其加入下一个C++标准库的版本中。

 

Boost提供了下面几种智能指针:

shared_ptr<T>

本指针中有一个引用指针记数器,表示类型T的对象是否已经不再使用。shared_ptr 是Boost中提供普通的智能指针,大多数地方都使用shared_ptr

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>

scoped_ptr 是 Boost 提供的一个简单的智能指针,它能够保证在离开作用域后对象被释放。

例子说明:本例子使用了一个帮助我们理解的类: CSample, 在类的构造函数、赋值函数、析构函数中都加入了打印调试语句。因此在程序执行的每一步都会打印调试信息。在例子的目录里已经包含了程序中需要的Boost库的部分内容,不需要下载其它内容(查看Boost的安装指南)。

 

下面的例子就是使用scoped_ptr 指针来自动释放对象的:

使用普通指针

使用scoped_ptr 指针

void Sample1_Plain()

{

  CSample * pSample(new CSample);

 

  if (!pSample->Query() )

  // just some function...

  {

    delete pSample;

    return;

  }

 

  pSample->Use();

  delete pSample;

}

#include "boost/smart_ptr.h"

 

void Sample1_ScopedPtr()

{

  boost::scoped_ptr<CSample>

       samplePtr(new CSample);

 

  if (!samplePtr->Query() )

  // just some function...

    return;   

 

  samplePtr->Use();

 

}

使用普通普通指针的时候,我们必须记住在函数退出的时候要释放在这个函数内创建的对象。当我们使用例外的时候处理指针是特别烦人的事情(容易忘记销毁它)。使用scoped_ptr 指针就能够在函数结束的时候自动销毁它,但对于函数外创建的指针就无能为力了。

优点:对于在复杂的函数种,使用scoped_ptr 指针能够帮助我们处理那些容易忘记释放的对象。也因此在调试模式下如果使用了空指针,就会出现一个断言。

 

优点

自动释放本地对象和成员变量[1],延迟实例化,操作PIMPL和RAII(看下面)

缺点

在STL容器里,多个指针操作一个对象的时候需要注意。

性能

使用scoped_ptr 指针,会增加一个普通指针。

 

3、 引用指针计数器

引用指针计数器记录有多少个引用指针指向同一个对象,如果最后一个引用指针被销毁的时候,那么就销毁对象本身。

shared_ptr 就是Boost中普通的引用指针计数器,它表示可以有多个指针指向同一个对象,看下面的例子:

void Sample2_Shared()

{

  // (A) 创建Csample类的一个实例和一个引用。

  boost::shared_ptr<CSample> mySample(new CSample);

  printf("The Sample now has %i references\n", mySample.use_count()); // The Sample now has 1 references

  // (B) 付第二个指针给它。

  boost::shared_ptr<CSample> mySample2 = mySample; // 现在是两个引用指针。

  printf("The Sample now has %i references\n", mySample.use_count());

 

  // (C) 设置第一个指针为空。

  mySample.reset();

  printf("The Sample now has %i references\n", mySample2.use_count());  // 一个引用

 

  // 当mySample2离开作用域的时候,对象只有一个引用的时候自动被删除。

}

 

在(A)中在堆栈重创建了CSample类的一个实例,并且分配了一个shared_ptr指针。对象mySample入下图所示:



然后我们分配了第二个指针mySample2,现在有两个指针访问同一个数据。


我们重置第一个指针(将mySample设置为空),程序中仍然有一个Csample实例,mySample2有一个引用指针。


只要当最有一个引用指针mySample2退出了它的作用域之外,Csample这个实例才被销毁。


当然,并不仅限于单个Csample这个实例,或者是两个指针,一个函数,下面是用shared_ptr的实例:

·         用作容器中。

·         用在PIMPL的惯用手法 (the pointer-to-implementation idiom )。

·         RAII(Resource-Acquisition-Is-Initialization)的惯用手法中。

·         执行分割接口。

注意:如果你没有听说过PIMPL (a.k.a. handle/body) 和 RAII,可以找一个好的C++书,在C++中处于重要的内容,一般C++程序员都应该知道(不过我就是第一次看到这个写法)。智能指针只是一中方便的他们的方法,本文中不讨论他们的内容。

PIMPL:如果必须包容一个可能抛异常的子对象,但仍然不想从你自己的构造函数中抛出异常,考虑使用被叫做Handle Class或Pimpl的方法(“Pimpl”个双关语:pImpl或“pointer to implementation”)

 

4、 主要特点

boost::shared_ptr 有一些重要的特征必须建立在其它操作之上。

·         shared_ptr<T>作用在一个未知类型上

当声明或定义一个shared_ptr<T>,T可能是一个未知的类型。例如你仅仅在前面声明了class T,但并没有定义class T。当我们要释放这个指针的时候我们需要知道这个T具体是一个声明类型。

·         shared_ptr<T>作用在任意类型上

在这里本质上不需要制定T的类型(如从一个基类继承下来的)

·         shared_ptr<T>支持自己定义释放对象的操作

如果你的类中自己写了释放方法,也可以使用。具体参照Boost文档。

·         强制转换

如果你定义了一个U*能够强制转换到T*(因为T是U的基类),那么shared_ptr<U>也能够强制转换到shared_ptr<T>

·         shared_ptr 是线程安全的

(这种设计的选择超过它的优点,在多线程情况下是非常必要的)

·         已经作为一种惯例,用在很多平台上,被证明和认同的。

 

5、 例子:在容器中使用shared_ptr

许多容器类,包括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")) );

 

vector被销毁的时候,这个元素自动被销毁了。当然,除非有另一个智能指针引用了它,则还本能被销毁。让我们看Sample3中的使用:

void Sample3_Container()

{

  typedef boost::shared_ptr<CSample> CSamplePtr;

 

  // (A) create a container of CSample pointers:

  std::vector<CSamplePtr> vec;

 

  // (B) add three elements

  vec.push_back(CSamplePtr(new CSample));

  vec.push_back(CSamplePtr(new CSample));

  vec.push_back(CSamplePtr(new CSample));

 

  // (C) "keep" a pointer to the second:

  CSamplePtr anElement = vec[1];

 

  // (D) destroy the vector:

  vec.clear();

 

  // (E) the second element still exists

  anElement->Use();

  printf("done. cleanup is automatic\n");

 

  // (F) anElement goes out of scope, deleting the last CSample instance

}

 

6、 使用Boost中的智能指针,什么是正确的使用方法

使用智能指针的一些操作会产生错误(突出的事那些不可用的引用计数器,一些对象太容易释放,或者根本释放不掉)。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的情况下让我们使用智能指针,让我们看看weak_ptr的详细情况。

 

8、 使用weak_ptr跳出循环

强引用和弱引用的比较:

一个强引用当被引用的对象活着的话,这个引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。boost::share_ptr就是强引用。相对而言,弱引用当引用的对象活着的时候不一定存在。仅仅是当它存在的时候的一个引用。

boost::weak_ptr<T> 是执行弱引用的智能指针。当你需要它的时候就可以使用一个强(共享)指针指向它(当对象被释放的时候,它为空),当然这个强指针在使用完毕应该立即释放掉,在上面的例子中我们能够修改它为弱指针。

struct CBetterChild : public CSample

{

  weak_ptr<CDad> myDad;

 

  void BringBeer()

  {

    shared_ptr<CDad> strongDad = myDad.lock(); // request a strong pointer

    if (strongDad)                      // is the object still alive?

      strongDad->SetBeer();

    // strongDad is released when it goes out of scope.

    // the object retains the weak pointer

  }

};

 

9、 Intrusive_ptr——轻量级共享智能指针

shared_ptr比普通指针提供了更完善的功能。有一个小小的代价,那就是一个共享指针比普通指针占用更多的空间,每一个对象都有一个共享指针,这个指针有引用计数器以便于释放。但对于大多数实际情况,这些都是可以忽略不计的。

intrusive_ptr 提供了一个折中的解决方案。它提供了一个轻量级的引用计数器,但必须对象本身已经有了一个对象引用计数器。这并不是坏的想法,当你自己的设计的类中实现智能指针相同的工作,那么一定已经定义了一个引用计数器,这样只需要更少的内存,而且可以提高执行性能。

如果你要使用intrusive_ptr 指向类型T,那么你就需要定义两个函数:intrusive_ptr_add_ref intrusive_ptr_release。下面是一个简单的例子解释如何在自己的类中实现:

#include "boost/intrusive_ptr.hpp"

 

// forward declarations

class CRefCounted;

 

 

namespace boost

{

    void intrusive_ptr_add_ref(CRefCounted * p);

    void intrusive_ptr_release(CRefCounted * p);

};

 

// My Class

class CRefCounted

{

  private:

    long    references;

    friend void ::boost::intrusive_ptr_add_ref(CRefCounted * p);

    friend void ::boost::intrusive_ptr_release(CRefCounted * p);

 

  public:

    CRefCounted() : references(0) {}   // initialize references to 0

};

 

// class specific addref/release implementation

// the two function overloads must be in the boost namespace on most compilers:

namespace boost

{

 inline void intrusive_ptr_add_ref(CRefCounted * p)

  {

    // increment reference count of object *p

    ++(p->references);

  }

 

 

 

 inline void intrusive_ptr_release(CRefCounted * p)

  {

   // decrement reference count, and delete object when reference count reaches 0

   if (--(p->references) == 0)

     delete p;

  }

} // namespace boost

        

         这是一个最简单的(非线程安全)实现操作。但作为一种通用的操作,如果提供一种基类来完成这种操作或许很有使用价值,也许在其他地方会介绍到。

 

10、 scoped_array  shared_array

scoped_array  shared_array和上面讲的基本上相同,只不过他们是指向数组的。就像使用指针操作一样使用operator new[] ,他们都重载了operator new[]。注意他们并不初始化分配长度。

 

11、 Boost的安装

www.boost.org上下载最新版本的boost,然后解压缩到你指定的目录里,解压缩后的文件目录如下:

Boost\     boost的源文件和头文件。

Doc\        HTML格式的文档。

Lib\         库文件(不是必需的)

…             其他文件(“more\”里有其他资料)

添加目录到我们自己的IDE里:

VC6:在菜单Tools/Options,Directories tab, "Show Directories for... Include files",

VC7: 在菜单Tools/Options,  Projects/VC++ directories, "Show Directories for... Include files".

Boost的头文件都在boost\子目录里,例如本文档例子中有#include "boost/smart_ptr.hpp"。所以任何人当读到年的源文件的时候就立刻知道你用到了boost中的智能指针。

 

12、 关于本文档中的例子

本文档中的例子里有一个子目录boost\仅仅包含了本例子中使用到的一些头文件,仅仅是为了你编译这个例子,如果你需要下载完整的boost或者获取更多的资源请到www.boost.org

 

13、 VC6min/max的灾难

 

当在VC中使用boost库,或者其他库的时候会有一些小的问题。

在Windows的头文件中已经定义了min 和 max宏,所以在STL中的这两个函数就调用不到了,例如在MFC中就是这样,但是在Boost中,都是使用的std::命名空间下的函数,使用Windows的函数不能够接受不同类型的参数在模板中使用,但是许多库都依赖这些。

虽然Boost尽量处理这些问题,但有时候遇到这样的问题的时候就需要在自己的代码中加入像下面的代码在第一个#include前加入#define _NOMINMAX

#define _NOMINMAX            // disable windows.h defining min and max as macros

#include "boost/config.hpp"  // include boosts compiler-specific "fixes"

using std::min;              // makle them globally available

using std::max;

         这样操作并不是在任何时候都需要,而只有我们碰到使用了就需要加入这段代码。

 

14、 资源

获取更多的信息,或者有问题可以查找如下资源:

·         Boost home page

·         Download Boost

·         Smart pointer overview

·         Boost users mailing list

·         Boost中的智能指针(撰文  Bjorn Karlsson    翻译  曾毅)


 

 

 -----------------------------------------------------------------------------------------------------------------------------------



weak_ptr 用法


转自 http://hi.baidu.com/cpuramdisk/item/37d4124a22d9c00d6dc2f0da


头文件: "boost/weak_ptr.hpp"

weak_ptr 是 shared_ptr 的观察员。它不会干扰shared_ptr所共享的所有权。当一个被weak_ptr所观察的 shared_ptr 要释放它的资源时,它会把相关的 weak_ptr的指针设为空。这防止了 weak_ptr 持有悬空的指针。你为什么会需要 weak_ptr? 许多情况下,你需要旁观或使用一个共享资源,但不接受所有权,如为了防止递归的依赖关系,你就要旁观一个共享资源而不能拥有所有权,或者为了避免悬空指针。可以从一个weak_ptr构造一个shared_ptr,从而取得对共享资源的访问权。

以下是 weak_ptr的部分定义,列出并简要介绍了最重要的函数。

[cpp]  view plain copy
  1. namespace boost {  
  2.   
  3.     template<typename T> class weak_ptr {  
  4.     public:  
  5.         template <typename Y>  
  6.         weak_ptr(const shared_ptr<Y>& r);  
  7.   
  8.         weak_ptr(const weak_ptr& r);  
  9.   
  10.         ~weak_ptr();  
  11.   
  12.         T* get() const;   
  13.         bool expired() const;   
  14.         shared_ptr<T> lock() const;  
  15.     };   
  16. }  

成员函数template <typename Y> weak_ptr(const shared_ptr<Y>& r);

这个构造函数从一个shared_ptr创建 weak_ptr ,要求可以从 Y* 隐式转换为 T*. 新的 weak_ptr 被配置为旁观 r所引向的资源。r的引用计数不会有所改变。这意味着r所引向的资源在被删除时不会理睬是否有weak_ptr 引向它。这个构造函数不会抛出异常。

weak_ptr(const weak_ptr& r);

这个复制构造函数让新建的 weak_ptr 旁观weak_ptr r(译注:原文为shared_ptr r,有误)所引向的资源。weak_ptr(译注:原文为shared_ptr,有误)的引用计数保持不变。这个构造函数不会抛出异常。

~weak_ptr();

weak_ptr 的析构函数,和构造函数一样,它不改变引用计数。如果需要,析构函数会把 *this 与共享资源脱离开。这个析构函数不会抛出异常。

bool expired() const;

如果所观察的资源已经"过期",即资源已被释放,则返回 True 。如果保存的指针为非空,expired 返回 false. 这个函数不会抛出异常。

shared_ptr<T> lock() const

返回一个引向weak_ptr所观察的资源的 shared_ptr ,如果可以的话。如果没有这样指针(即 weak_ptr 引向的是空指针),shared_ptr 也将引向空指针。否则,shared_ptr所引向的资源的引用计数将正常地递增。这个函数不会抛出异常。


用法

我们从一个示范weak_ptr的基本用法的例子开始,尤其要看看它是如何不影响引用计数的。这个例子里也包含了 shared_ptr,因为 weak_ptr 总是需要和 shared_ptr一起使用的。使用 weak_ptr 要包含头文件 "boost/weak_ptr.hpp".

[cpp]  view plain copy
  1. #include "boost/shared_ptr.hpp"  
  2. #include "boost/weak_ptr.hpp"  
  3. #include <iostream>  
  4. #include <cassert>  
  5.   
  6. class A {};  
  7.   
  8. int main() {  
  9.   
  10.     boost::weak_ptr<A> w;  
  11.     assert(w.expired());  
  12.     {  
  13.         boost::shared_ptr<A> p(new A());  
  14.         assert(p.use_count()==1);  
  15.         w=p;  
  16.         assert(p.use_count()==w.use_count());  
  17.         assert(p.use_count()==1);  
  18.   
  19.         // 从weak_ptr创建shared_ptr   
  20.         boost::shared_ptr<A> p2(w);  
  21.         assert(p2==p);  
  22.     }  
  23.     assert(w.expired());  
  24.     boost::shared_ptr<A> p3=w.lock();  
  25.     assert(!p3);  
  26. }  


weak_ptr w 被缺省构造,意味着它初始时不旁观任何资源。要检测一个 weak_ptr 是否在旁观一个活的对象,你可以使用函数 expired. 要开始旁观,weak_ptr 必须要被赋值一个 shared_ptr. 本例中,shared_ptr p 被赋值给 weak_ptr w, 这等于说p和 w 的引用计数应该是相同的。然后,再从weak_ptr构造一个shared_ptr,这是一种从weak_ptr那里获得对共享资源的访问权的方法。如果在构造shared_ptr时,weak_ptr 已经过期了,将从shared_ptr的构造函数里抛出一个 boost::bad_weak_ptr类型的异常。再继续,当 shared_ptr p 离开作用域,w 就变成过期的了。当调用它的成员函数 lock 来获得一个shared_ptr时,这是另一种获得对共享资源访问权的方法,将返回一个空的 shared_ptr 。注意,从这个程序的开始到结束,weak_ptr 都没有影响到共享对象的引用计数的值。

与其它智能指针不同的是,weak_ptr 不对它所观察的指针提供重载的 operator* 和 operator->. 原因是对weak_ptr所观察的资源的任何操作都必须是明显的,这样才安全;由于不会影响它们所观察的共享资源的引用计数器,所以真的很容易就会不小心访问到一个无效的指针。这就是为什么你必须要传送 weak_ptr 给 shared_ptr的构造函数,或者通过调用weak_ptr::lock来获得一个 shared_ptr 。这两种方法都会使引用计数增加,这样在 shared_ptr 从 weak_ptr创建以后,它可以保证共享资源的生存,确保在我们要使用它的时候它不会被释放掉。


常见问题

由于在智能指针中保存的是指针的值而不是它们所指向的指针的值,因此在标准库容器中使用智能指针有一个常见的问题,就是如何在算法中使用智能指针;算法通常需要访问实际对象的值,而不是它们的地址。例如,你如何调用 std::sort 并正确地排序?实际上,这个问题与在容器中保存并操作普通指针是几乎一样的,但事实很容易被忽略(可能是由于我们总是避免在容器中保存裸指针)。当然我们不能直接比较两个智能指针的值,但也很容易解决。只要用一个解引用智能指针的谓词就可以了,所以我们将创建一个可重用的谓词,使得可以在标准库的算法里使用引向智能指针的迭代器,这里我们选用的智能指针是 weak_ptr

[cpp]  view plain copy
  1. #include <functional>  
  2. #include "boost/shared_ptr.hpp"  
  3. #include "boost/weak_ptr.hpp"  
  4.   
  5. template <typename Func, typename T>   
  6. struct weak_ptr_unary_t :   
  7.     public std::unary_function<boost::weak_ptr<T>,bool> {  
  8.         T t_;  
  9.         Func func_;  
  10.   
  11.         weak_ptr_unary_t(const Func& func,const T& t)   
  12.             : t_(t),func_(func) {}  
  13.   
  14.         bool operator()(boost::weak_ptr<T> arg) const {  
  15.             boost::shared_ptr<T> sp=arg.lock();  
  16.             if (!sp) {  
  17.                 return false;  
  18.             }  
  19.             return func_(*sp,t_);  
  20.         }  
  21. };  
  22.   
  23. template <typename Func, typename T> weak_ptr_unary_t<Func,T>   
  24. weak_ptr_unary(const Func& func, const T& value) {  
  25.     return weak_ptr_unary_t<Func,T>(func,value);  
  26. }  


weak_ptr_unary_t 函数对象对要调用的函数以及函数所用的参数类型进行了参数化。把要调用的函数保存在函数对象中使用使得这个函数对象很容易使用,很快我们就能看到这一点。为了使这个谓词兼容于标准库的适配器,weak_ptr_unary_t 要从 std::unary_function派生,后者保证了所有需要的 typedefs 都能提供(这些要求是为了让标准库的适配器可以这些函数对象一起工作)。实际的工作在调用操作符函数中完成,从weak_ptr创建一个 shared_ptr 。必须要确保在函数调用时资源是可用的。然后才可以调用指定的函数(或函数对象),传入本次调用的参数(要解引用以获得真正的资源) 和在对象中保存的值,这个值是在构造weak_ptr_unary_t时给定的。这个简单的函数对象现在可以用于任意可用的算法了。为方便起见,我们还定义了一个助手函数,weak_ptr_unary, 它可以推出参数的类型并返回一个适当的函数对象。我们来看看如何使用它。

[14] 要使得这个类型更通用,还需要更多的设计。

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <string>  
  3.   
  4. #include <vector>  
  5. #include <algorithm>  
  6. #include "boost/shared_ptr.hpp"  
  7. #include "boost/weak_ptr.hpp"  
  8.   
  9. int main() {  
  10.     using std::string;  
  11.     using std::vector;  
  12.     using boost::shared_ptr;  
  13.     using boost::weak_ptr;  
  14.   
  15.     vector<weak_ptr<string> > vec;  
  16.   
  17.     shared_ptr<string> sp1(  
  18.         new string("An example"));  
  19.     shared_ptr<string> sp2(  
  20.         new string("of using"));  
  21.     shared_ptr<string> sp3(  
  22.         new string("smart pointers and predicates"));  
  23.     vec.push_back(weak_ptr<string>(sp1));  
  24.     vec.push_back(weak_ptr<string>(sp2));  
  25.     vec.push_back(weak_ptr<string>(sp3));  
  26.   
  27.     vector<weak_ptr<string> >::iterator  
  28.         it=std::find_if(vec.begin(),vec.end(),  
  29.         weak_ptr_unary(std::equal_to<string>(),string("of using")));  
  30.   
  31.     if (it!=vec.end()) {  
  32.         shared_ptr<string> sp(*++it);  
  33.         std::cout << *sp << '\n';  
  34.     }  
  35. }  

本例中,创建了一个包含weak_ptr的 vector。最有趣的一行代码(是的,它有点长)就是我们为使用find_if算法而创建weak_ptr_unary_t的那行。

[cpp]  view plain copy
  1. vector<weak_ptr<string> >::iterator it=std::find_if(  
  2.     vec.begin(),  
  3.     vec.end(),  
  4.     weak_ptr_unary(  
  5.     std::equal_to<string>(),string("of using")));  

通过把另一个函数对象,std::equal_to, 和一个用于匹配的string一起传给助手函数weak_ptr_unary,创建了一个新的函数对象。由于 weak_ptr_unary_t 完全兼容于各种适配器(由于它是从std::unary_function派生而来的),我们可以再从它组合出各种各样的函数对象。例如,我们也可以查找第一个不匹配"of using"的串:

[cpp]  view plain copy
  1. vector<weak_ptr<string> >::iterator it=std::find_if(  
  2.     vec.begin(),  
  3.     vec.end(),  
  4.     std::not1(  
  5.     weak_ptr_unary(  
  6.     std::equal_to<string>(),string("of using"))));  

Boost智能指针是专门为了与标准库配合工作而设计的。我们可以创建有用的组件来帮助我们可以更简单地使用这些强大的智能指针。象 weak_ptr_unary 这样的工具并不是经常要用到的;有一个库提供了比weak_ptr_unary更好用的泛型绑定器。弄懂这些智能指针的语义,可以让我们更清楚地使用它们。

[15] 指Boost.Bind库。


两种从weak_ptr创建shared_ptr的惯用法

如你所见,如果你有一个旁观某种资源的 weak_ptr ,你最终还是会想要访问这个资源。为此,weak_ptr 必须被转换为 shared_ptr, 因为 weak_ptr 是不允许访问资源的。有两种方法可以从weak_ptr创建shared_ptr:把 weak_ptr 传递给 shared_ptr的构造函数,或者调用 weak_ptr 的成员函数lock, 它返回 shared_ptr. 选择哪一个取决于你认为一个空的 weak_ptr 是错误的抑或不是。shared_ptr 构造函数在接受一个空的 weak_ptr 参数时会抛出一个 bad_weak_ptr 类型的异常。因此应该在你认为空的 weak_ptr 是一种错误时使用它。如果使用 weak_ptr 的函数 lock, 它会在weak_ptr为空时返回一个空的 shared_ptr。这在你想测试一个资源是否有效时是正确的,一个空的 weak_ptr 是预期中的。此外,如果使用 lock, 那么使用资源的正确方法应该是初始化并同时测试它,如下:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <string>  
  3. #include "boost/shared_ptr.hpp"  
  4. #include "boost/weak_ptr.hpp"  
  5.   
  6. int main() {  
  7.     boost::shared_ptr<std::string>   
  8.         sp(new std::string("Some resource"));  
  9.     boost::weak_ptr<std::string> wp(sp);  
  10.     // ...  
  11.     if (boost::shared_ptr<std::string> p=wp.lock())  
  12.         std::cout << "Got it: " << *p << '\n';  
  13.     else  
  14.         std::cout << "Nah, the shared_ptr is empty\n";  
  15. }  

如你所见, p 被weak_ptr wp 的lock函数的结果初始化。然后 p 被测试,只有当它非空时资源才能被访问。由于 shared_ptr 仅在这个作用域中有效,所以在这个作用域之外不会有机会让你不小心用到它。另一种情形是当 weak_ptr 逻辑上必须非空的时候。那种情形下,不需要测试 shared_ptr 是否为空,因为 shared_ptr 的构造函数会在接受一个空weak_ptr时抛出异常,如下:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <string>  
  3. #include "boost/shared_ptr.hpp"  
  4. #include "boost/weak_ptr.hpp"  
  5.   
  6. void access_the_resource(boost::weak_ptr<std::string> wp) {  
  7.     boost::shared_ptr<std::string> sp(wp);  
  8.     std::cout << *sp << '\n';  
  9. }  
  10.   
  11. int main() {  
  12.     boost::shared_ptr<std::string>   
  13.         sp(new std::string("Some resource"));  
  14.     boost::weak_ptr<std::string> wp(sp);  
  15.     // ...  
  16.     access_the_resource(wp);   
  17. }  

在这个例子中,函数 access_the_resource 从一个weak_ptr构造 shared_ptr sp 。这时不需要测试 shared_ptr 是否为空,因为如果 weak_ptr 为空,将会抛出一个 bad_weak_ptr 类型的异常,因此函数会立即结束;错误会在适当的时候被捕获和处理。这样做比显式地测试 shared_ptr 是否为空然后返回要更好。这就是从weak_ptr获得shared_ptr的两种方法。


总结

weak_ptr 是Boost智能指针拼图的最后一块。weak_ptr 概念是shared_ptr的一个重要伙伴。它允许我们打破递归的依赖关系。它还处理了关于悬空指针的一个常见问题。在共享一个资源时,它常用于那些不参与生存期管理的资源用户。这种情况不能使用裸指针,因为在最后一个 shared_ptr 被销毁时,它会释放掉共享的资源。如果使用裸指针来引用资源,将无法知道资源是否仍然存在。如果资源已经不存在,访问它将会引起灾难。通过使用 weak_ptr, 关于共享资源已被销毁的信息会传播给所有旁观的 weak_ptrs,这意味着不会发生无意间访问到无效指针的情形。这就象是观察员模式(Observer pattern)的一个特例;当资源被销毁,所有表示对此感兴趣的都会被通知到。

对于以下情形使用 weak_ptr :

  •      1. 要打破递归的依赖关系

  •      2. 使用一个共享的资源而不需要共享所有权

  •      3. 避免悬空的指针

 

 






-----------------------------------------------------------------------------------------------------------------------------------



智能指针 std::auto_ptr 和 shared_ptr


           本文转自http://ertou.blog.51cto.com/789994/224800

 

           auto_ptr 类可以用于管理由 new 分配的单个对象,但是无法管理动态分配的数组(我们通常不会使用数组,而是使用 vector 代替数组)。auto_ptr 在拷贝和赋值的时候有不寻常的行为,因此 auto_ptrs 不能被保存在 stl 的容器中。当 auto_ptr 离开了自己的作用域或者被销毁,由 auto_ptr 管理的对象也会被销毁。
           使用std::auto_ptr需要的头文件: #include <memory>
// 示例 1(b): 安全代码, 使用了auto_ptr
void f()
{
   auto_ptr<T> pt( new T );
    .....
} // 酷: 当pt出了作用域时析构函数被调用,从而对象被自动删除


  现在代码不会泄漏T类型的对象,不管这个函数是正常退出还是抛出了异常,因为pt的析构函数总是会在出栈时被调用。清理会自动进行。
  最后,使用一个auto_ptr就像使用一个内建的指针一样容易,而且如果想要“撤销”资源,重新采用手动的所有权,我们只要调用release().

 // 示例 2: 使用一个 auto_ptr
void g()
{
    T* pt1 = new T;  // 现在,我们有了一个分配好的对象
    auto_ptr<T> auto_pt2( pt1 ); // 将所有权传给了一个auto_ptr对象,auto_pt2 指向了 pt1

   // 使用auto_ptr就像我们以前使用简单指针一样
   *auto_pt2 = 12; // 就像 "*pt1 = 12;"
   auto_pt2->SomeFunc(); // 就像 "pt1->SomeFunc();"

   // 用get()来获得指针的值
   assert( pt1 == auto_pt2.get() );  // 二者一样
   // 用release()来撤销所有权, auto_pt2 把保存的指针地址给了pt3, 而自己指向了NUll。
   T* pt3 = auto_pt2.release();  //
   // 自己删除这个对象,因为现在没有任何auto_ptr拥有这个对象
   delete pt3;
}     // pt2不再拥有任何指针,所以不要试图删除它...ok,不要重复删除



  最后,我们可以使用auto_ptr的reset()函数来重置auto_ptr使之拥有另一个对象。如果这个auto_ptr已经拥有了一个对象,那么,它会先删除已经拥有的对象,因此调用reset()就如同销毁这个auto_ptr,然后新建一个并拥有一个新对象:
// 示例 3: 使用reset()
//
 void h()
 {
   auto_ptr<T> pt( new T(1) );
   pt.reset( new T(2) );  //即pt会首先delete pt目前指向的地址(new T(1)得到的地址),
                            //然后再指向new T(2)分配的地址
 } // 最后,pt出了作用域,
    // 第二个T也被自动删除了


 
           boost::scoped_ptr虽然简单易用,但它不能共享所有权的特性却大大限制了其使用范围,而boost::shared_ptr可以解决这一局限。顾名思义,boost::shared_ptr是可以共享所有权的智能指针,首先让我们通过一个例子看看它的基本用法:
#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>

class implementation
{
public:
    ~implementation() { std::cout <<"destroying implementation/n"; }
    void do_something() { std::cout << "did something/n"; }
};

void test()
{
    boost::shared_ptr<implementation> sp1(new implementation());
    std::cout<<"The Sample now has "<<sp1.use_count()<<" references/n";

    boost::shared_ptr<implementation> sp2 = sp1;
    std::cout<<"The Sample now has "<<sp2.use_count()<<" references/n";
    
    sp1.reset();
    std::cout<<"After Reset sp1. The Sample now has "<<sp2.use_count()<<" references/n";

    sp2.reset();
    std::cout<<"After Reset sp2./n";
}

void main()
{
    test();
}



           该程序的输出结果如下:
           The Sample now has 1 references
           The Sample now has 2 references
           After Reset sp1. The Sample now has 1 references
           destroying implementation
           After Reset sp2.

           可以看到,boost::shared_ptr指针sp1和sp2同时拥有了implementation对象的访问权限,且当sp1和sp2都释放对该对象的所有权时,其所管理的的对象的内存才被自动释放。在共享对象的访问权限同时,也实现了其内存的自动管理。

boost::shared_ptr的内存管理机制:
           boost::shared_ptr的管理机制其实并不复杂,就是对所管理的对象进行了引用计数,当新增一个boost::shared_ptr对该对象进行管理时,就将该对象的引用计数加一;减少一个boost::shared_ptr对该对象进行管理时,就将该对象的引用计数减一,如果该对象的引用计数为0的时候,说明没有任何指针对其管理,才调用delete释放其所占的内存。
上面的那个例子可以的图示如下:
  1. sp1对implementation对象进行管理,其引用计数为1
  2. 增加sp2对implementation对象进行管理,其引用计数增加为2
  3. sp1释放对implementation对象进行管理,其引用计数变为1
  4. sp2释放对implementation对象进行管理,其引用计数变为0,该对象被自动删除
boost::shared_ptr的特点:
           和前面介绍的boost::scoped_ptr相比,boost::shared_ptr可以共享对象的所有权,因此其使用范围基本上没有什么限制(还是有一些需要遵循的使用规则,下文中介绍),自然也可以使用在stl的容器中。另外它还是线程安全的,这点在多线程程序中也非常重要。

boost::shared_ptr的使用规则:
           boost::shared_ptr并不是绝对安全,下面几条规则能使我们更加安全的使用boost::shared_ptr:
  1. 避免对shared_ptr所管理的对象的直接内存管理操作,以免造成该对象的重释放
  2. shared_ptr并不能对循环引用的对象内存自动管理(这点是其它各种引用计数管理内存方式的通病)。
  3. 不要构造一个临时的shared_ptr作为函数的参数。
    如下列代码则可能导致内存泄漏:
void test()
{
    foo(boost::shared_ptr<implementation>(new    implementation()),g());
}
正确的用法为:
void test()
{
    boost::shared_ptr<implementation> sp    (new implementation());
    foo(sp,g());
}




-----------------------------------------------------------------------------------------------------------------------------------



借shared_ptr实现copy-on-write



转自 http://soft-app.iteye.com/blog/921974



场景:
         一个多线程的C++程序,24h x 5.5d运行。有几个工作线程ThreadW{0,1,2,3},处理客户发过来的交易请求,另外有一个背景线程ThreadB,不定期更新程序内部的参考数据。这些线程都跟一个hash表打交道,工作线程只读,背景线程读写,必然要用到一些同步机制,防止数据损坏。这里的示例代码用std::map代替hash表,意思是一样的:

[cpp]  view plain copy
  1. typedef map<string, vector<pair<string, int> > > Map;  
         map 的 key 是用户名,value 是一个vector,里边存的是不同stock的最小交易间隔,vector已经排好序,可以用二分查找。
         我们的系统要求工作线程的延迟尽可能小,可以容忍背景线程的延迟略大。一天之内,背景线程对数据更新的次数屈指可数,最多一小时一次,更新的数据来自于网络,所以对更新的及时性不敏感。Map的数据量也不大,大约一千多条数据。
         最简单的同步办法,用读写锁,工作线程加读锁,背景线程加写锁。但是读写锁的开销比普通mutex要大,如果工作线程能用最普通的非重入Mutex实现同步,就不必用读写锁,性能较高。我们借助shared_ptr实现了这一点:

[cpp]  view plain copy
  1. class Mutex;  
  2. class MutexLockGuard;  
  3.   
  4. class CustomerData  
  5. {  
  6. public:  
  7.     CustomerData() : data_(new Map)  
  8.     { }  
  9.   
  10.     ~CustomerData();  
  11.   
  12.     int query(const string& customer, const string& stock) const  
  13.     {  
  14.         MapPtr data = getData();  
  15.         // data 一旦拿到,就不再需要锁了。取数据的时候只有getData()内部有锁,多线程并发读的性能很好。  
  16.   
  17.         // 假设用户肯定存在  
  18.         const EntryList& entries = (*data)[customer];  
  19.         return findEntry(entries, stock);  
  20.     }  
  21.   
  22.     void update(const string& customer, const EntryList& entries );  
  23.   
  24. private:  
  25.     typedef vector<string, int> EntryList;  
  26.     typedef map<string, EntryList> Map;  
  27.     typedef tr1::shared_ptr<Map> MapPtr;  
  28.   
  29.     static int findEntry(const EntryList& entries, const string& key) const  
  30.     { /* 用 lower_bound 在 entries 里找 key */ }  
  31.   
  32.     MapPtr getData() const  
  33.     {  
  34.         MutexLockGuard lock(dataMutex_);  
  35.         return data_;  
  36.     }  
  37.   
  38.     MapPtr data_;  
  39.     mutable Mutex dataMutex_;  
  40. };  

         关键看CustomerData::update()怎么写。既然要更新数据,那肯定得加锁,如果这时候其他线程正在读,那么不能在原来的数据上修改,得创建一个副本,在副本上修改,修改完了再替换。如果没有用户在读,那么就能直接修改,节约一次拷贝。

[cpp]  view plain copy
  1. void CustomerData::update(const string& customer, const EntryList& entries )  
  2. {  
  3.     MutexLockGuard lock(dataMutex_);  
  4.     if (!data_.unique())  
  5.     {  
  6.         MapPtr newData(new Map(*data_));  
  7.         data_.swap(newData);  
  8.     }  
  9.     assert(data_.unique());  
  10.     (*data_)[customer] = entries;  
  11. }  

         注意其中用了shared_ptr::unique()来判断是不是有人在读,如果有人在读,那么我们不能直接修改,因为query()并没有全程加锁,只在getData()内部有锁。shared_ptr::swap()把data_替换为新副本,而且我们还在锁里,不会有别的线程来读,可以放心地更新。
         据我们测试,大多数情况下更新都是在原来数据上进行的,拷贝的比例还不到1%,很高效。更准确的说,这不是copy-on-write,而是copy-on-other-reading。
         我们将来可能会采用无锁数据结构,不过目前这个实现已经非常好,满足我们的要求。






------------------------------------------------------------------------------------------------------------------------------


智能指针的标准之争


本文转自http://ertou.blog.51cto.com/789994/224818

 

       2001 年10 月和2002 年4 月,在美国的华盛顿和荷兰的安的列斯群岛上分别召开了两次C++标准会议。会议的内容之一是对一项新的C++特性提议——智能指针(Smart Pointer)——进行讨论。本文将对可能成为C++新标准的两种智能指针方案(Boost vs. Loki)进行介绍和分析,并给出了相应的使用实例。

关键词:智能指针 C++ Boost Loki

  在现在的标准C++中,只有一种智能指针:std::auto_ptr。其原因并非是因为auto_ptr 已足以应付所有相关的工作——实际上,auto_ptr 有一个重大的缺陷,就是它不能被用在STL 容器中——而是因为现在的C++标准在制定时并未能对智能指针进行全面的考察。按照C++标准委员会成员Herb Sutter 的说法,只有一种标准的智能指针是一件“可羞”的事情:首先,智能指针所能做的许多有用的事情,是可怜的auto_ptr 不能完成的;其次,在有些情况下使用auto_ptr 可能会造成问题,上面所说的不能在容器中使用就是一例。实际上,许多程序员已经开发了各种有用的智能指针,有些甚至在auto_ptr 被定为标准之前就已存在,但问题是,它们不是标准的。在这样的情况下,C++标准委员会考虑引入新的智能指针,也就是自然而然的事情了。目前进入委员会视野的,主要有两种智能指针方案:Boost 智能指针和Loki 智能指针。前者是由C++标准委员会库工作组发起的Boost 组织开发的,而后者由世界级的C++专家Andrei Alexandrescu 开发,并在他所著的“Modern C++ Design”一书中进行了详细的阐释。下面,让我们分别来看一看这两种方案各自的技术特点。


一、 Boost 智能指针
  Boost 的智能指针方案实现了五种智能指针模板类,每种智能指针都用于不同的目的。这五种智能指针是:

  template<typename T> class scoped_ptr;
  template<typename T> class scoped_array;
  template<typename T> class shared_ptr;
  template<typename T> class shared_array;
  template<typename T> class weak_ptr;


  下面将分别介绍它们各自的特性,并给出相应的使用实例:


   scoped_ptr:意在用作指向自动(栈)对象的、不可复制的智能指针。该模板类存储的是指向动态分配的对象(通过new 分配)的指针。被指向的对象保证会被删除,或是在scoped_ptr 析构时,或是通过显式地调用reset 方法。注意该模板没有“共享所有权”或是“所有权转让”语义。同时,它也是不可复制的(noncopyable)。正因为如此,在用于不应被复制的指针时,它比shared_ptr 或std:auto_ptr 要更安全。与auto_ptr一样,scoped_ptr 也不能用于STL 容器中;要满足这样的需求,应该使用shared_ptr。另外,它也不能用于存储指向动态分配的数组的指针,这样的情况应使用scoped_array。

  下面是使用scoped_ptr 的一个简单实例:

class CTest
{
public:
    CTest() : m_id(0) {}
    CTest(int id) : m_id(id) {}
    ~CTest() { std::cout << "id: " << m_id << " - Destructor is being called/n"; }
    void SetId(int id) { m_id = id; }
    int GetId() { return m_id; }
    void DoSomething()
    { std::cout << "id: " << m_id << " - Doing something/n"; }
private:
    int m_id;
};
void main()
{
    boost::scoped_ptr<CTest> pTest(new CTest);
    pTest->DoSomething();
}


  其运行结果为:
  id: 0 - Doing something
  id: 0 - Destructor is being called
(以下的几个例子所用的CTest 类的定义完全相同,为节省篇幅,不再列出——作者)

  显然,尽管我们自己没有调用delete,pTest 仍然为我们正确地删除了它所指向的对象。看起来scoped_ptr的用途和auto_ptr 十分类似,但实际上,scoped_ptr 类型的指针的所有权不可转让,这一点是和auto_ptr相当不同的。


   scoped_array:该模板类与scoped_ptr 类似,但意在用于数组而不是单个对象。std::vector 可用于替换scoped_array,并且远为灵活,但其效率要低一点。在不使用动态分配时,boost::array 也可用于替换scoped_array。

  下面是一个使用scoped_array 的实例:

void main()
{
    boost::scoped_array<CTest> pTest(new CTest[2]);
    pTest[0].SetId(0);
    pTest[1].SetId(1);
    pTest[0].DoSomething();
    pTest[1].DoSomething();
    std::cout << '/n';
}

  其运行结果为:
  id: 0 - Doing something
  id: 1 - Doing something
  id: 1 - Destructor is being called
  id: 0 - Destructor is being called
  scoped_array 将负责使用delete [],而不是delete 来删除它所指向的对象。


  shared_ptr:意在用于对被指向对象的所有权进行共享。与scoped_ptr 一样,被指向对象也保证会被删除,但不同的是,这将发生在最后一个指向它的shared_ptr 被销毁时,或是调用reset 方法时。shared_ptr符合C++标准库的“可复制构造”(CopyConstructible)和“可赋值”(Assignable)要求,所以可被用于标
准的库容器中。另外它还提供了比较操作符,所以可与标准库的关联容器一起工作。shared_ptr 不能用于存储指向动态分配的数组的指针,这样的情况应该使用shared_array。该模板的实现采用了引用计数技术,所以无法正确处理循环引用的情况。可以使用weak_ptr 来“打破循环”。shared_ptr 还可在多线程环境中使用。
  下面的例子演示怎样将shared_ptr 用于std::vector 中:

typedef boost::shared_ptr<CTest> TestPtr;
void PT(const TestPtr &t)
{
    std::cout << "id: " << t->GetId() << "/t/t" << "use count: " << t.use_count() << '/n';
}
void main()
{
    std::vector<TestPtr> TestVector;
    TestPtr pTest0(new CTest(0));
    TestVector.push_back(pTest0);
    TestPtr pTest1(new CTest(1));
    TestVector.push_back(pTest1);
    TestPtr pTest2(new CTest(2));
    TestVector.push_back(pTest2);
    std::for_each(TestVector.begin(), TestVector.end(), PT);
    std::cout << '/n';
    pTest0.reset();
    pTest1.reset();
    pTest2.reset();
    std::for_each(TestVector.begin(), TestVector.end(), PT);
    std::cout << '/n';
    TestVector.clear();
    std::cout << '/n';
    std::cout << "exiting.../n";
}

其运行结果为:
  id: 0 use count: 2
  id: 1 use count: 2
  id: 2 use count: 2
  id: 0 use count: 1
  id: 1 use count: 1
  id: 2 use count: 1
  id: 0 - Destructor is being called
  id: 1 - Destructor is being called
  id: 2 - Destructor is being called
  exiting...

  运行结果中的“use count”是通过shared_ptr 的use_count()方法获得的“使用计数”,也就是,对所存储指针进行共享的shared_ptr 对象的数目。我们可以看到,在通过new 分配了3 个CTest 对象,并将相应的shared_ptr 对象放入TestVector 后,三个使用计数都为2;而在我们使用reset()方法复位pTest0、pTest1 和pTest2 后,TestVector 中的各个shared_ptr 对象的使用计数变成了1。这时,我们调用TestVector的clear()方法清除它所包含的shared_ptr 对象;因为已经没有shared_ptr 对象再指向我们先前分配的3个CTest 对象,这3 个对象也随之被删除,并导致相应的析构器被调用。


   shared_array:该模板类与shared_ptr 类似,但意在用于数组而不是单个对象。指向std::vector 的shared_ptr 可用于替换scoped_array,并且远为灵活,但其效率也要低一点。

  下面是使用实例:

void main()
{
    boost::shared_array<CTest> pTest1(new CTest[2]);
    pTest1[0].SetId(0);
    pTest1[1].SetId(1);
    std::cout << "use count: " << pTest1.use_count() << "/n/n";
    boost::shared_array<CTest> pTest2(pTest1);
    std::cout << "use count: " << pTest1.use_count() << "/n/n";
    pTest1.reset();
    pTest2[0].DoSomething();
    pTest2[1].DoSomething();
    std::cout << '/n';
    std::cout << "use count: " << pTest1.use_count() << "/n/n";
}

其运行结果为:
  use count: 1
  use count: 2
  id: 0 - Doing something
  id: 1 - Doing something
  use count: 1
  id: 1 - Destructor is being called
  id: 0 - Destructor is being called
  如此例所示,我们通过new 所分配的数组只有在指向它的pTest1 和pTest2 都被销毁或复位后才被删除。


   weak_ptr:该模板类存储“已由shared_ptr 管理的对象”的“弱引用”。要访问weak_ptr 所指向的对象,可以使用shared_ptr 构造器或make_shared 函数来将weak_ptr 转换为shared_ptr。指向被管理对象的最后一个shared_ptr 被销毁时将删除该对象,即使仍有weak_ptr 指向它也是如此。与原始指针不同的是,届时最后一个shared_ptr 会检查是否有weak_ptr 指向该对象,如果有的话就将这些weak_ptr 置为空。这样就不会发生使用原始指针时可能出现的“悬吊指针”(dangling pointer)情况,从而获得更高的安全水平。
  weak_ptr 符合C++标准库的“可复制构造”(CopyConstructible)和“可赋值”(Assignable)要求,所以可被用于标准的库容器中。另外它还提供了比较操作符,所以可与标准库的关联容器一起工作。

void main()
{
    boost::shared_ptr<CTest> pTest(new CTest);
    boost::weak_ptr<CTest> pTest2(pTest);
    if(boost::shared_ptr<CTest> pTest3 = boost::make_shared(pTest2))
        pTest3->DoSomething();
    pTest.reset();
    assert(pTest2.get() == NULL);
}

其运行结果为:
  id: 0 - Doing something
  id: 0 - Destructor is being called
  main 函数最后的断言确认了pTest2 所存储的指针的确已被置为NULL。

  显然,Boost 的智能指针方案会让我们产生这样的疑问:如果我们还需要其他类型的智能指针(比如支持COM 的智能指针),是否意味着我们必须在C++中再增加智能指针类型,或是采用非标准的实现呢?在泛型技术已得到极大发展的今天,Boost 的“增加增加再增加”的思路是不能让人满意的。正是在这里,我们看到了下面将要介绍的Loki Smart Pointer 的关键点:通过基于策略(policy-based)的设计来实现通用的智能指针模板。


二、 Loki 智能指针
  按照美国传统辞典(双解)的解释,Loki 是“A Norse god who created discord, especially among his fellow gods.”(斯堪的纳维亚的一个制造混乱的神,尤其是在其同类之间)。就其给Boost 智能指针带来的麻烦而言,Loki 智能指针倒真的当得起这个名字;而在另一方面,就其实现的优雅以及功能的强大(也就是说,它给开发者带来的好处)而言,它也的确属于“神族”。
  上面已经说过,Loki 的智能指针方案采用了基于策略的设计。其要点在于把将各功能域分解为独立的、由主模板类进行混合和搭配的策略。让我们先来看一看Loki 智能指针模板类SmartPtr 的定义:

template
<
    typename T,
    template <class>class OwnershipPolicy =RefCounted,
    class ConversionPolicy =DisallowConversion,
    template <class>class CheckingPolicy =AssertCheck,
    template <class>class StoragePolicy =DefaultSPStorage
>
class SmartPtr;


  我们可以看到,除了SmartPtr 所指向的对象类型T 以外,在模板类SmartPtr 中包括了这样一些策略:OwnershipPolicy(所有权策略)、ConversionPolicy(类型转换策略)、CheckingPolicy(检查策略)、StoragePolicy(存储策略)。正是通过这样的分解,使得SmartPtr 具备了极大的灵活性。我们可以任意组合各种不同的策略,从而获得不同的智能指针实现。下面先对各个策略逐一进行介绍:

   OwnershipPolicy:指定所有权管理策略,可以从以下预定义的策略中选择:DeepCopy(深度复制)、RefCounted(引用计数)、RefCountedMT(多线程化引用计数)、COMRefCounted(COM 引用计数)、RefLinked(引用链接)、DestructiveCopy(销毁式复制),以及NoCopy(无复制)。
   ConversionPolicy:指定是否允许进行向被指向类型的隐式转换。可以使用的实现有AllowConversion 和DisallowConversion。
   CheckingPolicy:定义错误检查策略。可以使用AssertCheck、AssertCheckStrict、RejectNullStatic、RejectNull、RejectNullStrict,以及NoCheck。
   StoragePolicy:定义怎样存储和访问被指向对象。Loki 已定义的策略有:DefaultSPStorage、ArrayStorage、LockedStorage,以及HeapStorage。

  除了Loki 已经定义的策略,你还可以自行定义策略。实际上,Loki 的智能指针模板覆盖了四种基本的Boost 智能指针类型:scoped_ptr、scoped_array、shared_ptr 和shared_array;至于weak_ptr,也可以通过定义相应的策略来实现其等价物。通过即将成为C++标准(C++0x)的typedef 模板特性,我们还可以利用Loki 的SmartPtr 模板来直接定义前面提到的Boost 的前四种智能指针类型。举例来说,我们可以这样定义:

shared_ptr:
template<typename T> // typedef 模板还不是标准的
typedef Loki::SmartPtr
<
    T,
    RefCounted, // 以下都是缺省的模板参数
    DisallowConversion,
    AssertCheck,
    DefaultSPStorage
>
shared_ptr;

  下面是一个使用Loki “shared_ptr”的实例:

typedef Loki::SmartPtr<CTest> TestPtr;
void PT(const TestPtr &t)
{
    std::cout << "id: " << t->GetId() << '/n';
}

void main()
{
    std::vector<TestPtr> TestVector;
    TestPtr pTest0(new CTest(0));
    TestVector.push_back(pTest0);
    TestPtr pTest1(new CTest(1));
    TestVector.push_back(pTest1);
    std::for_each(TestVector.begin(), TestVector.end(), PT);
    std::cout << '/n';
    Loki::Reset(pTest0, NULL);
    Loki::Reset(pTest1, NULL);
    std::for_each(TestVector.begin(), TestVector.end(), PT);
    std::cout << '/n';
    TestVector.clear();
    std::cout << '/n';
    std::cout << "exiting.../n";
}

其运行结果为:
  id: 0
  id: 1
  id: 0
  id: 1
  id: 0 - Destructor is being called
  id: 1 - Destructor is being called
  exiting...

  前面已经提到,要通过Loki 定义与Boost 的shared_ptr 功能等价的智能指针,除了第一个模板参数以外,其他的参数都可以使用缺省值,所以在上面的例子中,我们直接使用“typedef Loki::SmartPtr<CTest> TestPtr;”就可以了。非常的简单!

  为了进一步说明Loki 的“基于策略的设计方法”,让我们再来看一个更为复杂的例子:通过Loki::SmartPtr 实现线程专有存储(Thread-Specific Storage,TSS;又称线程局部存储,Thread Local Storage,TLS)。

  所谓线程专有存储,是指这样一种机制,通过它,多线程程序可以使用一个逻辑上的全局访问点来访问线程专有的数据,并且不会给每次访问增加额外的锁定开销。举一个简单的例子,在C 语言中,我们可以通过errno变量来获取错误代码;通常errno 就是一个普通的全局变量——在单线程环境中,这当然没有什么问题,但如果是多线程环境,这个全局的errno 变量就会给我们带来麻烦了。TSS 正是解决这一问题的有效方案。

  显然,智能指针的语义能够很好地适用于TSS。我们可以编写一种智能指针,使得所有对其所指向对象的访问都成为线程专有的——也就是说,每个线程访问的实际上是自己专有的对象,但从程序的外表来看,却都是对同一对象的访问。有了Loki::SmartPtr,我们可以非常容易地实现这样的智能指针:如其名字所指示的,TSS 涉及的是存储问题,我们只要对Loki::SmartPtr 的StoragePolicy 进行定制就可以了,其他的工作可以交给Loki::SmartPtr 去完成。

  在POSIX PThreads 库和Win32 中都提供了用于线程专有存储的函数,它们分别是pthread_key_create、pthread_setspecific、pthread_getspecific 和pthread_key_delete(POSIX PThreads 库),以及TlsAlloc、TlsSetvalue、TlsGetvalue 和TlsFree(Win32)。关于这些函数的详细信息,请参阅相关的文档。

  下面给出在MSVC 6.0 下实现的用于TSS 的StoragePolicy,并通过注释逐行进行分析(这个实现使用了PThreads-Win32 库,这是一个Win32 上的PThreads 实现。使用Win32 的线程专有函数也可以实现类似的StoragePolicy,但编写在线程退出时调用的CleanupHook()却需要一点“窍门”。具体方法可参考Boost 的thread_specific_ptr 实现):
namespace Loki
{
    // 实现TSS 的Loki 存储策略。改编自Douglas C. Schmidt、Timothy H. Harrison
    // 和Nat Pryce 的论文“Thread-Specific Storage for C/C++”中的部分代码。
    // def T& ReferenceType; // operator*所返回的类型。
public:
    // 构造器。对成员变量进行初始化。
    TS_SPStorage() : once_(0), key_(0), keylock_(NULL)
        { pthread_mutex_init(&keylock_, NULL); }
    // 析构器。释放先前获取的资源。
    ~TS_SPStorage()
    {
        pthread_mutex_destroy(&keylock_);
        pthread_key_delete(key_);
    }
    // 返回线程专有的被指向对象。
    PointerType operator->() const { return GetPointee(); }
    // 返回线程专有的被指向对象的引用。
    ReferenceType operator*() { return *GetPointee(); }
    // Accessors。获取线程专有的被指向对象。
    friend inline PointerType GetImpl(TS_SPStorage& sp)
    { return sp.GetPointee(); }
    // 获取线程专有的被指向对象的引用。
    // 该函数没有实现。但NoCheck 需要它才能正常工作。
    friend inline const StoredType& GetImplRef(const TS_SPStorage& sp)
    { return 0; }
protected:
    // 销毁所存储的数据。空函数。该工作将由CleanupHook()在各个线程退出时完成。
    void Destroy() {}
    // 获取当前线程专有的数据。
    PointerType GetPointee()
    {
        PointerType tss_data = NULL;
        // 使用双重检查锁定模式来在除了初始化以外的情况下避免锁定。
        // 之所以在这里,而不是在构造器中对“专有钥”进行初始化及分配TS 对象,
        // 是因为:(1) 最初创建TS 对象的线程(例如,主线程)常常并不是使用
        // 它的线程(工作线程),所以在构造器中分配一个TS 对象的实例常常并无
        // 好处,因为这个实例只能在主线程中访问。(2)在有些平台上,“专有
        // 钥”是有限的资源,所以等到对TS 对象进行第一次访问时再进行分配,有
        // 助于节省资源。
        // 第一次检查。
        if(once_ == 0)
        {
            // 加锁。确保访问的序列化。
            pthread_mutex_lock(&keylock_);
            // 双重检查。
            if(once_ == 0)
            {
                // 创建“专有钥”。
                pthread_key_create(&key_, &CleanupHook);
                // 必须在创建过程的最后出现,这样才能防止其他线程在“专有钥”
                // 被创建之前使用它。
                once_ = 1;
            }
            // 解锁。
            pthread_mutex_unlock(&keylock_);
        }
        // 从系统的线程专有存储中获取数据。注意这里不需要加锁。
        tss_data = (PointerType)pthread_getspecific(key_);
        // 检查是否这是当前线程第一次进行访问。
        if (tss_data == NULL)
        {
            // 从堆中为TS 对象分配内存。
            tss_data = new T;
            // 将其指针存储在系统的线程专有存储中。
            pthread_setspecific(key_, (void *)tss_data);
        }
        return tss_data;
    }
private:
    // 用于线程专有数据的“专有钥”。
    pthread_key_t key_;
    // “第一次进入”标志。
    int once_;
    // 用于避免在初始化过程中产生竞态情况。
    pthread_mutex_t keylock_;
    // 清扫挂钩函数,释放为TS 对象分配的内存。在每个线程退出时被调用。
    static void CleanupHook (void *ptr)
    {
        // 这里必须进行类型转换,相应的析构器才会被调用(如果有的话)
        delete (PointerType)ptr;
    }
};

// 用于模拟typedef template 的结构。
// 参见Herb Sutter 的“Template Typedef”一文。
struct TS_SPStorageWrapper
{
    template <class T>
    struct In
    {
        typedef TS_SPStorage<T> type;
    };
};


  使用了“Loki VC 6.0 Port”。

template <class T> class TS_SPStorage
{
public:
    typedef T* StoredType; // 被指向对象的类型。
    typedef T* PointerType; // operator->所返回的类型。
}

  下面让我们来看一个使用实例。首先让我们先定义:

Loki::SmartPtr
<
    int,
    Loki::NoCopyWrapper,
    Loki::DisallowConversion,
    Loki::NoCheckWrapper,
    Loki::TS_SPStorageWrapper
> value;



  其含义为:定义一种智能指针,被它指向的类型是int,OwnershipPolicy 是NoCopy,ConversionPolicy是DisallowConversion,CheckingPolicy 是NoCheck(因为TS 对象存储方式的限制,这是惟一能和TS_SPStorage 一起使用的CheckingPolicy。读者可自行尝试找到更好的解决方案),StoragePolicy 是TS_SPStorage。
然后,编写这样一个程序:

pthread_mutex_t io_mutex = NULL; // iostreams 不一定是线程安全的!
void *thread_proc(void *param)
{
    int id = (int)param;
    *value = 0;
    for (int i = 0; i < 3; i++)
    {
        (*value)++;
        pthread_mutex_lock(&io_mutex);
        std::cout << "thread " << id << ": " << *value << '/n';
        pthread_mutex_unlock(&io_mutex);
    }
    return NULL;
}

void main(int argc, char* argv[])
{
    pthread_mutex_init(&io_mutex, NULL);
    pthread_t id[3];
    for(int i = 0; i < 3; i++)
        pthread_create(&id[i], 0, thread_proc, (void *)i);
    for(int i = 0; i < 3; i++)
        pthread_join(id[i], NULL);
    pthread_mutex_destroy(&io_mutex);
}

其运行结果为:
  thread 0: 1
  thread 0: 2
  thread 1: 1
  thread 2: 1
  thread 1: 2
  thread 2: 2
  thread 1: 3
  thread 2: 3
  thread 0: 3

  由此我们可以看出,尽管看起来在各个线程中访问的都是同样的*value,但实际上访问的却是各自的线程专有的对象。而且除了初始化以外,对这些对象的访问无需进行序列化。因为Loki::SmartPtr 为我们做了大量的工作,TS_SPStorage 的实现十分简洁明了。有兴趣的读者,可以对这里的实现与Boost 的thread_specific_ptr 进行比较。像这样对Loki::SmartPtr 的策略进行的定制,在理论上数目是无限的,也就是说,通过它我们可以拥有五花八门、“千奇百怪”的智能指针——了不起的Loki,不是吗?有了这样的模板类,我们就再不需要一次一次地为标准C++增加新的智能指针类型了。


三、 结束语
  在推进器Boost 和斯堪的纳维亚之神Loki 的战争中,谁将胜出恐怕已不言而喻。倘若扮演上帝的C++标准委员会起了偏心,硬要选中Boost 的智能指针方案的话,那么在接下来的日子里,他们就将不再是C++标准委员会,而是C++智能指针救火委员会了。而即便那样,Loki,斯堪的纳维亚之神,也仍将矗立在C++大陆上,为他的“子民”——C++程序员们——带来统一、秩序和和谐。

相关资源
1. Herb Sutter,The New C++: Smart(er) Pointers,见C++ User Journal 网站:http://www.cuj.com/experts/2008/sutter.htm
2. Boost 文档:http://www.boost.org
3. Andrei Alexandrescu,Modern C++ Design 及Loki,见http://www.moderncppdesign.com或http://www.awprofessional.com/catalog/product.asp?product_id=%7B4ED3E6F3-371F-4ADC-9810-CC7B936164E3%7D。
4. Douglas C. Schmidt、Timothy H. Harrison 和Nat Pryce,Thread-Specific Storage for C/C++,见http://www.cs.wustl.edu/~schmidt/或http://www.flyingd&#111nkey.com/ace/(中文)。
5. Herb Sutter,Template Typedef,见http://www.gotw.ca/gotw/079.htm。
6. PThreads-Win32 库,见
http://sources.redhat.com/pthreads-win32/






















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值