C/C++内存管理 | new的机制 | 重载自己的operator new
一、C/C++内存分布
1. 内存分区
- 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。
- 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信 .
- 堆用于程序运行时动态内存分配,堆是可以上增长的
- 数据段–存储全局数据和静态数据 (也叫静态区)
- 代码段–可执行的代码/只读常量 (也叫常量区)
2. 程序运行时加载过程
首先,我们平时所写的代码(如test.cpp)存放在那?
其实是存放在磁盘上的,因为是文件形式
而运行一个程序的过程是怎么样的?
写好的代码 -> 编译链接 -> 可执行程序
也就说,我们写好代码点击运行就是运行的这个可执行程序
可执行程序(Windows下.exe、Linux下a.out)
中包括:
- 二进制指令代码(CPU读取)
- 数据
- 其他一些内容
而当程序运行时首先要加载哪些东西到内存呢?
- 二进制指令代码 -> 代码段
- 常量数据 -> 代码段
- 全局变量 -> 数据段 (因为全局变量在main函数前已经定义好)
堆栈上的数据何时创建?
-
当二进制指令代码加载到代码段之后,由CPU依次读取并执行二进制指令
-
当开始执行main函数时,开始创建函数栈帧,此时栈中开辟的变量和数据就开始定义了。
-
而堆上的数据也是在栈上,通过malloc等动态开辟内存的函数来开辟的
注意
类、函数、符号表、公共代码区等概念都是在编译链接阶段的概念编译链接之后,类、函数等都转变成了二进制指令加载到代码了,不存在所谓的类或者函数了
3. 常见数据类型在内存中的分布
二、动态内存管理
C/C++中,除了堆以外的内存分区中资源的申请与释放不用我们管,系统会自动处理。(所以内存泄漏等问题出现在堆上)
1. C语言中动态内存管理方式
malloc : 申请一块空间
calloc :申请一块空间并初始化
realloc :对一段空间进行扩容
使用完都需要 free
,防止内存泄漏
void Test ()
{int* p1 = (int*) malloc(sizeof(int));free(p1);
// 1.malloc/calloc/realloc的区别是什么?int* p2 = (int*)calloc(4, sizeof (int));int* p3 = (int*)realloc(p2, sizeof(int)*10);// 这里需要free(p2)吗?
// 注意: 不需要// 因为如果原地扩容:释放p3就相当于释放了p2// 如果异地扩容,realloc会自动完成原始空间的freefree(p3 );
}
2. C++动态内存管理
虽然C++向下兼容C,但是有些地方C的方式是无能为力的,所以C++又搞了一套自己的动态内存管理方式
即:new 和 delete操作符
注意:new和delete 不是函数,是操作符
new一个空间,使用delete
释放
new多个空间,使用delete []
释放
① new和delete针对 内置类型
// C语言动态开辟:
int* p1 = (int*)malloc(sizeof(int));
if(p1==NULL)
{perror("malloc fail");exit(-1);
}// C++动态开辟//申请一个int类型大小的空间
int* p2 = new int;//new自己去算int是多少字节//开辟多个int的空间 -- 也符合后定义的先析构!
int* p3 = new int[5];//开5个int大小的空间//申请一个int类型大小的空间,并初始化
int* p4 = new int(5); //申请一个int大小的空间,并初始化为5//对new的数组初始化 :{} (C++98不支持,C++11才支持)
int* p5 = new int[5]{ 1,2,3,}; //初始化为 1 2 3 0 0 (不给的默认给0)// 释放free(p1);//C的释放方式delete p2; // 针对new int
delete p4; // 和 new int()
delete[] p3; //针对new int[]
注意
new/delete 和 new[] /delete[] 一定要匹配,否则有时候会出现崩溃。一般来说自定义类型一定会报错,自定义类型一般不会报错
结论
1. 针对内置类型,new/delete 和 malloc/free没有本质的区别,只有用法上的区别,new/delete只是用法简化了
2. malloc的需要去检查是否开辟成功,new不需要,如果失败默认会抛异常
② new和delete针对自定义类型
new和delete针对内置类型与C的malloc/free无大区别,但是对于自定义类型,区别很大!
这也是引入new/delete的原因
new/delete针对自定义类型,与malloc/free最大的区别
- 就是new的时候会自动调用 默认构造函数(如果无默认构造会报错)
- delete的时候会自动调用析构函数
1. 单个自定义类型对象的构造/析构
//C语言用malloc,malloc不会初始化
A* p1 = (A*)malloc(sizeof(A));
if (p1 == nullptr)
{perror("malloc fail");exit(-1);
}// C++使用new,有两步:
// 1. new申请空间 2. 调用默认构造函数初始化
A* p2 = new A;
A* p3 = new A(10);// 显示传递参数来构造//释放空间的区别:
//C语言的free只是释放该对象,不会清理对象中的资源
free(p1);//C++: 1.调用析构函数清理对象中的资源 2. 释放空间
delete p2;
delete p3;
多个对象初始化
- 多个对象默认构造初始化(前提要提供默认构造函数)
A* p4 = new A[10];//默认构造(10次)
delete[] p4;
-
多个对象显示构造初始化
/* 写法比较多 */A* p5 = new A[10]{ 1,2,3,4,5 };//开10个,前5个显示调用构造初始化,后5个默认构造A* p6 = new A[]{ 1,2,3,4,5 };//后面有几个数组就开几个对象的大小A* p7 = new A[]{ (1),(2) };//用括号 A* p8 = new A[]{ {1},{2} };//用花括号A* p9 = new A[]{ A(1),A(2)};//用匿名构造
最常用的是第一种和第二种
注意:支持C++11的编译器才可以显示构造初始化
VS2013就不支持,2019以上是支持的
结论
new/delete是为自定义类型准备的,不仅在堆上申请出来,还会调用构造和析构函数初始化和清理
因为如果采用malloc申请自定义类型,是无法进行初始化的,因为构造函数是在对象定义的时候自动调用
malloc只是申请了空间,无法直接调用构造函数进行初始化,也没办法通过访问成员变量进行初始化(因为一般都是私有的)
③ malloc与new失败时的区别
malloc失败时,会返回NULL
new失败时,会抛异常
测试代码:
//malloc失败
void test3()
{char* p1 = (char*)malloc(1024u * 1024u * 1024u * 2);//+u是为了防止整形溢出printf("%p\n", p1);//以地址形式打印出p1
}//输出结果: 00000000
// 即:malloc失败,返回NULL
new失败
char* p2 = new char[1024u * 1024u * 1024u * 2 - 1];//-1是因为规定不能超过0x7fffffff即整形的一半
运行结果:
所以,malloc需要检查返回值看是否malloc失败
new是不需要检查返回值的,失败会抛异常
3. operator new和 operator delete函数(底层)
我们知道,new一个对象其实做了两件事
- 申请内存
- 调用构造函数
那么new申请内存是调用了谁来申请内存呢?
事实上,new和delete是用户进行动态申请内存和释放操作符,operator new 和 operator delete是系统提供的全局函数
new在底层调用operator new全局函数来申请空间
如图可见:new操作符被解析为指令的时候,主要是 1. call operator new函数 2. call A::A(构造函数)
注意:operator new就是一个函数名,不是运算符重载,只是名字很挫
① operator new和operator delete的底层原理
operator new的源代码
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid* p;//如果malloc失败,返回NULL 则抛异常while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}return (p);
}
可以看到,operator new开空间实际上他也是调用了malloc函数来开空间,只是operator new采用了如果失败就抛出
bad_alloc
的异常的做法。这也是为什么要用operator new封装malloc,而不直接采用malloc的原因
总结operator new的作用
- 帮助new开空间
- 封装malloc,以符合C++new的失败机制(抛异常)
operator delete
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void* pUserData)
{_CrtMemBlockHeader* pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK); /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg(pUserData, pHead->nBlockUse);__FINALLY_munlock(_HEAP_LOCK); /* release other threads */__END_TRY_FINALLYreturn;
}/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
operator delete是调用free来进行释放资源的
注意:free实际上也是宏定义的,原型是_free_dbg
free是为了方便用户使用
/其他的一些加锁和检查可以忽略/
所以,new/delete对于内置类型其实和malloc/free没有本质区别,因为从底层来看,内置类型不需要调用构造函数和析构函数
总结new的过程:
new Type -> call operator new(malloc失败抛异常) -> call Type构造函数
② 直接使用operator new开辟空间(了解)
operator new这个函数也可以自己单独使用,用法和malloc完全一样;不同的是,operator相比于malloc会抛异常,搭配抛异常的try catch使用
//operator new的使用try{char* ptr = (char*)operator new(1024u * 1024u);//operator new开空间printf("%p\n", ptr);//打印出地址operator delete(ptr);}catch (const std::exception& e){cout << e.what() << endl;}
实际当中,是不需要使用operator new 和 operator delete! 直接使用new和delete即可。
③ operator new和operator delete的重载(了解)
1) 利用重载找出内存泄漏
有时候我们不想用系统提供的operator new和operator delete
那么我们就可以进行重载我们自己的operator new和
operator delete来完成一些特殊的需要
如:我们想要检测哪里存在内存泄漏,这时候就可以自己重载一个operator new,至于operator delete则自己写一个全局的(不是重载)
我们在自己重载的operator new中打印文件、函数、行号、申请字节数,然后调用全局的
::operator new
自己写一个全局的operator delete,这样delete的时候就会先调用自己写的operator delete了
条件编译,在Debug下,new被宏定义为带着
__FILE__
,__FUNCTION__
,__LINE__
三个参数的new,从而实现调用我们自己重载的operator new。C++是允许这么玩的,正常默认只有new的空间大小一个参数传给operator new
注:__FILE__
,__FUNCTION__
,__LINE__
等是C语言中的宏
分别是当前文件名
,当前函数名
,当前行号
// 重载operator new,在申请空间时:打印在哪个文件、哪个函数、第多少行,申请了多少个字节
void* operator new(size_t size, const char* fileName, const char* funcName,size_t lineNo)
{void* p = ::operator new(size);cout <<"new:" << fileName << "-" << funcName << "-" << lineNo << "-" << p << "-"<< size << endl;return p;
}
//自己实现全局operator delete(使用delete时,就先调用自己写的operator delete)
void operator delete(void* p)
{cout << "delete:" << p << endl;free(p);
}// 重载operator delete,这里用不到,只是为了匹配重载的operator new来消除警告
void operator delete(void* p, const char* fileName, const char* funcName,size_t lineNo)
{cout << "delete:" << fileName << "-" << funcName << "-" << lineNo << "-" << p <<endl;::operator delete(p);
}// 使用条件编译和宏对调用进行简化
// 只有在Debug方式下,才调用用户重载的 operator new
// 条件编译需要放在函数重载的下面
//使用 __FILE__这样的宏,作为参数传给自己写的operator new,来实现打印文件名等功能。
#ifdef _DEBUG
#define new new(__FILE__, __FUNCTION__, __LINE__)
#endif
//还需要在main的上面,否则不进行替换程序就走完了int main()
{A* p1 = new A;//new的时候 调用重载的operator newdelete p1;//delete时调用自己写的全局operator deleteA* p2 = new A[4];//delete[] p2;A* p3 = new A;delete p3;A* p4 = new A;//delete p4;A* p5 = new A;delete p5;return 0;
}
//main函数中 申请5个对象 但是只释放3个
// 要找出没有释放的两个!
程序运行结果如图
new了5个,但是只delete了3个,并且再通过行号就可以查出具体是哪里没释放而导致的内存泄漏了
2) 重载一个类专属的operator解决频繁申请空间带来的空间碎片问题(内存池)
调用new,编译器会转换为调用 operator new + 构造函数
默认情况下,operator new 使用全局库里面的
但是每个类可以实现自己专属的operator new,new 这个类对象,就会调用这个自己实现的
我们知道,如果一个类 ,比如ListNode类(链表),需要频繁地向内存申请空间
这样就容易造成一些内存碎片问题。这里就可以利用池化技术来减少内存碎片问题,即内存池
内存池的工作原理:
因为malloc是去向堆申请内存,但是要知道操作系统是很忙的,如果频繁的申请就会经常打断操作系统的资源的分配
而内存池就相当于中间的一个角色。内存池先申请一部分内存,当你想要开辟空间首先到内存池中开辟,当内存池中的
内存用完,才会再去向堆上申请。这样就减少了请求操作系统的次数,提高效率。
用个比喻的话就是,堆是你妈妈的钱包,内存池是你自己的钱包。你的定期生活费就是内存池提前申请的内存
我们可以在类中重载一个operator new和operator delete函数。(注意不是函数重载,也不是运算符重载,只是命名空间即域不同)在我们重载的函数中使用内存池(可以自己写,也可以调用STL或者Boost库或第三方库中的内存池)
当new一个对象和delete一个对象的时候,机制决定了会先去类中找有没有类专属的operator new 和 operator delete (局部就近原则)
找到了就直接使用,找不到才回去全局找。并且因为是类专属的,其他的类的new/delete并不受影响
所以会直接调用我们在类中重载的operator new和operator delete函数,也就是先去我们定制的内存池中申请内存而不会直接向堆上申请内存
这里演示STL中的内存池allocator(空间配置器)
allocate
:申请空间
deallocate
:释放空间
//重载类专属operator new
struct ListNode
{int _val;ListNode* _next;//内存池:给所有ListNode用static allocator<ListNode> _alloc;//把内存池设置为静态成员变量(声明)//每个ListNode对象都可以访问到,属于整个类void* operator new(size_t n){cout << "void* operator new(size_t n) -> STL内存池allocator申请" << endl;//allocator类void* obj = _alloc.allocate(1);return obj;}void operator delete(void* ptr){cout << "void* operator delete(size_t n) -> STL内存池allocator释放" << endl;_alloc.deallocate((ListNode*)ptr,1);}ListNode(int val):_val(val), _next(nullptr){}};
//类外面定义内存池对象
allocator<ListNode> ListNode::_alloc;//默认构造
int main()
{//频繁申请ListNode 想提高效率 申请ListNode时,不去malloc而是走自己定制的内存池ListNode* node1 = new ListNode(1);ListNode* node2 = new ListNode(1);ListNode* node3 = new ListNode(1);delete node1;//delete node2;//这里还可以作内存泄漏的检测 申请和释放的个数不符合就是内存泄漏delete node3;return 0;
}
很容易看出存在一个内存泄漏
ps:(其实更官方一些的检查内存泄漏的并不是这样一个个数,而是用一个数据结构,在operator new的时候把地址存进来
operator delete的时候把它删除,最后进行查找,剩下的就是没被释放的)
4. new/delete的实现原理
① 内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:
new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申
请空间失败时会抛异常,malloc会返回NULL
② 自定义类型
- new的原理
- 调用operator new函数申请空间
- 在申请的空间上执行构造函数,完成对象的构造
- delete的原理
- 在空间上执行析构函数,完成对象中资源的清理工作
- 调用operator delete函数释放对象的空间
- new T[N]的原理
- 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对
象空间的申请 - 在申请的空间上执行N次构造函数
- 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对
- delete[]的原理
- 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
- 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释
放空间
5. 定位new表达式(Replace new)(了解)
我们知道,构造函数的调用时机常见的就是:
- 直接创建对象的时候,自动调用构造函数初始化
- new对象的时候,自动调用构造函数
但是上面两种方式都是在开空间创建对象的时候调用构造,我们是不可以显示调用构造函数的(可以显示调用析构函数)
并且也不可以访问成员变量直接初始化
那么如何对已经分配好空间的对象调用构造函数来初始化呢? 这就是定位new的作用
**定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象 **
但是析构的话需要自己显示调用
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
**place_address必须是一个指针,initializer-list是类型的初始化列表 **
// 对malloc或者operator new的空间 用定位new调用构造函数初始化
int main()
{//p2现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没// 有执行A* p2 = (A*)malloc(sizeof(A));if (p2 == NULL){perror("malloc fail");}//定位new 初始化已经开辟好的空间//new(p2)A; //不给参数new(p2)A(10);//给参数//free p2前需要手动析构,释放资源p2->~A();free(p2);//free p2return 0;
}
使用场景
相比new来说,new即开了空间还自动初始化,为啥还要有定位new呢?
这里就还是因为存在 内存池 的应用场景
如果我们去内存池申请空间,那么内存池只是开空间,并不会调用构造函数初始化
所以我们要对开好的空间进行初始化就必须使用定位new
扩展阅读:
C++重载 operator new 和 operator delete 实现一个简单内存泄漏跟踪器
相关文章:

C/C++内存管理 | new的机制 | 重载自己的operator new
一、C/C内存分布 1. 内存分区 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信 .堆用于程序运行时动态内…...

知识库管理系统:企业数字化转型的加速器
在数字化转型的大潮中,知识库管理系统(KBMS)已成为企业提升效率和创新能力的关键工具。本文将探讨知识库管理系统的定义、企业建立知识库的必要性,以及如何快速搭建企业知识库。 知识库管理系统是什么? 知识库管理系统…...
uniapp 如何使用vuex store (亲测)
首先是安装: npm install vuexnext --save 安装之后,Vue2 这样写 不管在哪里,建立一个JS文件,假设命名:store.js 代码这样写: import Vue from vue; import Vuex from vuex;Vue.use(Vuex);const store…...

[编译报错]ImportError: No module named _sqlite3解决办法
1. 问题描述: 在使用python进行代码编译时,提示下面报错: "/home/bspuser/BaseTools/Source/Python/Workspace/WorkspaceDatabase.py", line 18, in <module>import sqlite3File "/usr/local/lib/python2.7/sqlite3/_…...

【旷视科技-注册/登录安全分析报告】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…...
python学习记录16
字符串总结 python程序使用unicode编码,中文字符与英文字符都占一个字符,但英文字符只占一个字节,中文字符若按照utf-8格式编码占3个字节。 (1)字符串常用方法 1)大小写转化 string.upper()#将所有字母…...

