当前位置: 首页 > news >正文

C++ 智能指针底层逻辑揭秘:优化内存管理的核心技术解读

目录

0.为什么需要智能指针?

1.智能指针的使用及原理

RAII:

智能指针的原理:

2.智能指针有哪些?

std::auto_ptr

std::unique_ptr

std::shared_ptr

std::weak_ptr


0.为什么需要智能指针?

        想要回答这个问题,首先要来看一个没有智能指针存在的场景:

int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void Func()
{int* p1 = new int;int* p2 = new int;cout << div() << endl;delete p1;delete p2;
}
int main()
{try{Func();}catch (exception& e){cout << e.what() << endl;}return 0;
}

思考:如果p1这里new 抛异常会如何?如果p2这里new 抛异常会如何?如果div调用又会抛异常会如何?毫无疑问,如果new开辟空间时抛出异常亦或是div调用时候抛出异常都会导致p1p2就没有及时释放,造成内存泄露!但如果我们依次在抛出异常前手动释放掉资源又会显得繁琐,这时就需要智能指针

1.智能指针的使用及原理

        先来介绍一下智能指针运用到了什么原理:

RAII:

        RAII(Resource Acquisition Is Initialization)是一种 利用对象生命周期来控制程序资源 (如内存、文件句柄、网络连接、互斥量等等)的简单技术。
         在对象构造时获取资源 ,接着控制对资源的访问使之在对象的生命周期内始终保持有效, 最后在对象析构的时候释放资源 借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:不需要显式地释放资源采用这种方式,对象所需的资源在其生命期内始终保持有效。
// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr {
public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;}private:T* _ptr;
};

        上面就是根据RAII思想大致设计出的智能指针,指针内部存储的是对象的地址,这样就相当于把资源的地址交给一个类对象管理,当这个类销毁时调用析构函数就不需要手动释放了!

智能指针的原理:

        上面其实与真正的智能指针的框架大差不差,但是因为还要具备指针的行为,因此需要重载一下operator*以及operator->两个操作符函数:

template<class T>
class SmartPtr {
public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;}T& operator*() {return *_ptr;}T* operator->() {return _ptr;}
private:T* _ptr;
};

        这样就有了一个完整智能指针的雏形了~
 

2.智能指针有哪些?

std::auto_ptr

        C++98版本的库中就提供了auto_ptr的智能指针,让我们先来看一下关于auto_ptr的一些介绍:

        但其实auto_ptr是许多程序员不喜欢使用的智能指针,因此存在指针悬空的问题:

