C++ 智能指针

2 阅读3分钟

概述

使用智能指针的目的之一,就是减少对象的拷贝。要禁止拷贝和赋值

智能指针代码1


template <typename T>

class smart_ptr {

…

    smart_ptr(smart_ptr& other)

    {

        ptr_ = other.release();

    }

    smart_ptr& operator=(smart_ptr& rhs)

    {

        smart_ptr(rhs).swap(*this);

        return *this;

    }


    T* release()

    {

        T* ptr = ptr_;

        ptr_ = nullptr;

        return ptr;

    }

    void swap(smart_ptr& rhs)

    {

        using std::swap;

        swap(ptr_, rhs.ptr_);

    }

…

};

在拷贝构造函数中,通过调用 other 的 release 方法来释放它对指针的所有权

在赋值函数中,则通过拷贝构造产生一个临时对象并调用 swap 来交换对指针的所有权

同时,这里一个大问题。如果不小心other把它传递给另一个smart_ptr,就不再拥有这个对象

“移动”指针


template <typename T>

class smart_ptr {

…

    smart_ptr(smart_ptr&& other)

    {

        ptr_ = other.release();

    }

    smart_ptr& operator=(smart_ptr rhs)

    {

        rhs.swap(*this);

        return *this;

    }

…

};

把拷贝构造函数中的参数类型 smart_ptr& 改成了 smart_ptr&&;现在它成了移动构造函数

  • 输入一个即将销毁的右值,这样转移指针所有权完成移动

把赋值函数中的参数类型 smart_ptr& 改成了 smart_ptr,在构造参数时直接生成新的智能指针,从而不再需要在函数体中构造临时对象

现在赋值函数的行为是移动还是拷贝,完全依赖于构造参数时走的是移动构造还是拷贝构造

  • operator=()的参数在接收参数的时候,会调用构造函数,如果调用的是拷贝构造,那赋值操作就是拷贝

  • 如果调用的是移动构造,那么赋值操作就是移动

根据 C++ 的规则,如果我提供了移动构造函数而没有手动提供拷贝构造函数,那后者自动被禁用

通用构造函数


template <typename U>

smart_ptr(smart_ptr<U>&& other)

{

    ptr_ = other.release();

}

增加新的构造函数,但是这个构造函数不被编译器看成移动构造函数,因而不能自动触发删除拷贝构造函数行为

详细代码


#include <utility> // std::swap

class shared_count {

public:

    shared_count() noexcept

    : count_(1) {}

    void add_count() noexcept

    {

        ++count_;

    }

    long reduce_count() noexcept

    {

        return --count_;

    }

    long get_count() const noexcept

    {

        return count_;

    }

private:

        long count_;

};
    

template <typename T>

class smart_ptr {

    public:

    template <typename U>

    friend class smart_ptr;

    explicit smart_ptr(T* ptr = nullptr)

    : ptr_(ptr)

    {

        if (ptr) {

        shared_count_ =

        new shared_count();

    }

    }

    ~smart_ptr()

    {

        if (ptr_ &&

        !shared_count_

        ->reduce_count()) {

        delete ptr_;

        delete shared_count_;

    }

    }

    smart_ptr(const smart_ptr& other)

    {

        ptr_ = other.ptr_;

        if (ptr_) {

        other.shared_count_

        ->add_count();

        shared_count_ =

        other.shared_count_;

    }

    }

    template <typename U>

    smart_ptr(const smart_ptr<U>& other) noexcept

    {

        ptr_ = other.ptr_;

        if (ptr_) {

            other.shared_count_->add_count();

            shared_count_ = other.shared_count_;

        }

    }

    template <typename U>

    smart_ptr(smart_ptr<U>&& other) noexcept

    {

        ptr_ = other.ptr_;

        if (ptr_) {

        shared_count_ =

        other.shared_count_;

        other.ptr_ = nullptr;

    }

    }

    template <typename U>

    smart_ptr(const smart_ptr<U>& other,

    T* ptr) noexcept

    {

        ptr_ = ptr;

        if (ptr_) {

        other.shared_count_

        ->add_count();

        shared_count_ =

        other.shared_count_;

    }

    }

    smart_ptr&

    operator=(smart_ptr rhs) noexcept

    {

        // 掏空rhs。this原本持有的内容立即释放

        // @todo

        rhs.swap(*this);

        return *this;

    }

    T* get() const noexcept

    {

        return ptr_;

    }

    long use_count() const noexcept

    {

        if (ptr_) {

            return shared_count_

            ->get_count();

            } else {

            return 0;

        }

    }

    void swap(smart_ptr& rhs) noexcept

    {

        using std::swap;

        swap(ptr_, rhs.ptr_);

        swap(shared_count_,

        rhs.shared_count_);

    }

    T& operator*() const noexcept

    {

        return *ptr_;

    }

    T* operator->() const noexcept

    {

        return ptr_;

    }

    operator bool() const noexcept

    {

        return ptr_;

    }

    private:

    T* ptr_;

    shared_count* shared_count_;

};

template <typename T>

void swap(smart_ptr<T>& lhs,

smart_ptr<T>& rhs) noexcept

{

    lhs.swap(rhs);

}

template <typename T, typename U>

smart_ptr<T> static_pointer_cast(

const smart_ptr<U>& other) noexcept

{

    T* ptr = static_cast<T*>(other.get());

    return smart_ptr<T>(other, ptr);

}

template <typename T, typename U>

smart_ptr<T> reinterpret_pointer_cast(

const smart_ptr<U>& other) noexcept

{

    T* ptr = reinterpret_cast<T*>(other.get());

    return smart_ptr<T>(other, ptr);

}

template <typename T, typename U>

smart_ptr<T> const_pointer_cast(

const smart_ptr<U>& other) noexcept

{

    T* ptr = const_cast<T*>(other.get());

    return smart_ptr<T>(other, ptr);

}

template <typename T, typename U>

smart_ptr<T> dynamic_pointer_cast(

const smart_ptr<U>& other) noexcept

{

    T* ptr = dynamic_cast<T*>(other.get());

    return smart_ptr<T>(other, ptr);

}