当前位置:   article > 正文

C++ 11智能指针详解和使用_c++ 指针11

c++ 指针11


0 引入

  • C++ 程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。但使用普通指针,容易造成内存泄露(忘记释放)、二次释放、程序发生异常时内存泄露等问题等。所以 C++11 就引入了智能指针。

  • C 语言中最常使用的是malloc()函数分配内存,free()函数释放内存,而 C++ 中对应的是new、delete关键字。malloc()只是分配了内存,而new则更进一步,不仅分配了内存,还调用了构造函数进行初始化。


1、定义

C++11 中引入了智能指针(Smart Pointer):它利用了一种叫做 RAII(资源获取即初始化)的技术将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。这使得智能指针实质是一个对象,行为表现的却像一个指针。

智能指针主要分为shared_ptr、unique_ptr和weak_ptr三种,使用时需要引用头文件。C++98 中还有auto_ptr,基本被淘汰了,不推荐使用。而 C++11 中shared_ptr和weak_ptr都是参考boost库实现的


2、shared_ptr

shared_ptr是使用最多的一种。多个智能指针可以指向同一个对象,即实现了资源的多指针共享。shared_ptr引入了计数机制,可以通过use_count成员函数查看资源所有者的个数。当一个shared_ptr指向该对象,则计数加一,当一个shared_ptr释放,计数减一。当最后一个指针释放,就释放内存。

1.智能指针实现

代码如下(示例):

include<mutex>

template<typename T>
class Shared_ptr {
private:
    // 成员变量
    T *ptr_{nullptr};
    int *ref_count_{nullptr};
    std::mutex *mutex_{nullptr};
public:
    /******************** 构造函数和析构函数 ********************/
    // 默认构造函数
    constexpr Shared_ptr() noexcept = default;

    // 普通构造函数
    explicit Shared_ptr(T *ptr) : ptr_{ptr} {
        if (this->ptr_ != nullptr) {
            this->ref_count_ = new int{1};
            this->mutex_ = new std::mutex{};
        }
    }
    // 拷贝构造函数
    Shared_ptr(const Shared_ptr &rhs) noexcept: ptr_{rhs.ptr_}, ref_count_{rhs.ref_count_}, mutex_{rhs.mutex_} {
        if (this->ptr_ != nullptr) {
            this->addRefCount();
        }
    }

    // 移动构造函数
    Shared_ptr(Shared_ptr &&rhs) noexcept: ptr_{rhs.ptr_}, ref_count_{rhs.ref_count_}, mutex_{rhs.mutex_} {
        // right hand side is nullptr
        rhs.ptr_ = nullptr;
        rhs.ref_count_ = nullptr;
        rhs.mutex_ = nullptr;
    }

    // 析构函数
    ~Shared_ptr() noexcept {
        this->decrRefCount();
    }

    /******************** 运算符重载 ********************/
    // 拷贝赋值运算符
    Shared_ptr &operator=(const Shared_ptr &rhs) {
        Shared_ptr{rhs}.swap(*this);
        return *this;
    }

    // - move 本意为 "移动",但该函数并不能移动任何数据,它的功能很简单,就是将某个左值强制转化为右值。
    // - forward 完美转发,根据右值判断的推倒,调用forward 传出的值,若原来是一个右值,那么他转出来就是一个右值,否则为一个左值。这样的处理就完美的转发了原有参数的左右值属性,不会造成一些不必要的拷贝
    
    // 移动赋值运算符
    Shared_ptr &operator=(Shared_ptr &&rhs) noexcept {
        Shared_ptr{std::move(rhs)}.swap(*this);
        return *this;
    }

    // 取值
    T &operator*() const noexcept {
        return *this->ptr_;
    }

    // 取指针
    T &operator->() const noexcept {
        return this->ptr_;
    }

    // 到 bool 的隐式转换
    explicit operator bool() const noexcept {
        return static_cast<bool>(ptr_);
    }

    /******************** 成员函数 ********************/
    // 获取指针
    T *get() const noexcept {
        return ptr_;
    }

    // 获取引用计数
    int use_count() const noexcept {
        return this->ref_count_ == nullptr ? 0 : *this->ref_count_;
    }

    // 引用计数是否唯一
    bool unique() const noexcept {
        return *this->ref_count_ == 1;
    }

