聊聊 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)

大家都在看

  • [转] C++ STL中map.erase(it++)用法原理解析

    总结一下map::erase的正确用法。首先看一下在循环中使用vector::erase时我习惯的用法: 这一种用法是没有问题的。 如上所示,C++98中map::erase并没有…

    C++ 2023年5月29日
    077
  • 【C++服务端技术】对象池

    代码没贴全,就少一个锁头文件,可以做设计参考 设计思想就是维护一个空闲链表,没有用的就重新申请,有的话就拿链表的头,使用完又还给空闲链表。 /* 一个分配固定大小内存的内存池,使用…

    C++ 2023年5月29日
    076
  • C++常用的设计模式

    单例模式: 单例模式:确保一个类只有一个实例,并且这个实例化向整个系统提供 (例如只有一台打印机,可以有多个打印任务队列,但是只能有一个正在打印)。单例模式又分为(饿汉模式,懒汉模…

    C++ 2023年5月29日
    053
  • 聊聊 C++ 和 C# 中的 lambda 玩法

    这几天在看 C++ 的 lambda 表达式,挺有意思,这个标准是在 C11&#x6807;&#x51C6; 加进去的,也就是 2011 年,相比 C# 2007 …

    C++ 2023年5月29日
    064
  • C++ lambda 用法

    为什么要使用lambda 就地匿名的定义一个目标函数或者函数对象,不需要额外的再写一个命名函数或者函数对象,以更直接的方式去写函数,可以调高程序的可读性和可维护性。 简洁:不要额外…

    C++ 2023年5月29日
    073
  • C++ register 关键字

    register 简介: register 就像是汉语和英语中的形容词(不要问为什么只有两种语言,因为小编只会这两种),在 C++ 中 register 是用来修饰变量的。 reg…

    C++ 2023年5月29日
    056
  • C++ #ifndef、#define、#endif作用

    在C++项目中,#ifndef、#define、#endif非常常见,接下来就来简单说一下它们的作用。 作用:防止头文件被重复引用,防止被重复编译。 简介: ifndef 它是if…

    C++ 2023年5月29日
    090
  • C++ 内存池 — C++ Memory Pool

    这是我翻译的文章,来自 Code Project, 原文作者: DanDanger2000. 原文链接: http://www.codeproject.com/cpp/Memory…

    C++ 2023年5月29日
    086
  • C++面试题1

    1,LeetCode给出一个 32 位的有符号整数,将这个整数中每位上的数字进行反转; 2,怎么判断一个变量是指针; Original: https://www.cnblogs.c…

    C++ 2023年5月29日
    060
  • 【C/C++】输出文件的相对路径

    FILE 在有些编译器上会输出带路径的文件名,删除路径可使用函数 strrchr #include #include #define __FILENAME__ (strrchr(_…

    C++ 2023年5月29日
    070
  • 拓扑排序(二)之 C++详解

    拓扑排序(Topological Order)是指,将一个有向无环图(Directed Acyclic Graph简称DAG)进行排序进而得到一个有序的线性序列。 这样说,可能理解…

    C++ 2023年5月29日
    053
  • C++深拷贝与浅拷贝

    对于普通类型的对象来说,它们之间的复制是很简单的,例如:int a=88;int b=a;而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。下面看一个类对象拷贝…

    C++ 2023年5月29日
    045
  • CLion之C++框架篇-优化框架,引入boost(三)

    背景 结合上一篇CLion之C++框架篇-优化框架,单元测试(二) ,继续进行框架优化!这一版优化引入一个我们日常经常使用的操作库Boost,估算使用频率在70%以上! Boost…

    C++ 2023年5月29日
    084
  • Xcode 导出C++项目在其他电脑执行

    先找到C++项目的可执行文件的位置 https://blog.csdn.net/qq_34759481/article/details/82700587 关于存储和加载文件的目录,…

    C++ 2023年5月29日
    062
  • windows下使用vscode编写运行以及调试C/C++

    c++本地运行和debughttps://www.cnblogs.com/TAMING/p/8560253.html 运行C++/C代码 在终端运行 文件读入https://blo…

    C++ 2023年5月29日
    056
  • C++调用C#的动态库dll

    以往我们经常是需要使用C#来调用C++的dll,这通过PInvoke就能实现。现在在实际的项目过程中,有时会遇到在C++的项目中调用某个C#的dll来完成特定的某个功能,我们都知道…

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