手写智能指针(smart_ptr)

1. 摘要

在这里插入图片描述请注意,“值类别”(value category)和“值类型”(value type)是两个看似相似、却毫不相干的术语.

2. 类中的构造函数

2.1. 普通构造函数Constructor
2.2. 拷贝构造函数Copy Constructor
2.3. 拷贝赋值运算符Copy Assignment operator
2.4. 移动构造函数Move Constructor
2.5. 移动赋值运算符Move Assignment operator
2.6. 析构函数~Destructor

3. 右值引用和移动语义

以前临时变量用完以后就消亡了, 一般都是左值=临时变量, 而C++11标准提出的右值引用, 相当于也给这种临时变量起了个名字a, 将临时变量的生存期延长到和a的生命期一样, 通过合理使用右值引用可以更好地提高资源利用率.

4. RALL

Bjarne Stroustrup 在C++导论中的介绍:

The technique of acquiring resources in a constructor and releasing them in a destructor, known as Resource Acquisition Is Initialization or RAII, allows us to eliminate ‘‘naked new operations,’’ that is, to avoid allocations in general code and keep them buried inside the implementation of well-behaved abstractions. Similarly, ‘‘naked delete operations’’ should be avoided. Avoiding naked new and naked delete makes code far less error-prone and far easier to keep free of resource leaks.
在这里插入图片描述

5. 智能指针实现(C++)

#include <iostream>
#include <vector>
#include <utility>  // std::swap
#include <memory>

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) {
            puts("1 (explicit) con___");
            shared_count_ = new shared_count();
        }
    }
    ~smart_ptr()
    {
        if (ptr_ && !shared_count_->reduce_count()) {
            delete ptr_;
            delete shared_count_;
            puts("delete ptr_ and count");
        }
    }

    smart_ptr(const smart_ptr& other)
    {
        ptr_ = other.ptr_;
        if (ptr_) {
            puts("2 (const smart_ptr& other) con___");
            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_) {
            puts("3 (const smart_ptr<U>& other) con___");
            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_) {
            puts("4 (const smart_ptr<U>& other, T* ptr) con___");
            other.shared_count_->add_count();
            shared_count_ = other.shared_count_;
        }
    }
    smart_ptr&
    operator=(smart_ptr rhs) noexcept
    {
        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;
        std::cout << "before swap look rhs.ptr_: " << rhs.ptr_ << std::endl;
        std::cout << "before swap look ptr_: " << ptr_ << std::endl;
        swap(ptr_, rhs.ptr_);
        std::cout << "after swap look rhs.ptr_: " << rhs.ptr_ << std::endl;
        std::cout << "after swap look ptr_: " << ptr_ << std::endl;
        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);
}




class shape {
public:
    virtual ~shape() {}
};

class circle : public shape {
public:
    ~circle() { puts("~circle()"); }
    void hello()
    {
        puts("hello world.");
    }

    void setS(float ss)
    {
        s_ = ss;
    }
    void getS()
    {
        std::cout << "the S is: " << s_ << std::endl;
    }

private:
    float s_;
};

class  Map
{
public:
    typedef smart_ptr<Map> Ptr;
    ~Map()
    {
        std::cout << "~Map()" << std::endl;
    }
};

