聊聊 C++ 中的几种智能指针 (下)

一:背景

上一篇我们聊到了C++auto_ptr ,有朋友说已经在 C++ 17 中被弃用了,感谢朋友提醒,今天我们来聊一下 C++ 11 中引入的几个智能指针。

  1. unique_ptr
  2. shared_ptr
  3. weak_ptr

看看它们都怎么玩。

二:三大智能指针详解

1. unique_ptr

上一篇在说 auto_ptr 的时候聊过一个术语叫 控制权转移,言外之意就是下面的代码会 访问违例 。


void test() {
    int* i = new int(10);

    auto_ptr ptr1(i);
    auto_ptr ptr2 = ptr1;  //将 ptr1 的控制台送给了 ptr2

    cout << *ptr1 << endl;
}

int main() {
    test();
}

这段代码用了赋值运算符,可能是程序员的无心之举,却引来程序的崩溃,那能不能规避下这种 &#x65E0;&#x5FC3; 的行为呢? 当然是可以的,屏蔽掉 auto_ptr 中的赋值运算符不就可以了哈,C++ 中的 unique_ptr 就是这么实现的,可以看下源码。


class unique_ptr {

public:
    unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;
};

可以看到,unique_ptr 将 =&#xFF0C;&#x590D;&#x5236;&#x6784;&#x9020;&#x51FD;&#x6570; 都给屏蔽了,所以再硬写的话,必然是报错。

聊聊 C++ 中的几种智能指针 (下)

这个算是纯正的 &#x5F15;&#x7528;&#x8BA1;&#x6570;,高级语言中的 &#x5F15;&#x7528;&#x8DDF;&#x8E2A; 经常会拿它做比较,说它存在循环引用等各种问题。

在循环引用之前,先说下它的简单用法。


void test() {
    int* i = new int(10);
    shared_ptr ptr1(i);
    shared_ptr ptr2 = ptr1;
    shared_ptr ptr3 = ptr2;

    printf("ptr.refcnt= %d, ptr2.refcnt=%d, ptr3.refcnt=%d \n",
                                                   ptr1.use_count(),
                                                   ptr2.use_count(),
                                                   ptr3.use_count());
}

int main() {
    test();
}

聊聊 C++ 中的几种智能指针 (下)

再看下源码,大概在内部共同维护了对 _Ref_count_base 引用。


class _Ref_count_base {
private:
    _Atomic_counter_t _Uses = 1;
    _Atomic_counter_t _Weaks = 1;
}

很显然当所有的析构函数将 _Uses 降为 0 的时候就会对 i 进行释放,参考源码如下:


class shared_ptr : public _Ptr_base {
    ~shared_ptr() noexcept { // release resource
        this->_Decref();
    }
}

class _Ref_count_base {

    void _Decref() noexcept { // decrement use count
        if (_MT_DECR(_Uses) == 0) {
            _Destroy();
            _Decwref();
        }
    }
}

原理大概摸清楚了,接下来看下循环引用,它最大的问题就是让 _Uses 永远也不会为 0,


#include
#include

using namespace std;

class BClass;

class AClass
{
public:
    int i = 10;
    shared_ptr b;
};

class BClass
{
public:
    int i = 11;
    shared_ptr a;
};

void test() {

    AClass* a = new AClass();
    BClass* b = new BClass();

    shared_ptr aclass(a);
    shared_ptr bclass(b);

    aclass->b = bclass;
    bclass->a = aclass;

    printf("a.refcnt=%d, b.refcnt=%d, a=%x,b=%x \n", aclass.use_count(), bclass.use_count(), &(*a), &(*b));
}

int main() {
    test();
}

聊聊 C++ 中的几种智能指针 (下)

接下来可以在 main 方法结束的地方观察 a,b 所有的 heap 块的内存内容是否被释放? 可以清楚的看到还在那里。。. 如下图所示:

聊聊 C++ 中的几种智能指针 (下)

这就是 &#x5FAA;&#x73AF;&#x5F15;&#x7528; 造成的问题,这时候就需要用到 weak_ptr 了。

3. weak_ptr

弱引用就是它的引用不改变对原对象中 _Uses 的引用,接下来将 AClass 和 BClass 中的 shared_ptr 改成 weak_ptr 版。


class AClass
{
public:
    int i = 10;
    weak_ptr b;
};

class BClass
{
public:
    int i = 11;
    weak_ptr a;
};

聊聊 C++ 中的几种智能指针 (下)

从图中可以看到,此时 refcnt=1, 再观察下地址 0x007f71a8 的内容,可以发现已经被释放啦。

聊聊 C++ 中的几种智能指针 (下)

好了,今天我们就聊这么多,希望对大家有帮助。