AI 大模型在软件开发中的角色

React中类组件和函数组件的理解和区别
react代码模块分为类组件和函数组件。 从语法和定义、内部状态管理、生命周期、性能、可读性和维护性、上下文、集成状态管理库等角度对比React中类组件和函数组件。 1、语法和定义 类组件: 使用 ES6 的类(class)语法定义的 React 组件。…...

Day62||prim算法精讲 |kruskal算法精讲
prim算法精讲 53. 寻宝(第七期模拟笔试) 题目描述 在世界的某个区域,有一些分散的神秘岛屿,每个岛屿上都有一种珍稀的资源或者宝藏。国王打算在这些岛屿上建公路,方便运输。 不同岛屿之间,路途距离不同&…...

upload-labs通关练习
目录 环境搭建 第一关 第二关 第三关 第四关 第五关 第六关 第七关 第八关 第九关 第十关 第十一关 第十二关 第十三关 第十四关 第十五关 第十六关 第十七关 第十八关 第十九关 第二十关 总结 环境搭建 upload-labs是一个使用php语言编写的,…...

wordpress搭建主题可配置json
网站首页展示 在线访问链接 http://dahua.bloggo.chat/ 配置json文件 我使用的是argon主题,你需要先安装好主题,然后可以导入我的json文件一键配置。 需要json界面配置文件的,可以在评论区回复,看见评论我会私发给你。~...

