C++内存池实现

1.内存池概念

内存池就和其他的池数据(如线程池)结构类似,由程序维护一个“池”结构来管理程序使用的内存,然后根据需要从内存池中申请使用内存或者向内存池中释放内存,来达到高效管理内存的目的。
在一般的内存管理的库函数中,不管是C中的malloc/free还是C++中的New/delet,都涉及到一个问题:在申请和释放内存时,都需要与操作系统进行交互来完成内存的分配,而在释放时,系统需要对申请的堆内存就行整理,判断free的内存块前后是否有空闲,如果有的话就需要进行合并,因此,直接使用库函数来向系统申请内存会耗费大量的时间,同时也可能产生大量的内存碎片,对系统整体造成压力。
因此,对于需要在短时间内大量申请或者释放小块内存的系统,维护一个内存池来管理内存的分配和回收,在提高系统的效率和并发能力上就很有意义了。
内存池的原理就是,由程序在初始化时一次性向系统申请一块大的内存,然后将其分成多个固定大小的内存块来进行管理,从而避免程序运行时频繁的进行系统调用来减少内存碎片和分配开销。

2.内存池框架

在这里的内存池实现框架中,把内存池分为前端和后端两个部分,由后端维护16个自由链表,在每个链表下挂载管理相同大小的内存块,从8,16,24到128,在申请使用时,当申请的内存小于128字节时,从内存池中分配对应大小的内存块给对象,如果申请的内存大于128字节,则使用malloc从系统中获取。在释放内存时,从内存池申请的内存释放给内存池的管理工具,而从系统申请的内存则释放给系统,由系统进行维护。
内存池维护的数据结构前端使用类模板,通过重新定义一个New类和Delete类来帮助使用者利用内存池管理内存。

3.工具类:Mutex

这里是封装一个锁,来保证在内存池使用时的线程安全。

