通过对比理解C++智能指针

news2024/11/15 12:36:53

理论

  • 概述

    • 智能指针:把管理资源的责任交给了对象,这样我们在使用结束时不需要显式地释放资源,而是由析构函数自动完成这一工作

    • 它是一个类模板,可以创建任意类型的指针对象

    • 智能指针使用时,资源对象不能被重复释放,否则会导致未定义的行为

  • C++中的智能指针关系

    智能指针简述
    auto_ptr存在管理权转移问题,不适合共享资源,已被C++11弃用
    unique_ptr暴力处理auto_ptr的管理权转移问题,独占资源所有权,不允许拷贝和赋值操作(开销小)
    shared_ptr使用引用计数优雅解决auto_ptr的问题,但带来了循环引用bug(开销大)
    weak_ptr专注给shared_ptr打补丁,不增加引用计数
  • auto_ptr

    • 不足

      管理权转移,原对象拷贝给新对象时,原对象被置为nullptr,此时只有新对象指向这块资源空间

      此时如果再使用原来的对象,那么会出现未定义的行为(资源不能被重复释放)

  • unique_ptr

    • 原理

      针对auto_ptr在拷贝、赋值操作中的管理权转移问题,unique_ptr直接禁用了拷贝和赋值操作

      这样自然就避免了重复释放资源

    • 不足

      不能进行拷贝和赋值操作

      可以使用move把左值转成右值进行赋值操作,此时效果和auto_ptr一样

      move是一种将对象转变为右值引用的方法:表示将放弃当前对象的所有权,将所有权转移到另一个对象,由新对象接管所有权

  • shared_ptr

    • 原理

      为了进行拷贝和赋值操作,shared_ptr采用了引用计数原理,记录有多少shared_ptr实例指向同一资源

      有几个实例对象计数值就是几,调用析构函数会将计数值减1,当计数为0时,才会释放资源

      引用计数指向的对象在堆上,保证了所有线程都能够访问该资源;

      shared_ptr引用计数通过原子操作实现,因此引用计数本身是线程安全的

      但是,如果多线程尝试修改引用计数时,需要用户手动进行同步、加锁等操作以确保对资源的访问是安全的

    • 不足

      存在循环引用问题,导致引用计数不能变成0

      两个类对象,二者通过shared_ptr智能指针互相引用,会增加引用计数

  • weak_ptr

    • 原理

      weak_ptr对象可以指向shared_ptr,但不会改变shared_ptr的引用计数

    • 不足

      仅用于解决shared_ptr的循环引用问题,它不能直接访问资源



实验

auto_ptr管理权转移

// auto_ptr管理权转移
#include <iostream>
#include <memory>
using namespace std;

class Test {
public:
    Test(const string &name):obj_name(name){ 
		std::cout << obj_name << "——构造函数.\n"; 
		}
    ~Test(){ 
		std::cout << obj_name << "——析构函数.\n"; 
		}
private:
	string obj_name;
};

int main() {
    auto_ptr<Test> obj1(new Test("obj1")); 
	auto_ptr<Test> obj2(new Test("obj2"));
	cout << "obj1 address = " << obj1.get() << endl;
	cout << "obj2 address = " << obj2.get() << endl;
	cout << "\n执行 obj2 = obj1\n" << endl;
	obj2 = obj1;
	cout << "obj1 address = " << obj1.get() << endl;
	cout << "obj2 address = " << obj2.get() << endl;
    return 0;
}
image-20240915171327248

unique_ptr独占资源

// unique_ptr独占资源
#include <iostream>
#include <memory>
using namespace std;

class Test {
public:
    Test(const string &name):obj_name(name){ 
		cout << obj_name << "——构造函数.\n"; 
	}
    ~Test(){ 
		cout << obj_name << "——析构函数.\n"; 
	}
private:
	string obj_name;
};

int main() {
    unique_ptr<Test> obj1(new Test("obj1")); 
    unique_ptr<Test> obj2(new Test("obj2"));
    cout << "obj1 address = " << obj1.get() << endl;
    cout << "obj2 address = " << obj2.get() << endl;
    cout << "\n执行 obj2 = move(obj1)\n" << endl;
    // obj2 = obj1;
	obj2 = move(obj1);
    cout << "obj1 address = " << obj1.get() << endl;
    cout << "obj2 address = " << obj2.get() << endl;
    return 0;
}
image-20240915171846314

shared_ptr共享资源

// shared_ptr共享资源
#include <iostream>
#include <memory>
using namespace std;

class Test {
public:
    Test(const string &name):obj_name(name){ 
		cout << obj_name << "——构造函数.\n"; 
	}
    ~Test(){ 
		cout << obj_name << "——析构函数.\n"; 
	}
private:
	string obj_name;
};