RWKV-5/6 论文被 COLM 2024 收录
由 Bo PENG 和 RWKV 开源社区共同完成的 RWKV-5/6架构论文《Eagle and Finch: RWKV with Matrix-Valued States and Dynamic Recurrence》被顶级会议 COLM 2024 收录。 这是继 RWKV-4 架构论文《RWKV: Reinventing RNNs for the Transformer Era》被 EMNLP 2023 收录之后&…...
MinIO分片下载超大文件
一、前言 各位亲爱的们,之前介绍过了上传超大文件到MinIO: MinIO分片上传超大文件(纯服务端)MinIO分片上传超大文件(非纯服务端) 这里最后再补充一下从MinIO下载超大文件。 二、从MinIO分片下载大文件 …...
Vue3 -- 新组件【谁学谁真香系列6】
Teleport Teleport是什么?–Teleport是一种能够将我们的组件html结构移动到指定位置的技术。 父组件: <template><div calss="outer"><h2>我是App组件</h2><img src="https://z1.ax1x.com/2023/11/19/piNxLo4.jpg" alt=&qu…...

Openstack3--本地仓库搭建(ftp源搭建失败)
上传镜像 后面的ftp源做不了,请将下面的本地openstack源在控制节点和计算节点都配置 在控制节点上传,安装ftp并配置启动后再在计算节点配置 将openStack-train.iso文件通过MobaXterm远程连接软件上传至控制节点 /opt 目录下 挂载 进入 /opt 目录 创建…...