//Mutex.h//
// Created by crab on 2024/10/28.
//#ifndef MUTEX_H
#define MUTEX_H
#include <pthread.h>class Mutex
{
public://创建锁static Mutex* createNew();//构造与析构函数Mutex();~Mutex();//加锁 解锁void lock();void unlock();//获取锁的对象pthread_mutex_tpthread_mutex_t* get() { return &mMutex; };private:pthread_mutex_t mMutex;};
//Guard对象,用来保证锁与加锁的生命周期一致
class MutexLockGuard
{
public:MutexLockGuard(Mutex* mutex);~MutexLockGuard();private:Mutex* mMutex;};
#endif //MUTEX_H
//Mutex.cpp
//
// Created by crab on 2024/10/28.
//#include "Mutex.h"
#include "New.h"Mutex* Mutex::createNew()
{//return new Mutex();return New<Mutex>::allocate();
}Mutex::Mutex()
{pthread_mutex_init(&mMutex, NULL);
}Mutex::~Mutex()
{pthread_mutex_destroy(&mMutex);
}void Mutex::lock()
{pthread_mutex_lock(&mMutex);
}void Mutex::unlock()
{pthread_mutex_unlock(&mMutex);
}MutexLockGuard::MutexLockGuard(Mutex* mutex) :mMutex(mutex)
{mMutex->lock();
}MutexLockGuard::~MutexLockGuard()
{mMutex->unlock();
}

4.代码概览

4.1内存池后端

//Allocator.h
//
// Created by crab on 2024/10/28.
//#ifndef ALLOCATOR_H
#define ALLOCATOR_H#include <cstdint>
#include <cstdlib>
#include <cstring>#include "Mutex.h"//内存池
//单例类,通过getInstance获取唯一实例
class Allocator
{
public:enum {ALIGN = 8};enum {MAX_BYTES = 128};enum {NFREELISTS = MAX_BYTES / ALIGN};union Obj {union Obj* next;char data[1];};static void* allocate(uint32_t size);static void deallocate(void* p, uint32_t size);private:Allocator() : mStartFree(NULL), mEndFree(NULL), mHeapSize(0){mMutex = new Mutex;memset(mFreeList, 0, sizeof(mFreeList));};~Allocator() {};static Allocator* getInstance();void* alloc(uint32_t size);void dealloc(void* p, uint32_t size);/* 获取指定字节数在自由链表的下标 */uint32_t freelistIndex(uint32_t bytes) {return (((bytes) + ALIGN-1) / ALIGN - 1);}/* 字节对齐 */uint32_t roundup(uint32_t bytes) {return (((bytes) + ALIGN-1) & ~(ALIGN - 1));}void *refill(uint32_t bytes);char* chunkAlloc(uint32_t size, int& nobjs);private:static Allocator* mAllocator;Mutex* mMutex;/* 维护缓存块 */char* mStartFree;char* mEndFree;uint32_t mHeapSize;Obj* mFreeList[NFREELISTS];};
#endif //ALLOCATOR_H
//Allocator.cpp
//
// Created by crab on 2024/10/28.
//#include "Allocator.h"
#include <cstdlib>
#include <iostream>Allocator* Allocator::mAllocator = NULL;void* Allocator::allocate(uint32_t size)
{return getInstance()->alloc(size);
}void Allocator::deallocate(void* p, uint32_t size)
{getInstance()->dealloc(p, size);
}Allocator* Allocator::getInstance()
{if(!mAllocator)mAllocator = new Allocator();return mAllocator;
}void* Allocator::alloc(uint32_t size)
{Obj* result;uint32_t index;MutexLockGuard mutexLockGuard(mMutex);/* 如果分配内存大于 MAX_BYTES,那么就直接通过 malloc 分配 */if(size > MAX_BYTES)return malloc(size);index = freelistIndex(size);result = mFreeList[index];/* 如果没有找到则重新分配内存 */if(!result){void* r = refill(roundup(size));return r;}/* 找到了就从链表中删除内存块 */mFreeList[index] = result->next;return result;
}void Allocator::dealloc(void* p, uint32_t size)
{Obj* obj = (Obj*)p;uint32_t index;MutexLockGuard mutexLockGuard(mMutex);/* 如果释放内存大于 MAX_BYTES,那么就直接通过 free 释放 */if(size > MAX_BYTES)free(p);index = freelistIndex(size); //获取该大小在freelist的下标/* 将内存块添加进链表中 */obj->next = mFreeList[index];mFreeList[index] = obj;
}/* 重新分配内存 */
void* Allocator::refill(uint32_t bytes)
{int nobjs = 20;char* chunk = chunkAlloc(bytes, nobjs); //分配内存Obj* result;Obj* currentObj;Obj* nextObj;int i;uint32_t index;/* 如果只有一个节点,那么直接放回,不需要处理剩余内存 */if(1 == nobjs)return chunk;result = (Obj*)chunk;index = freelistIndex(bytes);mFreeList[index] = nextObj = (Obj*)(chunk + bytes);/* 将剩余内存连成链表 */for(i = 1; ; ++i){currentObj = nextObj;nextObj = (Obj*)((char*)nextObj + bytes);if(nobjs-1 == i) //最后一个节点{currentObj->next = 0;break;}else{currentObj->next = nextObj;}}return result;
}char* Allocator::chunkAlloc(uint32_t size, int& nobjs)
{char* result;uint32_t totalBytes = size * nobjs; //总共需求的内存uint32_t bytesLeft = mEndFree - mStartFree; //缓存块中剩余的内存大小if(bytesLeft > totalBytes) //如果缓存块的内存满足需求,则直接从缓存块中获取内存{result = mStartFree;mStartFree += totalBytes;return result;}else if(bytesLeft > size) //如果缓存块剩余大小大于一个节点的大小,则尽可能返回多个节点{nobjs = bytesLeft / size;totalBytes = size * nobjs;result = mStartFree;mStartFree += totalBytes;return result;}else{uint32_t bytesToGet = 2 * totalBytes + roundup(mHeapSize >> 4); //至少两倍增长if(bytesLeft > 0) //如果缓存块还剩余内存,那么它肯定可以插入到某个节点中{uint32_t index = freelistIndex(bytesLeft);((Obj*)(mStartFree))->next = mFreeList[index];mFreeList[index] = (Obj*)mStartFree;}/* 重新申请内存 */mStartFree = (char*)malloc(bytesToGet);mHeapSize += bytesToGet;mEndFree = mStartFree + bytesToGet;/* 递归调用chunkAlloc,重新分配 */return chunkAlloc(size, nobjs);}
}

4.2内存池前端

//Construct.h
//
// Created by crab on 2024/10/28.
//#ifndef CONSTRUCT_H
#define CONSTRUCT_H#include <new>//在特定内存位置上构造或销毁对象,与内存池连用template <class T>
inline void destroy(T* pointer)
{pointer->~T();
}template <class T>
inline void construct(T* p)
{new (p) T();
}template <class T, class T1>
inline void construct(T* p, const T1& a1)
{new (p) T(a1);
}template <class T, class T1, class T2>
inline void construct(T* p, const T1& a1, const T2& a2)
{new (p) T(a1, a2);
}template <class T, class T1, class T2, class T3>
inline void construct(T* p, const T1& a1, const T2& a2, const T3& a3)
{new (p) T(a1, a2, a3);
}template <class T, class T1, class T2, class T3, class T4>
inline void construct(T* p, const T1& a1, const T2& a2, const T3& a3, const T4& a4)
{new (p) T(a1, a2, a3, a4);
}#endif //CONSTRUCT_H
//New.h
//
// Created by crab on 2024/10/28.
//#ifndef NEW_H
#define NEW_H#include "Allocator.h"
#include "Construct.h"#define     ALLOCATOR       Allocatortemplate <class T>
class New
{
public:typedef     T           Value;typedef     T*          Point;typedef     T&          Ref;typedef     ALLOCATOR   Alloc;public:static Point allocate() {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj);return obj;}template <class T1>static Point allocate(const T1& a1) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1);return obj;}template <class T1, class T2>static Point allocate(const T1& a1, const T2& a2) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2);return obj;}template <class T1, class T2, class T3>static Point allocate(const T1& a1, const T2& a2, const T3& a3) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2, a3);return obj;}template <class T1, class T2, class T3, class T4>static Point allocate(const T1& a1, const T2& a2, const T3& a3, const T4& a4) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2, a3, a4);return obj;}
};class Delete
{
public:typedef     ALLOCATOR   Alloc;template <class T1>static void release(T1* point) {destroy(point);Alloc::deallocate(point, sizeof(T1));}};#endif //NEW_H