void test()
{//c++98中提供auto_ptrauto_ptr<A> ap1(new A(1));auto_ptr<A> ap2(new A(2));auto_ptr<A> ap3(ap1);//这里用sp1去构造sp3就会出现指针悬空的问题(ap1->a)++;//sp1被置为空后再去访问就会报错(ap2->a)++;}

        在上面的场景中就出现了指针悬空的问题,这里进行了管理权转移,ap1将管理权交给了ap3,那么ap3构造的时候会将ap1置为nullptr,如果此时再去对ap1进行访问就属于非法的!

下面来简单实现一下auto_ptr,以便我们明了具体结构:

思路:

---1--- 其实大致框架已经在前面给出了,完成了operator->以及operator*函数的重写,这里需要完成的是拷贝构造函数

---2--- 在拷贝构造函数中,需要把地址传给成员变量,将参数的地址置为nullptr

template<class T>
class auto_ptr
{
public:auto_ptr(T* ptr):_ptr(ptr){ }auto_ptr(auto_ptr<T>& ptr):_ptr(ptr._ptr){ptr._ptr = nullptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};

         如果不想要这种危险的情况发生,可以使用std::unique_ptr:

std::unique_ptr

        顾名思义,unique则代表的是唯一,也就是说此智能指针不能拷贝构造一个新的同类型对象,先让我们看看介绍:

        那么如果你试图用一个unique_ptr对象去拷贝构造一个新对象,这种行为在编译时就会报错:

void test()
{unique_ptr<A> up1(new A(1));unique_ptr<A> up2(new A(2));//unique_ptr直接进行了反拷贝操作,禁止了这种指针拷贝行为unique_ptr<A> up3(up1);
}

 为什么会有删除函数这样的报错,是因为unique_ptr的底层实现了反拷贝:

        我们来模拟实现一下unique_ptr:

思路:

---1--- 大致框架还是相同,不同的是这样要如何实现反拷贝?

---2--- 如果你使用C++98那么反拷贝应该将拷贝构造函数以及赋值函数只声明不实现

---3--- 如果你使用C++11那么只需将这两个函数设置为=delete即可,这也是为什么报错显示函数已经被删除

template<class T>
class unique_ptr
{
public:unique_ptr(T* ptr):_ptr(ptr){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private://unique这里用到了反拷贝,c++98中也可以只声明不实现,将声明放到private中unique_ptr(unique_ptr<T>& ptr) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;T* _ptr;
};

         那么如果我就是要实现可以拷贝构造和赋值构造的智能指针呢?登场的是std::shared_ptr:

std::shared_ptr

        顾名思义,shared就是可以共享,那么就代表此类型的智能指针可以实现多个指针指向同一块内存,先来看看介绍:

        那么如果你使用shared_ptr去构造和赋值,都是可以通过编译的:

void test()
{bit::shared_ptr<A> sp1(new A(1));bit::shared_ptr<A> sp2(new A(2));bit::shared_ptr<A> sp3(sp1);//这里支持拷贝,是因为实现了引用计数sp2 = sp3;
}

         话不多说,来看看底层实现:

思路:

---1--- 框架没有变化,但是如果要实现shared_ptr的拷贝构造和赋值构造就要一点难度了:

---2--- 为什么shared_ptr可以支持多个对象指向同一内存?这里运用了引用计数,每个对象销毁时计数-1,直到减为0时,这块内存发生析构释放,这里最适合来进行技术的是int*类型的对象,为什么不是static或者是int,static只会实例化一份,无法实现多个对象的引用计数,int又会导致每个对象的引用计数都是拷贝,无法从根本修改计数

---3--- 默认构造:先new int,初始化为1即可

---4--- 拷贝构造:将指针赋值,将计数+1,并把地址赋值一下

---5--- 赋值构造:不仅仅是计数的增加以及指针的赋值,还要考虑原指针指向的内存是否应该释放

template<class T>
class shared_ptr
{
public:shared_ptr(T* ptr=nullptr):_ptr(ptr), _pcount(new int(1)){}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr){++(*(sp._pcount));_pcount = sp._pcount;}shared_ptr<T>& operator=(const shared_ptr<T>& sp){//在改变被赋值对象的指针之前,先要考虑是否要释放被赋值对象管理的内存资源//也就是说改变指针之前要对原理管理资源的pcount--,如果减到0就需要释放//首先要考虑指向通过一块内存的两个指针相互赋值,如果不同名只是麻烦一点,如果同名那么就会造成释放后再去访问的问题if (sp._ptr == _ptr){return *this;}//对于被赋值的要记得减去引用计数来决定是否释放内存,防止造成内存泄漏if (--(*_pcount) == 0){delete _ptr;delete _pcount;}_ptr = sp._ptr;++*(sp._pcount);_pcount = sp._pcount;return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get()const//返回指针{return _ptr;}size_t use_count()const//返回引用计数的个数{return *_pcount;}~shared_ptr(){if (--(*_pcount) == 0){_del(_ptr);delete _pcount;}}
private:T* _ptr;//shared_ptr可以支持多个指针指向同一块空间,怎么实现?//引用计数,如何实现?使用static int不可行,因为多个对象只能有一个static,//但是每一块资源都需要一个计数int* _pcount;};

        但是,shared_ptr也会产生相应的问题,那就是引用循环:

        引用循环(Reference Cycle),也被称为循环引用,它会引发内存泄漏等问题。

        定义:引用循环指两个或多个对象之间相互持有对方的引用,形成一个闭环引用结构,导致这些对象无法被正常释放,造成内存资源的浪费。例如,对象 A 持有对象 B 的引用,而对象 B 又持有对象 A 的引用,这样就形成了引用循环。

        假设存在两个类AB ,它们的定义如下:

#include <memory>
class B;
class A {
public:std::shared_ptr<B> b_ptr;~A() {std::cout << "A destroyed" << std::endl;}
};
class B {
public:std::shared_ptr<A> a_ptr;~B() {std::cout << "B destroyed" << std::endl;}
};void test() {std::shared_ptr<A> a = std::make_shared<A>();std::shared_ptr<B> b = std::make_shared<B>();a->b_ptr = b;b->a_ptr = a;
}

        在test函数结束时,ab所指向的对象本应被释放,但由于它们相互引用,引用计数都不会降为 0,这里值得细说一下:a_ptr由b管理,当b析构时a_ptr发生析构,但是b_ptr又管理b,当b_ptr发生析构,b才会析构,那么b_ptr由谁管理呢?a,当a发生析构b_ptr才会析构,但是a又被a_ptr管理着,这就构成了死循环,最后引用计数由2->1,会导致内存泄漏。

        如何解决?weak_ptr登场:不过在这之前先要补充定制删除器:如果让智能指针管理开辟的数组,该如何释放呢?shared_ptr提供了这样的接口:

这里的D可以传入函数指针,仿函数亦或是lambda表达式

void test_sp4()
{//如果new了一个数组,又该如何释放呢?//了解定制删除器:new A[10]//template <class U, class D> shared_ptr (U* p, D del);//template <class D> shared_ptr(nullptr_t p, D del);//库中用了模板D来控制,其实是仿函数,同时也可以接收lameda表达式std::shared_ptr<A> sp1(new A[10], [](const A* ptr) { delete[] ptr; });std::shared_ptr<A> sp2((A*)malloc(sizeof(A)*10), Destroy<A>());std::shared_ptr<FILE> sp3(fopen("SmartPtr.hpp", "r"), [](FILE* ptr) {return fclose(ptr); });//ps:这里的A是作者自定义的类
}

 这是怎么做到的呢?其实是用到了U实例化后的包装器来接收传入的函数指针,仿函数亦或是lambda表达式,并使用模板来处理,这样在析构函数的时候使用包装器传入要释放资源的指针即可:(了解一下,理解即可)

template<class T>
class shared_ptr
{
public:template<class D>shared_ptr(T* ptr,D del):_ptr(ptr),_pcount(new int(1)),_del(del){}function<void(T*)> _del;//使用包装器来解决模板参数无法涉及析构函数的问题
};

std::weak_ptr

        weak_ptr可以理解为是专门用于处理循环引用的问题的指针,但请注意并没有采用RAII思想来搭建,所以并不属于智能指针:

可以看到,weak_ptr并不支持传参构造,通常用shared_ptr去构造weak_ptr,那么如果这样使用weak_ptr就可以正常析构:

#include <memory>
class B;
class A {
public:std::weak_ptr<B> b_ptr;//这里改成了weak_ptr~A() {std::cout << "A destroyed" << std::endl;}
};
class B {
public:std::weak_ptr<A> a_ptr;//这里改成了weak_ptr~B() {std::cout << "B destroyed" << std::endl;}
};void test() {std::shared_ptr<A> a = std::make_shared<A>();std::shared_ptr<B> b = std::make_shared<B>();a->b_ptr = b;b->a_ptr = a;
}

原理是什么呢?其实weak_ptr并没有处理shared_ptr中的引用计数部分,只负责了指针的拷贝,这才使得循环引用得以解决,请注意weak_ptr不属于RAII智能指针,可以参与资源的访问,但是不参与资源的释放!!!

模拟实现一下:

思路:

---1--- 其实很简单,只用实现shared_ptr构造和默认构造即可,直接给出:

template<class T>
class weak_ptr
{
public:weak_ptr()//不支持传参构造:_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp)//支持sharedptr构造:_ptr(sp._ptr){ }weak_ptr<T>& operator=(const weak_ptr<T>& wp){_ptr = wp._ptr;return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};

相关文章:

C++ 智能指针底层逻辑揭秘:优化内存管理的核心技术解读

目录 0.为什么需要智能指针&#xff1f; 1.智能指针的使用及原理 RAII&#xff1a; 智能指针的原理&#xff1a; 2.智能指针有哪些&#xff1f; std::auto_ptr std::unique_ptr std::shared_ptr std::weak_ptr 0.为什么需要智能指针&#xff1f; 想要回答这个问题&…...

Android基础入门、Android常见界面布局基础练习

第1章 Android基础入门、第2章Android常见界面布局 一. 填空题 1. (填空题)如果希望在XML布局文件中调用颜色资源&#xff0c;可以使用_____调用。 正确答案&#xff1a; (1) color 2. (填空题)Android程序入口的Activity是在_____文件中注册的。 正确答案&#xff1a; (1…...

Spring Cloud主要组件介绍

一、Spring Cloud 1、Spring Cloud技术概览 分为:服务治理,链路追踪,消息组件,配置中心,安全控制,分布式任务管理、调度,Cluster工具,Spring Cloud CLI,测试 2、注册中心:常用注册中心(Euerka[AP]、Zookeeper[CP]) 1)Euerka Client(服务提供者)=》注册=》Eue…...

【7】深入学习Buffer缓冲区-Nodejs开发入门

深入学习Buffer缓冲区 前言ASCII码GBK/GB2312UnicodeJavascript转换 BufferBuffer的作用Buffer的创建Buffer.allocBuffer.allocUnsafe(size)Buffer.allocUnsafeSlow(size)Buffer.from(array)Buffer.from(arrayBuffer[, byteOffset[, length]])Buffer.from(buffer)Buffer.from(s…...

酶动力学参数预测,瓶颈识别……中科院深圳先进技术研究院罗小舟分享AI在酶领域的创新应用

蛋白质&#xff0c;作为生命的基石&#xff0c;在生命活动中发挥着关键作用&#xff0c;其结构和功能的研究&#xff0c;对创新药物研发、合成生物学、酶制剂生产等领域&#xff0c;有着极其重要的意义。但传统蛋白质设计面临诸多难题&#xff0c;蛋白质结构复杂&#xff0c;序…...

Dockerfile

Dockerfile Dockerfile 是一个文本文件&#xff0c;其内包含了一条条指令&#xff0c;每一条指令构建镜像的一层&#xff0c;因此每一条指令的内容&#xff0c;就是描述该层应当如何构建。 定制镜像&#xff0c;可以将镜像制作的每一层的修改、安装、构建、操作的命令&#xf…...

Redis高频面试题(含答案)

当然可以&#xff0c;Redis 是面试中非常常见的高频考点&#xff0c;尤其在后台开发、分布式系统、缓存设计等方向&#xff0c;面试官常常通过 Redis 来考察你的高并发处理能力、系统设计能力和对缓存一致性理解。 以下是一些典型 Redis 的面试场景题目类型和你可以如何回答的…...

#3 物联网 的标准

商业化的技术都有标准&#xff0c; 标准的本质就是 可以重复多次实现的方法。而这些方法都是设定物联网的那些人布局的&#xff0c;当然在保证按方法操作的结果是属于物联网这个基本的操作里面&#xff0c;藏着的是对某些利益团队的维护&#xff0c;这里大家知道就可以了。 除 …...

Moviepy 视频编辑的Python库,可调整视频分辨率、格式

MoviePy简介 MoviePy 是一个用于视频编辑的Python库&#xff0c;支持视频剪辑、和合成、转码等多种操作&#xff0c;主要有点&#xff1a; 基于 FFmpeg&#xff1a;能够处理几乎所有常见的视频格式。 修改视频分辨率 方法一:指定新的宽度和高度 from moviepy.editor import V…...

【LeetCode 热题 100】哈希 系列

&#x1f4c1;1. 两数之和 本题就是将通过两层遍历优化而成的&#xff0c;为什么需要两层遍历&#xff0c;因为遍历 i 位置时&#xff0c;不知道i-1之前的元素是多少&#xff0c;如果我们知道了&#xff0c;就可以通过两数相加和target比较即可。 因为本题要求返回下标&#xf…...

蓝光三维扫描:汽车冲压模具与钣金件全尺寸检测的精准解决方案

随着汽车市场竞争日趋激烈&#xff0c;新车型开发周期缩短&#xff0c;安全性能要求提高&#xff0c;车身结构愈加复杂。白车身由多达上百个具有复杂空间型面的钣金件&#xff0c;通过一系列工装装配、焊接而成。 钣金件尺寸精度是白车身装配精度的基础。采用新拓三维XTOM蓝光…...

鲲鹏+昇腾部署集群管理软件GPUStack,两台服务器搭建双节点集群【实战详细踩坑篇】

前期说明 配置&#xff1a;2台鲲鹏32C2 2Atlas300I duo&#xff0c;之前看网上文档&#xff0c;目前GPUstack只支持910B芯片&#xff0c;想尝试一下能不能310P也部署试试&#xff0c;毕竟华为的集群软件要收费。 系统&#xff1a;openEuler22.03-LTS 驱动&#xff1a;24.1.rc…...

面试篇 - GPT-1(Generative Pre-Training 1)

GPT-1&#xff08;Generative Pre-Training 1&#xff09; ⭐模型结构 Transformer only-decoder&#xff1a;GPT-1模型使用了一个12层的Transformer解码器。具体细节与标准的Transformer相同&#xff0c;但位置编码是可训练的。 注意力机制&#xff1a; 原始Transformer的解…...

探索机器人创新技术基座,傅利叶开源人形机器人 Fourier N1

一&#xff0e;傅利叶为什么要开源&#xff1f; 2025年3月17日&#xff0c;傅利叶正式开源全尺寸人形机器人数据集Fourier ActionNet。 2025年4月11日&#xff0c;傅利叶正式发布首款开源人形机器人 Fourier N1。 傅利叶为什么要做这些开源工作呢&#xff1f;4月11日&#x…...

正则表达式和excel文件保存(python)

正则表达式 import re data """ <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8" /> <title>测试页面</title> </head> <body> <h1>《人工智能的发展趋势分析报…...

无人船 | 图解基于视线引导(LOS)的无人艇制导算法

目录 1 视线引导法介绍2 LOS制导原理推导3 Lyapunov稳定性分析4 LOS制导效果 1 视线引导法介绍 视线引导法&#xff08;Line of Sight, LOS&#xff09;作为无人水面艇&#xff08;USV&#xff09;自主导航领域的核心技术&#xff0c;通过几何制导与动态控制深度融合的机制&am…...

大腾智能获邀出席华为云2025生态大会,携全栈工业软件助力产业智能升级

4月10日-4月11日&#xff0c;以“聚力共创&#xff0c;加速行业智能跃迁”为主题的华为云生态大会2025在安徽芜湖召开。大腾智能受邀出席此次盛会&#xff0c;与众多行业精英、生态伙伴齐聚一堂&#xff0c;深度参与前沿技术演示、生态伙伴签约及商业场景共创&#xff0c;与行业…...

Java基础关键_037_Java 常见新特性

目 录 一、新语法 1.JShell 2.try-with-resources &#xff08;1&#xff09;jdk 7 之前 &#xff08;2&#xff09;jdk 7 之后 &#xff08;3&#xff09;jdk 9 之后 3.局部变量类型判断&#xff08;不推荐&#xff09; 4.instanceof 的模式匹配 &#xff08;1&a…...

鸿蒙公共通用组件封装实战指南:从基础到进阶

一、鸿蒙组件封装核心原则 1.1 高内聚低耦合设计 在鸿蒙应用开发中&#xff0c;高内聚低耦合是组件封装的关键准则&#xff0c;它能极大提升代码的可维护性与复用性。 从原子化拆分的角度来看&#xff0c;我们要把复杂的 UI 界面拆分为基础组件和复合组件。像按钮、输入框这…...

IntelliJ 配置(二)配置相关类库(2)LineMarkerProvider

一、介绍 LineMarkerProvider 是 IntelliJ 平台插件开发中的一个接口&#xff0c;它的作用是在编辑器左侧的“行标记区域”&#xff08;就是代码行号左边那一栏&#xff09;添加各种图标、标记或导航链接。比如Java 类中看到的&#xff1a; 小绿色三角形&#xff08;可以点击运…...

红宝书第四十二讲:Angular核心特性精讲:依赖注入 RxJS整合

红宝书第四十二讲&#xff1a;Angular核心特性精讲&#xff1a;依赖注入 & RxJS整合 资料取自《JavaScript高级程序设计&#xff08;第5版&#xff09;》。 查看总目录&#xff1a;红宝书学习大纲 一、依赖注入&#xff08;Dependency Injection&#xff09;&#xff1a;快…...

AD917X系列JESD204B MODE7使用

MODE7特殊在F8&#xff0c;M4使用2个复数通道 CH0_NCO10MHz CH1_NCO30MHZ DP_NCO50MHz DDS1偏移20MHz DDS2偏移40MHz...

软考高级系统架构设计师-第11章 系统架构设计

【本章学习建议】 根据考试大纲&#xff0c;本章不仅考查系统架构设计师单选题&#xff0c;预计考12分左右&#xff0c;而且案例分析和论文写作也是必考&#xff0c;对应第二版教材第7章&#xff0c;属于重点学习的章节。 软考高级系统架构设计师VIP课程https://edu.csdn.net/…...

中和农信的“三农”服务密码:科技+标准化助力乡村振兴

作为中国农村市场最大的专注服务农村小微客户的“三农”综合服务机构&#xff0c;中和农信凭借多年积累的农村服务经验&#xff0c;成功从单一小额信贷机构转型为覆盖金融、生产、生活及生态服务的综合型“三农”服务平台。近期&#xff0c;中和农信在由中保保险资产登记交易系…...

【Redis】布隆过滤器应对缓存穿透的go调用实现

布隆过滤器 https://pkg.go.dev/github.com/bits-and-blooms/bloom/v3 作用&#xff1a; 判断一个元素是不是在集合中 工作原理&#xff1a; 一个位数组&#xff08;bit array&#xff09;&#xff0c;初始全为0。多个哈希函数&#xff0c;运算输入&#xff0c;从而映射到位数…...

MyBatis-Plus笔记(下)

注解 tablename注解 - 描述&#xff1a;表名注解&#xff0c;标识实体类对应的表 - 使用位置&#xff1a;实体类 代码举例&#xff1a; TableName//可以不加&#xff0c;使用实体类的名字作为表名&#xff01;忽略大小写 //BaseMapper->User实体类-》实体类名-》表名数据…...

【项目管理】第14章 项目沟通管理-- 知识点整理

项目管理-相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 (一)知识总览 项目管理知识域 知识点: (项目管理概论、立项管理、十大知识域、配置与变更管理、绩效域) 对应:第6章-第19章 第6章 项目管理概论 4分第13章 项目资源管理 3-4分第7章 项目…...

3个关键数据解密:首航上市如何重构ebay电商新能源供应链?

3个关键数据解密&#xff1a;首航上市如何重构eBay电商新能源供应链&#xff1f; 在跨境电商圈&#xff0c;一个新玩家的崛起往往意味着新的格局变动。2024年&#xff0c;伴随一家名为“首航”的新能源企业在港股成功上市&#xff0c;整个eBay类目的供应链悄然掀起新一轮洗牌。…...

《华为云Node.js部署:从开发环境到生产上线的完整指南》

目录 引言第一步&#xff1a; 重置密码第二步&#xff1a;连接到服务器第三步&#xff1a;安装必要软件第四步&#xff1a;创建项目目录第五步&#xff1a;将代码上传到服务器1、安装 FileZilla2、打开FileZilla&#xff0c;连接到您的服务器&#xff1a;3、连接后&#xff0c;…...

【网络原理】TCP/IP协议五层模型

目录 一. 协议的分层 二. OSI七层网络协议 三. TCP/IP五层网络协议 四. 网络设备所在分层 五. 封装 六. 分用 七. 传输中的封装和分用 八. 数据单位术语 一. 协议的分层 常见的分层为两种OSI七层模型和TCP/IP五层模型 为什么要协议分层&#xff1f; 在网络通信中&…...

Asp.Net Core学习随笔

学习自BLBL杨中科老师 依赖注入(Dependency Injection) 依赖注入是实现控制反转(Inversion Of Control 即IOC)的一种方式(还有一种叫服务定位器的实现,但是不如依赖注入好用),软件开发中实现解耦常用的方式. 比如吃饭 ​1. 传统写法&#xff08;没有DI&#xff0c;紧耦合&a…...

基于PHP的酒店网上订房系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 酒店服务是旅游行业的一个重要组成部分&#xff0c;它的作用已经从过去的单一的住宿、结算帐务向全面、高水平的服务型酒店转变。酒店的服务工作贯穿于整个酒店的市场营销、预定、入住、退房、结账等环节&#xff0c;酒店要提高整体工作水平&#xff0c;简化工作程序&…...

《MySQL从入门到精通》

文章目录 《MySQL从入门到精通》1. 基础-SQL通用语法及分类2. 基础-SQL-DDL-数据库操作3. 基础-SQL-DDL-表操作-创建&查询4. 基础-SQL-DDL-数据类型及案例4.1 数值类型4.2 字符串类型4.3 时间和日期类型 5. 基础-SQL-DDL-表操作-修改&删除5.1 DDL-表操作-修改5.2 DDL-表…...

MySQL聚合查询

聚合查询 group by...

生信初学者教程(三十四):文章的方法

文章目录 介绍数据收集和整理数据整合差异基因分析功能富集分析免疫浸润分析候选标记物识别诊断ROC曲线单细胞分析统计方法介绍 在数据分析进行的同时,我们可以逐步撰写方法部分,确保其与结果紧密相连。一旦结果部分完成,方法部分应根据结果的逻辑顺序进行分类和组织。在描…...

算力云平台部署—SadTalker的AI数字人视频

选择算力 部署选择 选择镜像 机器管理 控制台 通过平台工具进入服务器 认识管理系统 打开命令行 进入目录 stable-diffusion-webui# cd 增加执行权限 chmod x ./webui.sh 运行命令 bash ./webui.sh sudo apt install -y python3 python3-venv git 安装软件 Creating the …...

iPhone相册导出到电脑的完整指南

iPhone相册导出到电脑的完整指南 本文介绍通过数据线连接实现iPhone照片视频传输到电脑的标准操作方法&#xff0c;适用于需要备份移动设备影像资料的用户。 环境准备 使用原装Lightning或USB-C数据线连接设备与电脑需在电脑端安装设备管理工具&#xff08;如克魔助手&#…...

【数据结构】励志大厂版·初阶(复习+刷题):复杂度

前引&#xff1a;从此篇文章开始&#xff0c;小编带给大家的是数据结构初阶的刷题讲解 &#xff0c;此类文章将简略的包含相关知识&#xff0c;详细的思路拆分讲解&#xff0c;分析每一题的难点、易错点&#xff0c;看见题目如何分析&#xff0c;以上就是小编预备的内容&#x…...

Nginx底层架构(非常清晰)

目录 前言&#xff1a; 场景带入&#xff1a; HTTP服务器是什么&#xff1f; 反向代理是什么&#xff1f; 模块化网关能力&#xff1a; 1.配置能力&#xff1a; 2.单线程&#xff1a; 3.多worker进程 4.共享内存&#xff1a; 5.proxy cache 6.master进程 最后&…...

Golang|Channel 相关用法理解

文章目录 用 channel 作为并发小容器channel 的遍历channel 导致的死锁问题用 channel 传递信号用 channel 并行处理文件用channel 限制接口的并发请求量用 channel 限制协程的总数量 用 channel 作为并发小容器 注意这里的 ok 如果为 false&#xff0c;表示此时不仅channel为空…...

智能合约安全审计平台——以太坊虚拟机安全沙箱

目录 以太坊虚拟机安全沙箱 —— 理论、设计与实战1. 引言2. 理论背景与安全原理2.1 以太坊虚拟机(EVM)概述2.2 安全沙箱的基本概念2.3 安全证明与形式化验证3. 系统架构与模块设计3.1 模块功能说明3.2 模块之间的数据流与安全性4. 安全性与密码学考量4.1 密码学保障在沙箱中…...

趣说区块链隐私智能合约Shielder 实现原理

目录 核心理念 Deposit Withdraw Shielder 是 Aleph Zero 上的智能合约,它利用 zk-SNARK 技术实现隐私支付以及与 DeFi 的隐私交互。这与常规区块链的完全透明性形成鲜明对比,常规区块链允许追踪单个用户与链上合约以及其他用户的所有交互。Shielder 通过使第三方链观察者…...

TCPIP详解 卷1协议 五 Internet协议

5.1——Internet协议 IP是TCP/IP协议族中的核心协议。所有TCP、UDP、ICMP和IGMP数据都通过IP数据报传输。IP 提供了一种尽力而为、无连接的数据报交付服务。“尽力而为”的含义是不保证 IP 数据报能成功到达目的地。任何可靠性必须由上层&#xff08;例如TCP&#xff09;提供。…...

基于Oracle ADG通过dblink创建物化视图同步数据到目标库

基于Oracle ADG通过dblink创建物化视图同步数据到目标库 环境说明&#xff1a;源端环境Oracle ADG一主一备&#xff0c;版本11.2.0.4&#xff0c;目标端版本11.2.0.4&#xff0c;测试通过dblink方式在目标库创建物化视图同步ADG备库的数据。 PROD --> STANDBY – > TAR…...

openGauss新特性 | 自动参数化执行计划缓存

目录 自动化参数执行计划缓存简介 SQL参数化及约束条件 一般常量参数化示例 总结 自动化参数执行计划缓存简介 执行计划缓存用于减少执行计划的生成次数。openGauss数据库会缓存之前生成的执行计划&#xff0c;以便在下次执行该SQL时直接使用&#xff0c;可…...

qt中的正则表达式

问题&#xff1a; 1.在文本中把dog替换成cat&#xff0c;但可能会把dog1替换成cat1&#xff0c;如果原本不想替换dog1&#xff0c;就会出现问题 2文本中想获取某种以.txt为结尾的多有文本&#xff0c;普通的不能使用 3如果需要找到在不同的系统中寻找换行符&#xff0c;可以…...

开源项目 | 17款云原生安全相关的扫描和平台类开源工具

“ 随着云计算技术的不断发展&#xff0c;越来越多的企业开始将应用程序和数据存储到云上。然而&#xff0c;云安全问题也随之而来&#xff0c;因此&#xff0c;开源云原生安全工具的需求也越来越大。在本文中&#xff0c;我们将介绍一些流行的开源云原生安全工具&#xff0c;以…...

力扣面试150题—旋转图像和矩阵置零

Day21 题目描述 思路 矩阵转置 在将列反转 1 2 3 4 5 6 7 8 9 转置 1 4 7 2 5 8 3 6 9 反转 7 4 1 8 5 2 9 6 3 class Solution {public void rotate(int[][] matrix) { //分为两步 矩阵转置&#xff0c;将列倒序 int x0; int nmatrix.length; //转…...

ScholarCopilot:“学术副驾驶“

这里写目录标题 引言&#xff1a;学术写作的痛点与 AI 的曙光ScholarCopilot 的核心武器库&#xff1a;智能生成与精准引用智能文本生成&#xff1a;不止于“下一句”智能引用管理&#xff1a;让引用恰到好处 揭秘背后机制&#xff1a;检索与生成的动态协同快速上手&#xff1a…...

Node.js项目开启多进程的2种方案

当node项目只部署一个单进程单实例时,遇到异常发生后程序会崩溃,此时杀掉进程在重启单这段时间会导致服务不能正常使用,这显然会影响用户体验。 所以需要以多进程的模式去部署应用,这样当某一个进程发生异常重启时,此时有其他请求被接受后,其他进程依旧可以对外提供服务…...