凌云的博客

行胜于言

漫谈 C++ 智能指针 02

分类:c++| 发布时间:2015-09-18 23:10:00


更通用的智能指针

在上一节中,我们给出了一个智能指针的简单实现,但是这个实现有一个严重的问题, 它只能保存 some_type 类型的指针。 如何实现一个更加通用的智能指针呢? 通过使用模板可以简单地实现这一目的:

template<typename T>
class smart_ptr
{
public:
    explicit smart_ptr(T *p): p_(p) {}
    ~smart_ptr() {
        clean();
    }

    void reset(T *p) {
        if (p_ == p)
            return;

        clean();
        p_ = p;
    }

    void clean() {
        if (p_) {
            delete p_;
            p_ = 0;
        }
    }
private:
    T *p_;
};

bool some_func()
{
    smart_ptr<int> int_p;
    smart_ptr<some_type> some_type_p;
    {
        some_type *tmp = new some_type();
        if (!tmp)
            return false;

        some_type_p.reset(tmp);
    }

    {
        int *tmp = new int(3);
        if (!tmp)
            return false;

        int_p.reset(tmp);
    }
    // do something
    if (failed) {
        // Ok, it will delete auto
        return false;
    }

    return true;
}

可以看到,使用了模板之后 smart_ptr 可以保存任意类型的指针, 所需要做的改变只是在尖括号内给出指针的类型(如 auto_ptr 等标准实现一样)。

获取普通(裸)指针

在上面的例子中,通过我们实现的智能指针可以确保在函数结束时释放内存。 但上面的实现缺少一个非常重要的功能,先来看一个例子:

int *make_int()
{
    int *tmp = new int(3);
    if (!tmp)
        return 0;

    if (failed)
        return 0;

    return tmp;
}

bool some_func()
{
    smart_ptr<int> int_p(make_int());
    if (int_p is null)
        return false;

    // set int to 4
    // Oops: not way to do it
    return true;
}

在上面的例子中,我们想要将 4 赋给 int_p 指向的值。 却发现没有方法或者 int_p 包含的原始指针,我们需要添加一个方法以返回它的原始指针。

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

    T* get() {
        return p_;
    }
private:
    T *p_;
};

// see make_int above
bool some_func()
{
    smart_ptr<int> int_p(make_int());
    if (!int_p.get())
        return false;

    // set int to 4
    (*int_p.get()) = 4;
    return true;
}

我们添加了一个返回原始指针 get() 方法使得可以操作原始指针。 (事实上,在所有的智能指针实现中都会添加这么一个方法。)

放弃所有权

假设我们的函数 some_func 需要将 int_p 拥有的原始指针返回,我们应该怎么做呢? 直接通过 get() 获取原始指针然后返回?

int* some_func()
{
    smart_ptr<int> int_p(make_int());
    if (!int_p.get())
        return 0;

    // set int to 4
    (*int_p.get()) = 4;
    return int_p.get();
}

上述代码会不会有问题呢? 当 some_func 结束时,int_p 的生命周期也结束了,这是他的析构函数会 delete 掉它本身 所拥有的原始指针。因此,some_func 会返回一个无效的指针。

我们需要一个函数在返回原始指针的同时,放弃对原始指针的所有权。 这个函数通常被命名为 release() 。

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

    T* release() {
        T* tmp = p_;
        p_ = 0;
        return tmp;
    }
private:
    T *p_;
};

int* some_func()
{
    smart_ptr<int> int_p(make_int());
    if (!int_p.get())
        return 0;

    // set int to 4
    (*int_p.get()) = 4;
    return int_p.release();
}