5.代码解释

1.内存池后端

//
// Created by crab on 2024/10/28.
//#ifndef ALLOCATOR_H
#define ALLOCATOR_H#include <cstdint>
#include <cstdlib>
#include <cstring>#include "Mutex.h"//内存池
//单例类,通过getInstance获取唯一实例
class Allocator
{
public://ALIGN:内存块的对齐单位常量enum {ALIGN = 8};//Max_Bytes:内存块的最大字节数enum {MAX_BYTES = 128};//NFREELISTS:自由链表数量enum {NFREELISTS = MAX_BYTES / ALIGN};//一个union联合体,用来作为内存块节点的数据结构。 union Obj {union Obj* next;char data[1];};//根据请求的size的分配内存static void* allocate(uint32_t size);//释放指定大小的内存块pstatic void deallocate(void* p, uint32_t size);private://构造函数,初始化内存池的起始和结束指针mStartFree和mEndFree,堆大小mHeapSize,分配一个Mutex对象管理线程安全Allocator() : mStartFree(NULL), mEndFree(NULL), mHeapSize(0){mMutex = new Mutex;memset(mFreeList, 0, sizeof(mFreeList));};~Allocator() {};//静态方法用来获取Allocator的唯一实例static Allocator* getInstance();//内存分配void* alloc(uint32_t size);//内存释放void dealloc(void* p, uint32_t size);/* 获取指定字节数在自由链表的下标 *///快速找到该大小的链表头,如:16字节的内存:(16 + 8 - 1)/ 8 - 1= 1 uint32_t freelistIndex(uint32_t bytes) {return (((bytes) + ALIGN-1) / ALIGN - 1);}/* 字节对齐 *///将给定的字节数向上取整为8的倍数,实现快速对齐 //eg: 14字节: (14+8-1)~(8-1)=16 : ~(8-1) 7的二进制取反:11111000, 然后将21与11111000进行与运算,结果为16uint32_t roundup(uint32_t bytes) {return (((bytes) + ALIGN-1) & ~(ALIGN - 1));}//补充指定大小的内存块void *refill(uint32_t bytes);//从堆上分配多个内存块char* chunkAlloc(uint32_t size, int& nobjs);private://Allocator的静态实例static Allocator* mAllocator;//锁Mutex* mMutex;/* 维护缓存块 */char* mStartFree;char* mEndFree;uint32_t mHeapSize;//指针数组,用来维护内存块链表Obj* mFreeList[NFREELISTS];};
#endif //ALLOCATOR_H
//
// Created by crab on 2024/10/28.
//#include "Allocator.h"
#include <cstdlib>
#include <iostream>Allocator* Allocator::mAllocator = NULL;void* Allocator::allocate(uint32_t size)
{//通过Allocator的Instance调用alloc分配size大小的内存块return getInstance()->alloc(size);
}void Allocator::deallocate(void* p, uint32_t size)
{//delloc释放getInstance()->dealloc(p, size);
}Allocator* Allocator::getInstance()
{if(!mAllocator)mAllocator = new Allocator();return mAllocator;
}//分配内存,如果需要的内存大于MAX_BYTES,直接用malloc分配
void* Allocator::alloc(uint32_t size)
{Obj* result;uint32_t index;//加锁,确保线程安全MutexLockGuard mutexLockGuard(mMutex);/* 如果分配内存大于 MAX_BYTES,那么就直接通过 malloc 分配 */if(size > MAX_BYTES)return malloc(size);//获取内存块在链表数组中的位置,然后从mFreeList中获取对应链表的上的内存块index = freelistIndex(size);result = mFreeList[index];/* 如果没有找到则重新分配内存 */if(!result){void* r = refill(roundup(size));return r;}/* 找到了就从链表中删除内存块 */mFreeList[index] = result->next;return result;
}void Allocator::dealloc(void* p, uint32_t size)
{Obj* obj = (Obj*)p;uint32_t index;MutexLockGuard mutexLockGuard(mMutex);/* 如果释放内存大于 MAX_BYTES,那么就直接通过 free 释放 */if(size > MAX_BYTES)free(p);index = freelistIndex(size); //获取该大小在freelist的下标/* 将内存块添加进链表中 */obj->next = mFreeList[index];mFreeList[index] = obj;
}/* 重新分配内存 */
void* Allocator::refill(uint32_t bytes)
{int nobjs = 20;char* chunk = chunkAlloc(bytes, nobjs); //分配内存Obj* result;Obj* currentObj;Obj* nextObj;int i;uint32_t index;/* 如果只有一个节点,那么直接放回,不需要处理剩余内存 */if(1 == nobjs)return chunk;result = (Obj*)chunk;index = freelistIndex(bytes);mFreeList[index] = nextObj = (Obj*)(chunk + bytes);/* 将剩余内存连成链表 */for(i = 1; ; ++i){currentObj = nextObj;nextObj = (Obj*)((char*)nextObj + bytes);if(nobjs-1 == i) //最后一个节点{currentObj->next = 0;break;}else{currentObj->next = nextObj;}}return result;
}char* Allocator::chunkAlloc(uint32_t size, int& nobjs)
{char* result;uint32_t totalBytes = size * nobjs; //总共需求的内存uint32_t bytesLeft = mEndFree - mStartFree; //缓存块中剩余的内存大小if(bytesLeft > totalBytes) //如果缓存块的内存满足需求,则直接从缓存块中获取内存{result = mStartFree;mStartFree += totalBytes;return result;}else if(bytesLeft > size) //如果缓存块剩余大小大于一个节点的大小,则尽可能返回多个节点{nobjs = bytesLeft / size;totalBytes = size * nobjs;result = mStartFree;mStartFree += totalBytes;return result;}else{uint32_t bytesToGet = 2 * totalBytes + roundup(mHeapSize >> 4); //至少两倍增长if(bytesLeft > 0) //如果缓存块还剩余内存,那么它肯定可以插入到某个节点中{uint32_t index = freelistIndex(bytesLeft);((Obj*)(mStartFree))->next = mFreeList[index];mFreeList[index] = (Obj*)mStartFree;}/* 重新申请内存 */mStartFree = (char*)malloc(bytesToGet);mHeapSize += bytesToGet;mEndFree = mStartFree + bytesToGet;/* 递归调用chunkAlloc,重新分配 */return chunkAlloc(size, nobjs);}
}
//Construct.h
// Created by crab on 2024/10/28.
//#ifndef CONSTRUCT_H
#define CONSTRUCT_H#include <new>//在特定内存位置上构造或销毁对象,与内存池连用template <class T>
inline void destroy(T* pointer)
{pointer->~T();
}template <class T>
inline void construct(T* p)
{new (p) T();
}template <class T, class T1>
inline void construct(T* p, const T1& a1)
{new (p) T(a1);
}template <class T, class T1, class T2>
inline void construct(T* p, const T1& a1, const T2& a2)
{new (p) T(a1, a2);
}template <class T, class T1, class T2, class T3>
inline void construct(T* p, const T1& a1, const T2& a2, const T3& a3)
{new (p) T(a1, a2, a3);
}template <class T, class T1, class T2, class T3, class T4>
inline void construct(T* p, const T1& a1, const T2& a2, const T3& a3, const T4& a4)
{new (p) T(a1, a2, a3, a4);
}#endif //CONSTRUCT_H
//New.h
// Created by crab on 2024/10/28.
//#ifndef NEW_H
#define NEW_H#include "Allocator.h"
#include "Construct.h"#define     ALLOCATOR       Allocatortemplate <class T>
class New
{
public:typedef     T           Value;typedef     T*          Point;typedef     T&          Ref;typedef     ALLOCATOR   Alloc;public:static Point allocate() {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj);return obj;}template <class T1>static Point allocate(const T1& a1) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1);return obj;}template <class T1, class T2>static Point allocate(const T1& a1, const T2& a2) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2);return obj;}template <class T1, class T2, class T3>static Point allocate(const T1& a1, const T2& a2, const T3& a3) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2, a3);return obj;}template <class T1, class T2, class T3, class T4>static Point allocate(const T1& a1, const T2& a2, const T3& a3, const T4& a4) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2, a3, a4);return obj;}
};class Delete
{
public:typedef     ALLOCATOR   Alloc;template <class T1>static void release(T1* point) {destroy(point);Alloc::deallocate(point, sizeof(T1));}};#endif //NEW_H

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32设计学生宿舍监测控制系统

