Blog / 阅读

c++智能指针 shared_ptr 的使用方法

by admin on 2014-03-25 12:14:22 in ,



基于Boost库, C++11 加入了shared_ptr和weak_ptr. 它们最早在TR1中就被引入, 但在C++11中, 在Boost的基础上又加入了新的功能.
std::shared_ptr使用引用计数. 每一个shared_ptr的拷贝都指向相同的内存. 在最后一个shared_ptr析构的时候, 内存才会被释放.
[cpp] view plaincopy
 std::shared_ptr<int> p1(new int(5));  
std::shared_ptr<int> p2 = p1; // 都指向同一内存.  
   
p1.reset(); // 因为p2还在,所以内存没有释放.  
p2.reset(); // 释放内存, 因为没有shared_ptr指向那块内存了.  
  
std::shared_ptr 使用引用计数, 所以有循环计数的问题. 为了打破循环,可以使用std::weak_ptr. 顾名思义, weak_ptr是一个弱引用, 只引用, 不计数. 如果一块内存被shared_ptr和weak_ptr同时引用, 当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存, 内存也会被释放. 所以weak_ptr不保证它指向的内存一定是有效的, 在使用之前需要检查.  
  
std::shared_ptr<int> p1(new int(5));  
std::weak_ptr<int> wp1 = p1; // 还是只有p1有所有权.  
   
{  
  std::shared_ptr<int> p2 = wp1.lock(); // p1和p2都有所有权  
  if(p2) // 使用前需要检查  
  {   
    // 使用p2  
  }  
} // p2析构了, 现在只有p1有所有权.  
   
p1.reset(); // 内存被释放.  
   
std::shared_ptr<int> p3 = wp1.lock(); // 因为内存已经被释放了, 所以得到的是空指针.  
if(p3)  
{  
  // 不会执行到这.  
}  


shared_ptr的成员方法,user_count()的作用是获得当前对象被引用的次数,reset()的作用是释放指针对对象的引用,将指针设为空。
shared_ptr的线程安全性:
它是这样说的:  
 “Boost 文档对于 shared_ptr 的线程安全有一段专门的记述,内容如下:
 shared_ptr objects offer the same level of thread safety as built-in types. A shared_ptr instance can be "read" (accessed using only const operations) simultaneously by multiple threads. Different shared_ptr instances can be "written to" (accessed using mutable operations such as operator= or reset) simultaneosly by multiple threads (even when these instances are copies, and share the same reference count underneath.)
 Any other simultaneous accesses result in undefined behavior.
 翻译为中文如下:
 shared_ptr 对象提供与内建类型一样的线程安全级别。一个 shared_ptr 实例可以同时被多个线程“读”(仅使用不变操作进行访问)。 不同的 shared_ptr 实例可以同时被多个线程“写入”(使用类似 operator= 或 reset 这样的可变操作进行访问)(即使这些实 例是拷贝,而且共享下层的引用计数)。
 任何其它的同时访问的结果会导致未定义行为。” 
 
 这几句话比较繁琐,我总结一下它的意思: 
 
 1 同一个shared_ptr被多个线程“读”是安全的。 
 
 2 同一个shared_ptr被多个线程“写”是不安全的。 
 
 3 共享引用计数的不同的shared_ptr被多个线程”写“ 是安全的。 
 
 如何印证上面的观点呢? 
 
 其实第一点我觉得比较多余。因为在多个线程中读同一个对象,在正常情况下不会有什么问题。 
 
 所以问题就是:如何写程序证明同一个shared_ptr被多个线程"写"是不安全的? 
 
 我的思路是,在多个线程中同时对一个shared_ptr循环执行两遍swap。 shared_ptr的swap函数的作用就是和另外一个shared_ptr交换引用对象和引用计数,是写操作。执行两遍swap之后, shared_ptr引用的对象的值应该不变。 
 
 程序如下: 
[cpp] view plaincopy
#include <stdio.h>  
#include <tr1/memory>  
#include <pthread.h>  
  
using std::tr1::shared_ptr;  
  
shared_ptr<int> gp(new int(2000));  
  
shared_ptr<int>  CostaSwapSharedPtr1(shared_ptr<int> & p)  
{  
    shared_ptr<int> p1(p);  
    shared_ptr<int> p2(new int(1000));  
    p1.swap(p2);  
    p2.swap(p1);  
    return p1;  
}  
  
shared_ptr<int>  CostaSwapSharedPtr2(shared_ptr<int> & p)  
{  
    shared_ptr<int> p2(new int(1000));  
    p.swap(p2);  
    p2.swap(p);  
    return p;  
}  
  
  
void* thread_start(void * arg)  
{  
    int i =0;  
    for(;i<100000;i++)  
    {  
        shared_ptr<int> p= CostaSwapSharedPtr2(gp);  
        if(*p!=2000)  
        {  
            printf("Thread error. *gp=%d \n", *gp);  
            break;  
        }  
    }  
    printf("Thread quit \n");  
    return 0;  
}  
  
  
  
int main()  
{  
    pthread_t thread;  
    int thread_num = 10, i=0;  
    pthread_t* threads = new pthread_t[thread_num];  
    for(;i<thread_num;i++)  
        pthread_create(&threads[i], 0 , thread_start , &i);  
    for(i=0;i<thread_num;i++)  
        pthread_join(threads[i],0);  
    delete[] threads;  
    return 0;  
}  


 这个程序中我启了10个线程。每个线程调用10万次 CostaSwapSharedPtr2函数。 在CostaSwapSharePtr2函数中,对同一个share_ptr全局变量gp进行两次swap(写操作), 在函数返回之后检查gp的值是否被修改。如果gp值被修改,则证明多线程对同一个share_ptr执行写操作是不安全的。 
 
 程序运行的结果如下: 


[cpp] view plaincopy
Thread error. *gp=1000   
Thread error. *gp=1000   
Thread quit   
Thread quit   
Thread error. *gp=1000   
Thread quit   
Thread error. *gp=1000   
Thread quit   
Thread error. *gp=1000   
Thread quit   
Thread error. *gp=1000   
Thread quit   
Thread error. *gp=1000   
Thread quit   
Thread error. *gp=1000   
Thread quit   
Thread error. *gp=1000   
Thread quit   
Thread quit  


10个线程有9个出错。证明多线程对同一个share_ptr执行写操作是不安全的。
我们在程序中,如果不运行CostaSwapSharedPtr2, 改成运行CostaSwapSharedPtr1呢?
  CostaSwapSharedPtr1和CostaSwapSharedPtr2的区别在于, 它不是直接对全局变量gp进行写操作,而是将gp拷贝出来一份再进行写操作。运行的结果如下:
[cpp] view plaincopy
costa@pepsi:~/test/cpp/shared_ptr$ ./b  
Thread quit   
Thread quit   
Thread quit   
Thread quit   
Thread quit   
Thread quit   
Thread quit   
Thread quit   
Thread quit   
Thread quit  



写评论

相关文章

上一篇:java实现Excel导入导出

下一篇:DES加密算法详解- -

评论

写评论

* 必填.

分享

栏目

赞助商


热门文章

Tag 云