C++智能指针

今天复习了一下智能指针,然后动了动脑子,恍然觉得以前并没有真正的理解智能指针。首先是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,就先不管了。

发表评论