Original: https://www.cnblogs.com/huangxincheng/p/16514090.html
Author: 一线码农
Title: 聊聊 C++ 中的几种智能指针 (下)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/536393/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • EclipseC++学习笔记-5 隐藏cmd窗口

    每次启动eclipse后,会有一个cmd窗口,很影响整洁解决办法示例:powershell.exe -WindowStyle Hidden -c wsl — /root…

    C++ 2023年5月29日
    085
  • 使用Javah 生成C/C++头文件

    注意:编写java的接口文件。 注意native代码端一定不要有大括号,且要有”;”结尾。 需要让eclipse自动编译生成class文件。 调整路径到cl…

    C++ 2023年5月29日
    057
  • 哈希表查找(散列表查找) c++实现HashMap

    算法思想: 哈希表 什么是哈希表 在前面讨论的各种结构(线性表、树等)中,记录在结构中的相对位置是随机的,和记录的关键字之间不存在确定的关系,因此,在结构中查找记录时需进行一系列和…

    C++ 2023年5月29日
    091
  • c++11新特性学习2

    noexcept 替代 throw。优点是更安全, 如果noexcept 修饰的函数抛出了异常,编辑器可以直接选择终止程序。 C++ 11中析构函数默认为noexcept(true…

    C++ 2023年5月29日
    059
  • [C++] const和mutable关键字使用方法

    const 修饰的变量为常成员变量,表示此变量不能被修改赋值,并且构造函数中只能用初始化列表的方式初始化,其他初始化方式都是错误的 const 修饰的函数为常成员函数,表示此函数中…

    C++ 2023年5月29日
    075
  • c++实践课(上课用)

    源程序: //编写一个程序,将从键盘输入的n个字符串保存在一个一维数组A中。在输入字符串之前,先输入n的值。//要求,数组A需要动态申请空间,程序运行结束前再释放掉。//再输出n字…

    C++ 2023年5月29日
    061
  • Visual C++ 运行库合集包完整版 v20201209

    Microsoft Visual C++ Redistributable(系统运行库,简称MSVC,VB/VC)是Windows操作系统应用程序的基础类型库组件。 此版Visual…

    C++ 2023年5月29日
    0103
  • c++类大四个默认函数-构造函数 析构函数 拷贝构造函数 赋值构造函数

    每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类A,如果不编写上述函数,C++编译器将自动为A 产生四个缺…

    C++ 2023年5月29日
    073
  • 生成1~n之间随机整数_c++

    rand() % (high – low + 1) + low; Original: https://www.cnblogs.com/douzujun/p/16457919.htm…

    C++ 2023年5月29日
    053
  • 【C++】自绘控件基础

    由于我们对控件的功能、外观的需求,公共控件并不能很好地满足这一点,所以我们就得自绘控件。 自绘控件有许多方法,比如: 处理WM_PAINT消息, 设置ownDraw风格,处理WM_…

    C++ 2023年5月29日
    047
  • C++ 借助指针突破访问权限的限制,访问private、protected属性的成员变量(花拳绣腿)

    #include <iostream> using namespace std; class A { public: A(int a, int b, int c); p…

    C++ 2023年5月29日
    076
  • std::get<C++11多线程库~线程间共享数据>(10):使用互斥量保护共享数据(4)

    1 #ifndef DEADLOCK_QUESTIONDESCRIBLE_AND_SOLUTION_H 2 #define DEADLOCK_QUESTIONDESCRIBLE_A…

    C++ 2023年5月29日
    068
  • 设置c++中cout输出的字体颜色

    一种方法是通过右键控制台进行颜色设置,但是这种方法的问题在于它是全局的,没有具体文字的区分。另外一种方法就是使用代码来修改,本文主要介绍的就是这种方法。 最重要的函数是SetCon…

    C++ 2023年5月29日
    065
  • C++ 内联函数解析(inline)

    inline是C++语言中的一个关键字,可以用于程序中定义内联函数,inline的引进使内联函数的定义更加简单。说到内联函数,这里给出比较常见的定义,内联函数是C++中的一种特殊函…

    C++ 2023年5月29日
    054
  • C++引用 学习心得

    参数的传递本质上是一次赋值的过程,赋值就是对内存进行拷贝。所谓内存拷贝,是指将一块内存上的数据复制到另一块内存上。 对于像 char、bool、int、float 等基本类型的数据…

    C++ 2023年5月29日
    078
  • C++ 求时差

    C++ 求时差的三种方法 【1】标准C库方式 示例代码 【2】C++库方式 C++11之后才引入 示例代码: 【3】windows API方式 示例代码: 为什么第14行,我们把G…

    C++ 2023年5月29日
    0109
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球