    // 重置指针
    void reset() noexcept {
        Shared_ptr{}.swap(*this);
    }

    // 重置指针
    void reset(T *ptr) {
        Shared_ptr{ptr}.swap(*this);
    }

    // swap函数
    void swap(Shared_ptr &rhs) noexcept {
        std::swap(this->ptr_, rhs.ptr_);
        std::swap(this->ref_count_, rhs.ref_count_);
        std::swap(this->mutex_, rhs.mutex_);
    }

private:
    // 增加引用计数
    void addRefCount() {
        mutex_->lock();
        ++(*ref_count_);
        mutex_->unlock();
    }

    // 减少引用计数,如果为零,则释放指针
    void decrRefCount() {
        bool deleteflag = false;
        mutex_->lock();
        if (--(*ref_count_) == 0) {
            delete ptr_;
            delete ref_count_;
            deleteflag = true;
        }
        mutex_->unlock();
        if (deleteflag) {
            delete mutex_;
        }
    }
};

// 函数模板
template<typename T>
auto make_Shared(T v) {
    return Shared_ptr<T>{new T(v)};
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135

在这里插入图片描述

2.常见使用场景

2.1基本使用方法

代码如下(示例):

	    // 初始化
        shared_ptr<int> p1 = make_shared<int>(42);
        shared_ptr<int> p2(new int(1024));
        //上面用构造函数创建,等价于
        //int* ptr = new int(1024);
        // std::shared_ptr<int> p1(ptr);
        // std::shared_ptr<int> p2(ptr);//错误 不能同时赋值给多个智能指针
        shared_ptr<int> p3 = p2;
        // 获取值
        cout << p1.get() << endl;
        cout << *p1.get() << endl;
        cout << *p1 << endl;
        // 引用计数
        cout << p1.use_count() << endl;
        cout << p2.use_count() << endl;
        cout << p3.use_count() << endl;
        // 引用计数是否唯一
        cout << p1.unique() << endl;
        // 重置
        p1.reset(new int(322));
        // swap
        p1.swap(p2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 同一普通指针不能同时为多个 shared_ptr 对象赋值,否则会导致程序发生异常。

2.2大量复杂数据结构在多处引用

在大量数据转移时候,有时候可能转几手,自己都忘记如何释放,或者在不同线程中,之前的做法为了小心翼翼的避免内存泄露,都要一一对应,现在只需要用智能指针去处理,不用担心释放问题,(实际过程中需要避免循环引用)
代码如下(示例):

 //暂无,没有想好怎么表达
  • 1

3、weak_ptr

弱指针主要是为了避免shared_ptr循环引用问题:当两个对象相互使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

当出现循环引用时,把其中一个shared_ptr转化成weak_ptr即可解决问题。

		shared_ptr<int> p = make_shared<int>(100);
        weak_ptr<int> w(p);    // 与p指向相同对象的weak_ptr,
        w = p;                 // p可以是shared_ptr或者weak_ptr,赋值后w和p共享对象
        w.reset();             // weak_ptr置为空
        w.use_count();         // 与w共享对象的shared_ptr的计数
        w.expired();           // w.use_count()为0则返回true,否则返回false
        w.lock();              // w.expired()为true,返回空的shared_ptr;否则返回指向w的shared_ptr

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4、unique_ptr

unique_ptr相对于其他两个智能指针更加简单,它和shared_ptr使用差不多,但是功能更为单一,它是一个独占型的智能指针,不允许其他的智能指针共享其内部的指针,更像原生的指针(但更为安全,能够自己释放内存)。不允许赋值和拷贝操作,只能够移动


5、总结和引用

5.1 缺点

使用智能指针虽然能够解决内存泄漏问题,但是也付出了一定的代价。以shared_ptr举例:

  • shared_ptr的大小是原始指针的两倍,因为它的内部有一个原始指针指向资源,同时有个指针指向引用计数。
  • 引用计数的内存必须动态分配。虽然一点可以使用make_shared()来避免,但也存在一些情况下不能够使用make_shared()。
  • 增加和减小引用计数必须是原子操作,因为可能会有读写操作在不同的线程中同时发生。

5.2 引用

1、c++11智能指针解析——揭开底层面纱,完整理解智能指针
2、C++11 智能指针【详解+实现】【面试常考】


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/804271
推荐阅读
相关标签
  

闽ICP备14008679号