凌云的博客

行胜于言

漫谈 C++ 智能指针 05

分类:c++| 发布时间:2015-10-20 00:48:00


带引用计数的智能指针

在前面的文章中,我们实现了一个类似 std::auto_ptr 的智能指针。 这个实现最大的问题是赋值前后的两个元素不是逻辑独立的 (其中的一个直观表现是不能保存在 STL 容器中)。

通过实现带有引用计数的指针指针可以解决这个问题。 下面通过 C++11 的 std::shared_ptr 来演示下 (如果你的编译器不支持 C++11 可以使用 boost::shared_ptr 替代)。

#include <memory>
#include <iostream>

using namespace std;

void some_func()
{
    shared_ptr<int> p1(new int(3));
    shared_ptr<int> p2 = p1;

    cout << "p1 pointer: " << hex << p1.get() << endl;
    if (p1.get()) {
        cout <<  "p1 value: " << *p1 << endl;
    }

    cout << "p2 pointer: " << hex << p2.get() << endl;
    if (p2.get()) {
        cout << "p2 value: " << *p2 << endl;
    }
}

输出:

p1 pointer: 0x230b010
p1 value: 3
p2 pointer: 0x230b010
p2 value: 3

保存到 STL 容器中:

#include <vector>
#include <memory>
#include <iostream>

using namespace std;

void save_in_container()
{
    vector<shared_ptr<int> > vec;
    shared_ptr<int> p(new int(3));
    vec.push_back(p);
    shared_ptr<int> p2(new int(4));
    vec.push_back(p2);
    for (vector<shared_ptr<int> >::iterator i = vec.begin();
         i != vec.end(); ++i) {
        cout << **i << endl;
    }
}

实现带有引用计数的智能指针

下面来看看如何实现带有引用计数的智能指针。

namespace MY_DEMO {

template<typename T>
class shared_ptr
{
public:
    explicit shared_ptr(T *p = 0): p_(p), shared_count_(create_share_count()) {
    }

    shared_ptr(const shared_ptr<T> &rhs) {
        rhs.inc();
        this->p_ = rhs.p_;
        this->shared_count_ = rhs.shared_count_;
    }

    ~shared_ptr() {
        dec();
    }

    void reset(T *p) {
        if (this->get() == p)
            return;

        dec();
        shared_count_ = create_share_count();
        p_ = p;
    }

    T* get() {
        return p_;
    }

    shared_ptr<T>& operator= (const shared_ptr<T> &rhs) {
        if (rhs.shared_count_ != 0) {
            rhs.inc();
            this->dec();
            this->shared_count_ = rhs.shared_count_;
            this->p_ = rhs.p_;
        } else {
            this->dec();
            this->shared_count_ = 0;
            this->p_ = 0;
        }

        return *this;
    }

    bool operator! () const {
        return !p_;
    }

    T& operator* () const {
        return *p_;
    }

    T* operator-> () const {
        return p_;
    }

private:
    void inc() const {
        if (!shared_count_)
            return;

        ++*shared_count_;
    }

    void dec() {
        if (!shared_count_)
            return;

        if (!(--*shared_count_)) {
            delete shared_count_;
            shared_count_ = 0;

            if (p_) {
                delete p_;
                p_ = 0;
            }
        }
    }

    int* create_share_count() {
        int *tmp = new int(1);
        return tmp;
    }
private:
    T *p_;
    mutable int *shared_count_;
};

} // namespace MY_DEMO

带引用计数的智能指针通过共享一个引用计数器(在我们的实现中是 shared_count_) 来跟踪一共有多少个智能指针引用到了这个原始指针。 在拷贝构造函数或者赋值运算时将右值的引用计数加 1,然后将自身的引用计数减 1。 在构造函数中将引用计数置为 1,析构时将引用计数减 1。 当引用计数为 0 时,删除引用计数以及保存的原始指针。

测试带引用的指针指针

下面我们来测试下我们实现的智能指针是否如我们预期地工作。

首先,在 shared_ptr 上添加一些输出:

namespace MY_DEMO {

#include <iostream>
#include <string>

using namespace std;

template<typename T>
class shared_ptr
{
public:
    // See other impl above

    explicit shared_ptr(T *p = 0): p_(p), shared_count_(create_share_count()) {
        cout << "shared_ptr default constructor" << endl;
    }

    shared_ptr(const shared_ptr<T> &rhs) {
        cout << "shared_ptr copy constructor" << endl;
        rhs.inc();
        this->p_ = rhs.p_;
        this->shared_count_ = rhs.shared_count_;
    }

    ~shared_ptr() {
        cout << "shared_ptr destructor" << endl;
        dec();
    }

    void reset(T *p) {
        cout << "shared_ptr reset" << endl;
        if (this->get() == p)
            return;

        dec();
        shared_count_ = create_share_count();
        p_ = p;
    }

    shared_ptr<T>& operator= (const shared_ptr<T> &rhs) {
        cout << "shared_ptr operator=" << endl;
        if (rhs.shared_count_ != 0) {
            rhs.inc();
            this->dec();
            this->shared_count_ = rhs.shared_count_;
            this->p_ = rhs.p_;
        } else {
            this->dec();
            this->shared_count_ = 0;
            this->p_ = 0;
        }

        return *this;
    }