目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 电路图采用Altium Designer进行设计&#xff1a; 三、实物设计图 四、程序源代码设计 五、获取资料内容 前言 随着科技的飞速发展和智能化时代的到来&#xff0c;学生宿舍的安全、舒适…

企业如何提高招聘能力?

企业如何提高招聘能力&#xff1f; 许多企业在进行招聘工作时&#xff0c;常常会遇到各种问题和挑战。尽管付出了大量的时间和精力&#xff0c;但结果却并不总是如人意。例如&#xff0c;企业可能会经历一次又一次的面试&#xff0c;却仍然找不到一个能够适应岗位要求的合适人…

大模型在蓝鲸运维体系应用——蓝鲸运维开发智能助手

本文来自腾讯蓝鲸智云社区用户: CanWay 背景 1、运维转型背景 蓝鲸平台从诞生之初&#xff0c;就一直在不遗余力地推动运维转型&#xff0c;让运维团队可以通过一体化PaaS平台&#xff0c;快速编写脚本&#xff0c;编排流程&#xff0c;开发运维工具&#xff0c;从被动地提供…

3588 yolov8 onnx 量化转 rknn 并运行

本教程重点不在如何训练模型&#xff0c;重点是全流程链路&#xff0c;想学训练的可以网上找教程 环境 python 3.10.xrknn-toolkit2-2.2.0ultralytics_yolov8rknn 驱动版本2.2 模型训练 yolov8仓库地址&#xff1a;https://github.com/airockchip/ultralytics_yolov8.git下载…

