今天复习了一下智能指针,然后动了动脑子,恍然觉得以前并没有真正的理解智能指针。首先是shared_ptr,shared_ptr的作用就不做介绍了。那么shared_ptr是如何实现的呢?我分析了一下,智能指针的要点就是“计数”和“指针”。所以智能指针的成员变量中就必须有计数值和对象指针。
指针计数为零的时候自动销毁,所以析构函数中需要实现计数和对象销毁的功能。然后还要注意的就是复制构造函数和复制函数(operator=)。想起来觉得也不难实现,于是就动手写了个丑陋版shared_ptr。
#include <iostream>
namespace toy{
template<class T>
class shared_pointer{
public:
template<class Y> explicit shared_pointer(Y* p) {
px = p;
shared_counter = new long(1);
}
~shared_pointer() {
if(*shared_counter >= 1)
(*shared_counter)--;
else
delete px;
}
template<class Y> shared_pointer(shared_pointer<Y> &r) {
px = r.px;
shared_counter = r.shared_counter;
(*shared_counter)++;
}
template<class Y> shared_pointer & operator=(shared_pointer<Y> const & r) {
if(*shared_counter >= 1)
(*shared_counter)--;
else
delete px;
px = r.px;
shared_counter = r.shared_counter;
(*shared_counter)++;
return *this;
}
T & operator*() const {
return *(this->px);
} // never throws
T * operator->() const {
return this->px;
} // never throws
T * get() const; // never throws
void swap(shared_pointer & b){
T* tmp_ptr;
long* tmp;
tmp_ptr = b.px;
b.px = px;
px = tmp_ptr;
tmp = b.shared_counter;
b.shared_counter = shared_counter;
shared_counter = tmp;
}
public:
T* px;
long* shared_counter;
};
};
int main(int argc, char** argv){
toy::shared_pointer<int> sp1(new int(233));
toy::shared_pointer<int> sp2(new int(123));
std::cout<<*(sp1.shared_counter)<<std::endl;
toy::shared_pointer<int> sp3 = sp1;
std::cout<<*(sp3.shared_counter)<<std::endl;
sp3 = sp2;
std::cout<<*(sp1.shared_counter)<<std::endl;
std::cout<<*(sp2.shared_counter)<<std::endl;
sp3.swap(sp1);
std::cout<<*(sp1.shared_counter)<<std::endl;
std::cout<<*(sp3.shared_counter)<<std::endl;
std::cout<<"*sp1 = "<<*sp1<<std::endl;
std::cout<<"*sp2 = "<<*sp2<<std::endl;
return 0;
}
看到有的博客讨论shared_ptr安全性的问题。其中引用了boost文档中的一段话说shared_ptr 对象提供与内建类型一样的线程安全级别。一个 shared_ptr 实例可以同时被多个线程“读”(仅使用不变操作进行访问)。 不同的 shared_ptr 实例可以同时被多个线程“写入”(使用类似 operator= 或 reset 这样的可变操作进行访问)(即使这些实 例是拷贝,而且共享下层的引用计数)。
博文中使用了下面这个测试用例。
#include <stdio.h>
#include <tr1/memory>
#include <pthread.h>
using std::tr1::shared_ptr;
shared_ptr<int> gp(new int(2000));
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;
}
这个测试例子其实是不太对的,在CostaSwapSharedPtr2()函数中连续两次使用了swap()函数。即使swap()函数是原子性的操作,这也会导致测试出错。所以这并不是一个正确的测试方法。
事实上,swap()函数并不是原子性的。其实现中不可能使用xchg指令,因为有两个共享指针,不能同时操作。其次,share_ptr的实现也没有使用互斥量这些东西,所以对同一个share_ptr的写肯定不是线程安全的。其他的智能指针感觉原理都差不多,感觉像是功能阉割版的share_ptr,就先不管了。
发表评论