int main()
{
    std::vector<int> tmp;
    tmp.push_back(1);
    std::cout << "the &tmp addr is: " << &tmp << std::endl;// in stack
    std::cout << "the tmp addr is: " << tmp.data() << std::endl;// in heap

    int *d_tmp = new int;
    std::cout << "&d_tmp addr is: " << &d_tmp << std::endl;
    std::cout << "d_tmp addr is: " << d_tmp << std::endl;
    delete d_tmp;

    smart_ptr<int> smt_tmp(new int);
    std::cout << "smt_tmp addr is: " << smt_tmp << std::endl;
    std::cout << "&smt_tmp addr is: " << &smt_tmp << std::endl;

    std::shared_ptr<int> shared_tmp(new int);
    std::cout << "shared_tmp addr is: " << shared_tmp << std::endl;
    std::cout << "&shared_tmp addr is: " << &shared_tmp << std::endl;

    puts("------------------------smart_ptr start---------------------------");
    {
        smart_ptr<circle> ptr1(new circle());
        std::cout << "&ptr1 is: " << &ptr1 << std::endl;
        std::cout << "ptr1 is: " << ptr1 << std::endl;
        printf("use count of ptr1 is %ld\n", ptr1.use_count());
        printf("the ptr1 addr is %p\n", ptr1.get());
        printf("the ptr1 addr is %p\n", ptr1);
        printf("the &ptr1 addr is %p\n", &ptr1);
        printf("the &ptr1 addr is 0x%X\n", &ptr1);

        smart_ptr<shape> ptr2;
        printf("use count of ptr2 was %ld\n", ptr2.use_count());
        printf("the ptr2 addr  was %p\n", ptr2.get());

        puts("11111");
        ptr2 = ptr1;
        puts("22222");
//        ptr2.operator=(ptr1);
        printf("use count of ptr1 is now %ld\n", ptr1.use_count());
        printf("use count of ptr2 is now %ld\n", ptr2.use_count());
        printf("the ptr1 addr is now %p\n", ptr1.get());
        printf("the ptr2 addr is now %p\n", ptr2.get());

        smart_ptr<shape> ptr3(ptr2);
        printf("use count of ptr1 is now now: %ld\n", ptr1.use_count());
        printf("use count of ptr2 is now now: %ld\n", ptr2.use_count());
        printf("use count of ptr3 is now now: %ld\n", ptr3.use_count());
        printf("the ptr2 addr is now now %p\n", ptr2.get());
        printf("the ptr3 addr is now now %p\n", ptr3.get());

//    ptr3 = reinterpret_pointer_cast<circle>(ptr2);

        if (ptr1) {
            puts("ptr1 is not empty");
        } else {
            puts("ptr1 is empty");
        }
        if (ptr3) {
            puts("ptr3 is not empty");
        }

        smart_ptr<shape> ptr4(new circle());
        std::cout << "the ptr4 count is: " << ptr4.use_count() << std::endl;

        Map::Ptr map_ptr(new Map);
    }
    puts("------------------------smart_ptr end---------------------------");


    // 如何解决下面double free的问题:
//    int test[10];
    std::shared_ptr<int> test_ptr(test); // 都存在double free的问题
//    smart_ptr<int> test_ptr(test);
//    std::cout << "the count of test_ptr is: " << test_ptr.use_count() << std::endl;

//    // 下面也会出现double free
//    int* test;
//    std::cout << test << " " << std::endl;
//    delete test;

    circle* circle_test;
    std::cout << "*******" << circle_test << std::endl;
//    delete circle_test;

    {
        using namespace std;
        auto sp1 = make_shared<std::string>("obj1");
        printf("use count of sp1 is %ld\n", sp1.use_count());
        auto sp2 = make_shared<std::string>("obj2");
        printf("use count of sp2 is now %ld\n", sp2.use_count());
        { sp1 = sp2; }
        { sp1 = sp2; }
        sp1 = sp2;
        printf("use count of sp1 is now now %ld\n", sp1.use_count());
        printf("use count of sp2 is now now %ld\n", sp2.use_count());

        auto up1 = std::unique_ptr<int>(new int(4));
//        auto up2 = up1; // error, unique_ptr没有赋值和拷贝构造函数,这样就可以保证一个对象只能被单个unique_ptr拥有
//        auto up3(up1); // error
    }


    return 1;
}

100. 参考资料

[1]. C++中类型强制转换官网说明
[2]. [c++11]我理解的右值引用、移动语义和完美转发
[3]. 拷贝构造函数和移动构造函数
[4]. 右值引用那些事儿

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值