int main() {
    shared_ptr<Test> obj1(new Test("obj1")); 
    shared_ptr<Test> obj2(new Test("obj2"));
    cout << "obj1 address = " << obj1.get() << endl;
    cout << "obj2 address = " << obj2.get() << endl;
    cout << "\n执行 obj2 = obj1\n" << endl;
    obj2 = obj1;
    cout << "obj1 address = " << obj1.get() << endl;
    cout << "obj2 address = " << obj2.get() << endl;
    return 0;
}
image-20240915172140224

shared_ptr循环引用

// shared_ptr循环引用
#include <iostream>
#include <memory>
using namespace std;

class A;

class B {
public:
    shared_ptr<A> a_ptr;
    B() { cout << "B类 构造函数" << endl; }
    ~B() { cout << "B类 析构函数" << endl; }
};

class A {
public:
    shared_ptr<B> b_ptr;
    A() { cout << "A类 构造函数" << endl; }
    ~A() { cout << "A类 析构函数" << endl; }
};

int main() {
    // shared_ptr<A> a = make_shared<A>();
    // shared_ptr<B> b = make_shared<B>();
	shared_ptr<A> a(new A());
	shared_ptr<B> b(new B());

    a->b_ptr = b;  // A 指向 B
    b->a_ptr = a;  // B 指向 A

    // 循环引用导致对象不能被正确销毁

    cout << "a use_count: " << a.use_count() << endl;
    cout << "b use_count: " << b.use_count() << endl;

    return 0;
}
image-20240915173108183

weak_ptr解决循环引用

// weak_ptr解决循环引用
#include <iostream>
#include <memory>
using namespace std;

class A;  // 前向声明

class B {
public:
    weak_ptr<A> a_ptr;  // 使用 weak_ptr 来避免循环引用
    B() { cout << "B类 构造函数" << endl; }
    ~B() { cout << "B类 析构函数" << endl; }
};

class A {
public:
    // shared_ptr<B> b_ptr;
	weak_ptr<B> b_ptr;
    A() { cout << "A类 构造函数" << endl; }
    ~A() { cout << "A类 析构函数" << endl; }
};

int main() {
    // shared_ptr<A> a = make_shared<A>();
    // shared_ptr<B> b = make_shared<B>();
    shared_ptr<A> a(new A());
    shared_ptr<B> b(new B());

    a->b_ptr = b;
    b->a_ptr = a; 

    cout << "a use_count: " << a.use_count() << endl;
    cout << "b use_count: " << b.use_count() << endl;

    return 0;
}
image-20240915173621393

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2139025.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【CSS|第1期】网页设计的演变:从表格布局到Grid布局

日期&#xff1a;2024年9月9日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉在这里插入代码片得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对…

调用系统的录音设备提示:line with format PCM_SIGNED 16000.0 Hz

javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 16000.0 Hz, 8 bit, mono, 1 bytes/frame, not supported. 打开 设置->隐私->麦克风->允许应用访问你的麦克风 与 16000Hz没关系 与 16000Hz没关系 与 16000Hz没关系

【iOS】dismiss多级的方法

前言 上次笔者总结过push和pop推入和推出界面的方法&#xff0c;这里对于dismiss多级的方法进行一个总结&#xff0c;推入推出方法可以看看笔者这篇博客&#xff1a;【iOS】UI学习——界面切换 dismiss推出多级的原理 当我们使用pop推入新的界面的时候&#xff0c;连续pop推…

复杂情感识别系统

复杂情感识别系统&#xff08;CERS&#xff09;是一种先进的技术平台&#xff0c;旨在通过分析情感的组合、相互关系及其动态变化来解读和识别复杂的情感状态。这种系统通常采用以下技术和方法&#xff1a; 机器学习与深度学习&#xff1a; 通过训练算法识别和解释大量情感数据…

从汇编语言到高级语言:人类计算机科学的伟大探索

从20世纪中叶第一台电子计算机的诞生&#xff0c;到如今的智能设备遍布全球&#xff0c;计算机科学的发展历程是一部充满着人类探索精神的伟大史诗。计算机语言作为人类与机器交流的桥梁&#xff0c;见证并推动了这一切。从最早的汇编语言到如今多样的高级语言&#xff0c;我们…

视频监控摄像头国标GB28181配置参数逐条解析

转载&#xff1a;视频监控摄像头国标GB28181配置参数逐条解析 现在的很多信息化项目&#xff0c;都会涉及到国标GB28181的视频监控产品&#xff0c;当我们配置这些国标平台&#xff0c;录像机&#xff0c;摄像头时&#xff0c;如果对相关参数的定义不清楚的话&#xff0c;会给我…

【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树

在安卓源码的设计中&#xff0c;将将屏幕分为了37层&#xff0c;不同的窗口将在不同的层级中显示。 对这一块的概念以及相关源码做了详细分析&#xff0c;整理出以下几篇。 【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树 【Android 13源码分析】WindowCon…

Redis基础数据结构之 quicklist 和 listpack 源码解读