【初阶数据结构与算法】链表刷题之移除链表元素、反转链表、找中间节点、合并有序链表、链表的回文结构
文章目录 一、移除链表元素思路一思路二 二、合并两个有序链表思路:优化: 三、反转链表思路一思路二 四、链表的中间节点思路一思路二 五、综合应用之链表的回文结构思路一:思路二: 一、移除链表元素 题目链接:https:…...

【PGCCC】Postgresql Toast 原理
前言 上篇博客讲述了 postgresql 如何存储变长数据,它的应用主要是在 toast 。Toast 在存储大型数据时,会将它存储在单独的表中(称为 toast 表)。因为 postgresql 的 tuple(行数据)是存在在 Page 中的&…...

vue3使用element-plus,树组件el-tree增加引导线
vue3使用element-plus,树组件el-tree增加引导线 vue3项目element-plus,树组件el-tree增加引导线 element-plus组件库的el-tree样式 因为element的样式不满足当前的的需求,UI图,所以对el-tree进行增加了引导线 修改样式如下&am…...

AlphaFold3中文使用说明
目录 1. 在线网站用例1. 使用json输入预测蛋白结构 2. 本地命令行2.1 运行示例2.2 AF3输入输入格式JSON兼容性JSON最外层(Top-level)结构序列多序列比对MSA结构模板键 用户提供CCDs 2.3 AF3输出 AlphaFold3(AF3)可以通过在线网站或…...

使用@react-three/fiber,@mkkellogg/gaussian-splats-3d加载.splat,.ply,.ksplat文件
前言 假设您正在现有项目中集成这些包,而该项目的构建工具为 Webpack 或 Vite。同时,您对 Three.js 和 React 有一定的了解。如果您发现有任何错误或有更好的方法,请随时留言。 安装 npm install three types/three react-three/fiber rea…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...