    void dump() {
        cout << "value = " << *p_ << endl;
        cout << "shared_count_ = " << *shared_count_ << endl;
    }

private:
    void inc() const {
        if (!shared_count_)
            return;

        ++*shared_count_;
        cout << "inc shared_count_ " << *shared_count_ << endl;
    }

    void dec() {
        if (!shared_count_)
            return;

        if (!(--(*shared_count_))) {
            cout << "dec shared_count_ " << *shared_count_ << endl;
            delete shared_count_;
            shared_count_ = 0;

            if (p_) {
                delete p_;
                p_ = 0;
            }
        } else {
            cout << "dec shared_count_ " << *shared_count_ << endl;
        }
    }
}

} // namespace MY_DEMO

测试拷贝构造函数

using MY_DEMO::shared_ptr;

void test_copy_constructor()
{
    cout << "begin test_copy_constructor" << endl;
    shared_ptr<int> p1(new int(3));
    shared_ptr<int> p2(p1);
    cout << "p1 dump:" << endl;
    p1.dump();
    cout << "p2 dump:" << endl;
    p2.dump();
    cout << "end test_copy_constructor" << endl;
}

输出为:

begin test_copy_constructor
shared_ptr default constructor
shared_ptr copy constructor
inc shared_count_ 2
p1 dump:
value = 3
shared_count_ = 2
p2 dump:
value = 3
shared_count_ = 2
end test_copy_constructor
shared_ptr destructor
dec shared_count_ 1
shared_ptr destructor
dec shared_count_ 0

首先我们通过默认的构造函数创建了 p1,然后将 p1 拷贝给 p2, 此时引用计数为 2。 然后退出 test_copy_constructor,调用 p2 的析构函数,引用计数减 1, 此时引用计数为 1,调用 p1 的析构函数,引用计数减 1, 此时引用计数为 0,删除引用计数以及原始指针。

测试 operator=

using MY_DEMO::shared_ptr;

void test_assign()
{
    cout << "begin test_assign" << endl;
    shared_ptr<int> p1(new int(3));
    shared_ptr<int> p2;
    p2 = p1;
    cout << "p1 dump:" << endl;
    p1.dump();
    cout << "p2 dump:" << endl;
    p2.dump();
    cout << "end test_assign" << endl;
}

输出为:

begin test_assign
shared_ptr default constructor
shared_ptr default constructor
shared_ptr operator=
inc shared_count_ 2
dec shared_count_ 0
p1 dump:
value = 3
shared_count_ = 2
p2 dump:
value = 3
shared_count_ = 2
end test_assign
shared_ptr destructor
dec shared_count_ 1
shared_ptr destructor
dec shared_count_ 0

首先我们通过默认的构造函数创建了 p1,p2。 然后将 p1 赋值 p2,此时 p1 引用计数为 2, p2 本来的引用计数为 0,删除本来的引用计数以及原始指针, 将 p1 的引用计数以及原始指针赋给 p2。 然后退出 test_copy_constructor,调用 p2 的析构函数,引用计数减 1, 此时引用计数为 1,调用 p1 的析构函数,引用计数减 1, 此时引用计数为 0,删除引用计数以及原始指针。

测试 reset

using MY_DEMO::shared_ptr;

void test_reset()
{
    cout << "begin test_reset" << endl;
    shared_ptr<int> p1(new int(3));
    int *tmp = new int(6);
    p1.reset(tmp);
    p1.reset(tmp);
    p1.dump();
    cout << "end test_reset" << endl;
}

输出为:

begin test_reset
shared_ptr default constructor
shared_ptr reset
dec shared_count_ 0
shared_ptr reset
value = 6
shared_count_ = 1
end test_reset
shared_ptr destructor
dec shared_count_ 0

通过默认构造函数创建 p1,然后创建原始指针 tmp, 第一次将 tmp 设置给 p1 时 p1 原来的引用计数减 1, 此时原来的引用计数为 0,删除原来的引用计数以及原始指针。 创建新的引用计数,并将原始指针设置为 tmp。 再次将 tmp 设置给 p1,此时由于 tmp 与 p1 保存的原始指针是 同一个指针,直接退出 reset 。 然后就是退出 test_reset,p1 析构。

保存到 STL 容器中

using MY_DEMO::shared_ptr;

void test_save_in_vector()
{
    cout << "begin test_save_in_vector" << endl;
    vector<shared_ptr<int> > vec;
    shared_ptr<int> p1(new int(3));
    vec.push_back(p1);
    cout << "p1 dump:" << endl;
    p1.dump();
    cout << "end test_save_in_vector" << endl;
}

输出为:

begin test_save_in_vector
shared_ptr default constructor
shared_ptr copy constructor
inc shared_count_ 2
p1 dump:
value = 3
shared_count_ = 2
end test_save_in_vector
shared_ptr destructor
dec shared_count_ 1
shared_ptr destructor
dec shared_count_ 0

可以正常保存到 std::vector 中。