Vue 组件通信及进阶语法

文章目录 一、scoped 样式冲突二、data 是一个函数三、组件通信1. 父子通信1.1 props 校验1.2 props 比较 data 2. 非父子通信2.1 event bus2.2 provide-inject 四、进阶语法1. v-model 详解2. sync 修饰符3. ref 和 $refs4. $nextTick 一、scoped 样式冲突 注意点&#xff1a;…

LeetCode105.从前序与中序遍历构造二叉树

题目要求 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 提示: 1 < preorder.length < 3000inorder.length preorder.length-3000 < pr…

【问卷调研】HarmonyOS SDK开发者社区用户需求有奖调研

问卷请点击&#xff1a;HarmonyOS SDK开发者社区用户需求有奖调研

IOT物联网低代码可视化大屏解决方案汇总

目录 参考来源云服务商阿里云物联网平台产品主页产品文档 开源项目DGIOT | 轻量级工业物联网开源平台项目特点项目地址开源许可 IoTGateway | 基于.NET6的跨平台工业物联网网关项目特点项目地址开源许可 IoTSharp | 基于.Net Core开源的物联网基础平台项目特点项目地址开源许可…

如何在Mac上切换到JDK 17开发环境

在本文中&#xff0c;我将为您介绍如何在Mac上切换到JDK 17&#xff0c;包括下载和安装JDK 17、设置环境变量、在IntelliJ IDEA中配置项目、修改Maven编译配置&#xff0c;并最终使用mvn clean install重新编译项目。通过这个流程&#xff0c;您可以顺利地将开发环境升级到JDK …