目录标题 quicklist为什么要设计 quicklist&#xff1f;quicklist特点ziplist quicklist数据结构 listpacklistpack是什么&#xff1f;listpack数据结构ziplist干啥去了&#xff1f;为什么有listpack?什么是ziplist的连锁更新&#xff1f;listpack 如何避免连锁更新&#xff1…

从ANN到SNN的转换:实现、原理及两种归一化方法【MINIST、实战】

从ANN到SNN的转换&#xff1a;实现、原理及两种归一化方法 引言 随着神经形态计算的迅猛发展&#xff0c;脉冲神经网络&#xff08;Spiking Neural Networks, SNNs&#xff09;作为一种仿生神经计算模型&#xff0c;逐渐展现出其在低功耗和事件驱动计算领域的巨大潜力。不同于…

8.5LoG算子边缘检测

LoG的基本概念 LoG&#xff08;Laplacian of Gaussian&#xff09;算子是一种结合了高斯模糊和平滑处理的边缘检测方法。它通过先对图像应用高斯滤波器来去除噪声&#xff0c;然后再对结果应用拉普拉斯算子来检测边缘。LoG算子的主要优点是可以检测图像中的边缘和其他重要特征…

MPICH 源码编译 with ucx with cuda,应用示例

先基于 cuda 编译ucx 再基于 ucx 编译 mpich mkdir mpich mkdir ucx 1, 安装 ucx 预备环境&#xff1a; sudo apt-get install valgrind sudo apt-get install libibverbs-dev librdmacm-dev 下载ucx 源代码 git clone --recursive https://github.com/openucx/ucx.git cd…

堆排序,快速排序

目录 1.堆排序 2.快速排序 1.hoare版本 2.挖坑法 3.前后指针法 注意点 1.堆排序 void Swap(int* a, int* b) {int tmp *a;*a *b;*b tmp; } void adjustdown(int* a, int n, int parent) {int child parent * 2 1;while (child < n){if (child 1 < n &&am…

【Python基础】Python lambda(简洁与高效的匿名函数)

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程基础知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、lambda函数的基本概念三、lambda函数的应用实例3.1 在列表排序中使用lambda函数3.2 在map()函数中…

(批处理)设置延时+设置关机倒计时

使用方式&#xff1a;建立一个文本文件夹&#xff0c;将文件扩展名改为.bat&#xff0c;右键单击后编辑&#xff0c;将代码复制进去。 将文件保存 echo off echo 三秒后会出现一个提示自动关机ping -n 3 127.0.0.1 >nul rem 实现的功能是在这里停3秒再继续往下执行 rem 以…

OpenCore Legacy Patcher 2.0.0 发布,83 款不受支持的 Mac 机型将能运行最新的 macOS Sequoia

在不受支持的 Mac 上安装 macOS Sequoia (OpenCore Legacy Patcher v2.0.0) Install macOS on unsupported Macs 请访问原文链接&#xff1a;https://sysin.org/blog/install-macos-on-unsupported-mac/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主…

【Linux】多路转接epoll

一、I/O多路转接 poll 1.1 poll函数接口 函数原型 函数参数 fds&#xff1a;是一个poll函数监听的结构列表&#xff0c;每一个元素中包含了三部分内容&#xff1a;文件描述符&#xff0c;监听的事件集合&#xff0c;返回的事件集合。nfds&#xff1a;表示的是fds数组的长度tim…

VUE + NODE 历史版本安装

以node 12.20.0为例子&#xff0c;想下载哪个版本&#xff0c;后面写哪个版本 https://registry.npmmirror.com/binary.html?pathnode/v12.20.0/ 安装国内镜像7.1.0 cnpm npm install -g cnpm7.1.0 -g --registryhttps://registry.npmmirror.com 安装vue脚手架4.5.15 cnpm …

【有啥问啥】深入浅出马尔可夫链蒙特卡罗(Markov Chain Monte Carlo, MCMC)算法

深入浅出马尔可夫链蒙特卡罗&#xff08;Markov Chain Monte Carlo, MCMC&#xff09;算法 0. 引言 Markov Chain Monte Carlo&#xff08;MCMC&#xff09;是一类用于从复杂分布中采样的强大算法&#xff0c;特别是在难以直接计算分布的情况下。它广泛应用于统计学、机器学习…

【linux基础】linux中的开发工具(4)--调试器gdb的使用

目录 前言一&#xff0c;背景二&#xff0c;gdb的使用1. 启动 gdb 调试器&#xff1a;2. 罗列代码信息3. 运行程序4. 有关断点的操作(1) 打断点(2) 查看断点(3) 删除断点(4) 在一次调试中&#xff0c;断点是递增的(5) 关闭断点(6) 开启断点(7) 逐过程调试&#xff0c;相当于 F1…

我与Linux的爱恋:进程|进程的查看与管理|创建进程

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;Linux的学习 ​ 文章目录 一、进程的概念1.什么是进程2.在这里插入代码片多进程管理3.描述进程-PCB 2.查看进程与管理进程1.使用指令查看进程2.通过系统调用函数查看pid3.杀进程4.ppid&…