玩转ChatGPT:文献阅读 v2.0

一、写在前面 好久不更新咯。 因为最近ChatGPT更新了不少功能&#xff08;水一篇刷存在感&#xff09;&#xff1a; 上线ChatGPT-4o模型&#xff0c;说推理能力还不错&#xff1b;上线联网功能&#xff0c;类似Kimi那种。 所以呢&#xff0c;用它来读文献就挺舒服的了。例如…

自动驾驶系列—从数据采集到存储:解密自动驾驶传感器数据采集盒子的关键技术

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

HarmonyOS本地存储-Preferences(用户首选项)的使用

一&#xff0c;用户首选项简述 ohos.data.preferences (用户首选项) 用户首选项为应用提供Key-Value键值型的数据处理能力&#xff0c;支持应用持久化轻量级数据&#xff0c;并对其修改和查询。 数据存储形式为键值对&#xff0c;键的类型为字符串型&#xff0c;值的存储数据…

SpringCloud 微服务消息队列灰度方案 (RocketMQ 4.x)

目录 背景遇到的问题 RocketMQ 基础基础消息模型扩展后的消息模型部署模型相关概念点 方案对比影子Topic的方案Tag的方案UserProperty的方案影子Group的方案灰度分区的方案方案对比 灰度分区方案设计适配只有部分灰度的情况所做的功能扩展消费者&#xff08;无灰度&#xff09;…

「QT」文件类 之 QDataStream 数据流类

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「QT」QT5程序设计&#x1f4da;全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C」C/C程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定制…

QT<30> Qt中使鼠标变为转圈忙状态

前言&#xff1a;当我们在写软件时&#xff0c;在等待阻塞耗时操作时可以将鼠标变为忙状态&#xff0c;并在一段时间后恢复状态&#xff0c;可以用到GxtWaitCursor&#xff1a;Qt下基于RAII的鼠标等待光标类。 一、效果演示 二、详细代码 在项目中添加C文件&#xff0c;命名为…

Vue的基础使用

一、为什么要学习Vue 1.前端必备技能 2.岗位多&#xff0c;绝大互联网公司都在使用Vue 3.提高开发效率 4.高薪必备技能&#xff08;Vue2Vue3&#xff09; 二、什么是Vue 概念&#xff1a;Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套 构建用户界面 的 渐进式 框架…

数据结构Python版

2.3.3 双链表 双链表和链表一样&#xff0c;只不过每个节点有两个链接——一个指向后一个节点&#xff0c;一个指向前一个节点。此外&#xff0c;除了第一个节点&#xff0c;双链表还需要记录最后一个节点。 每个结点为DLinkNode类对象&#xff0c;包括存储元素的列表data、…

FBX福币交易所恒指收跌1.96% 半导体股继续回调

查查配分析11月14日电 周四,港股三大指数集体下跌。截至收盘,恒生指数跌1.96%,恒生科技指数跌3.08%,恒生中国企业指数跌2.21%。大市成交额1733亿港元。 FBX福币凭借用户友好的界面和对透明度的承诺,迅速在加密货币市场中崭露头角,成为广大用户信赖的平台。 来源:Wind 盘面上,科…

【学习日记】notebook添加JAVA支持

作者是个大学生 这个专栏主要收集课时常用的软件 以及女朋友上课用的软件的教程 新开了gitcode 用于上传安装包 环境说明 windows11 java23.0.1 ijava1.1.2 Anaconda-2024.02 需提前配置好java环境 本篇仅对添加支持进行说明 ijava的GitCode链接NotebookAddsSupportForJava:no…

RabbitMQ 篇-深入了解延迟消息、MQ 可靠性(生产者可靠性、MQ 可靠性、消费者可靠性)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 RabbitMQ 的可靠性 2.0 发送者的可靠性 2.1 生产者重试机制 2.2 生产者确认机制 2.2.1 开启生产者确认机制 2.2.2 定义 ReturnCallback 机制 2.2.3 定义 ConfirmC…