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_t
    pthread_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       Allocator

template <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);
	//释放指定大小的内存块p
    static 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进行与运算,结果为16
    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:
	//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       Allocator

template <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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式-参考的雷丰阳老师直播课

一般开发中使用的模式为模版模式策略模式组合&#xff0c;模版用来定义骨架&#xff0c;策略用来实现细节。 模版模式 策略模式 与模版模式特别像&#xff0c;模版模式会定义好步骤定义好框架&#xff0c;策略模式定义小细节 入口类 使用模版模式策略模式开发支付 以上使用…

Vue3打包自动生成版本JSON文件,添加系统版本检查,实现系统自动更新提示

实现该功能一共有三步。废话不多说&#xff0c;直接上代码&#xff01;&#xff01;&#xff01; 第一步&#xff1a;打包时自动生成版本信息的js文件&#xff0c;versionUpdate.js import fs from fs; import path from path; import { ElMessageBox } from element-plus; i…

华为云前台展示公网访问需要购买EIP,EIP流量走向

华为云前台网络&#xff08;VPC,安全组&#xff0c;EIP&#xff09; 1.EIP网段是从哪里划分的&#xff1f; 管理员在后台Service_OM已设置 Service_OM-网络资源-外部网络-创建外部网络基本信息&#xff1a;配置参数&#xff1a;*名称 public*网络类型 LOCAL 不带标签 类似开…

[Mysql基础] 表的操作

一、创建表 1.1 语法 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; 说明&#xff1a; field 表示列名 datatype 表示列的类型 character set 字符集&#xff0c;如果没有指定字符集…

ASP.NET MVC宠物商城系统

该系统采用B/S架构&#xff0c;使用C#编程语言进行开发&#xff0c;以ASP.NET MVC框架为基础&#xff0c;以Visual Studio 2019为开发工具&#xff0c;数据库采用SQL Server进行保存数据。系统主要功能包括登录注册、宠物展示、个人中心、我的订单、购物车、用户管理、宠物类别…

ArkTS学习笔记:ArkTS起步

ArkTS是HarmonyOS的主力应用开发语言&#xff0c;基于TypeScript扩展&#xff0c;强化了静态检查和分析&#xff0c;旨在提升程序稳定性和性能。它采用静态类型&#xff0c;禁止运行时改变对象布局&#xff0c;并对UI开发框架能力进行扩展&#xff0c;支持声明式UI描述和自定义…

Tofu AI视频处理模块视频输入配置方法

应用Tofu产品对网络视频进行获取做视频处理时&#xff0c;首先需要配置Tofu产品的硬件连接关系与设备IP地址、视频拉流地址。 步骤1 Tofu设备点对点直连或者通过交换机连接到电脑&#xff0c;电脑IP配置到与Tofu默认IP地址同一个网段。 打开软件 点击右上角系统设置 单击左侧…

stream学习

Stream流 定义 Steam流&#xff0c;用于操作集合或者数组中的数据&#xff0c;大量结合了Lamda表达式的语法风格&#xff0c;代码简洁。 重点&#xff1a; 流只能收集一次 ​ 获取Stream流 Stream流要与数据源建立连接。 1.list ​ 直接调用steam()即可 // list List<Stri…

鸿蒙动画开发06——打断动画

1、前 言 UI界面除了运行动画之外&#xff0c;还承载着与用户进行实时交互的功能。当用户行为根据意图变化发生改变时&#xff0c;UI界面应做到即时响应。 例如用户在应用启动过程中&#xff0c;上滑退出&#xff0c;那么启动动画应该立即过渡到退出动画&#xff0c;而不应该…

运算放大器的学习(一)输入阻抗

输入阻抗 最近需要对运算放大器进行学习&#xff0c;我们后面逐一对其参数进行了解。 首先了解下输入阻抗。 放大电路技术指标测试示意图&#xff1a; 输入电阻&#xff1a; 从放大电路的输入端看进去的等效电阻称为放大电路的输入电阻&#xff0c;如上图&#xff0c;此处考虑…

【测试框架篇】单元测试框架pytest(1):环境安装和配置

一、pytest简介 Pytest是Python的一种单元测试框架&#xff0c;与Python自带的unittest测试框架类似&#xff0c;但是比 unittest框架使用起来更简洁&#xff0c;效率更高。 二、pytest特点 Pytest是一个非常成熟的Python测试框架,主要特点有以下几点&#xff1a; 非常容易…

蓝桥杯竞赛单片机组备赛【经验帖】

本人获奖情况说明 笔者本人曾参加过两次蓝桥杯电子赛&#xff0c;在第十二届蓝桥杯大赛单片机设计与开发组获得省级一等奖和国家级二等奖&#xff0c;在第十五届嵌入式设计开发组获得省级二等奖。如果跟着本帖的流程备赛&#xff0c;只要认真勤奋&#xff0c;拿个省二绝对没问…

力扣 LeetCode 541. 反转字符串II(Day4:字符串)

解题思路&#xff1a; i可以成段成段的跳&#xff0c;而不是简单的i class Solution {public String reverseStr(String s, int k) {char[] ch s.toCharArray();// 1. 每隔 2k 个字符的前 k 个字符进行反转for (int i 0; i < ch.length; i 2 * k) {// 2. 剩余字符小于 …

密码学知识点整理二:常见的加密算法

常用的加密算法包括对称加密算法、非对称加密算法和散列算法。 对称加密算法 AES&#xff1a;高级加密标准&#xff0c;是目前使用最广泛的对称加密算法之一&#xff0c;支持多种密钥长度&#xff08;128位、192位、256位&#xff09;&#xff0c;安全性高&#xff0c;加密效率…

css:修改盒子样式

圆角边框 在css3中新增了圆角边框样式&#xff0c;这样我们的盒子就可以长得奇形怪状了 像csdn上的发布就是圆角边框 还有这些 .x,.y {background-color: cornflowerblue;width: 200px;height: 200px;margin: 0 auto;text-align: center;border-radius: 10px;} 10px是什么意思…

连续九届EI稳定|江苏科技大学主办

【九届EI检索稳定|江苏科技大学主办 | IEEE出版 】 &#x1f388;【截稿倒计时】&#xff01;&#xff01;&#xff01; ✨徐秘书&#xff1a;gsra_huang ✨往届均已检索&#xff0c;已上线IEEE官网 &#x1f38a;第九届清洁能源与发电技术国际学术会议&#xff08;CEPGT 2…

机器学习 - 为 Jupyter Notebook 安装新的 Kernel

https://ipython.readthedocs.io/en/latest/install/kernel_install.html 当使用jupyter-notebook --no-browser 启动一个 notebook 时&#xff0c;默认使用了该 jupyter module 所在的 Python 环境作为 kernel&#xff0c;比如 C:\devel\Python\Python311。 如果&#xff0c…

DVWA靶场通关——SQL Injection篇

一&#xff0c;Low难度下unionget字符串select注入 1&#xff0c;首先手工注入判断是否存在SQL注入漏洞&#xff0c;输入1 这是正常回显的结果&#xff0c;再键入1 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for…

无人机飞手执照处处需要,森林、石油管道、电力巡检等各行业都需要

无人机飞手执照在多个行业中确实具有广泛的应用需求&#xff0c;包括森林、石油管道、电力巡检等领域。以下是对这些领域无人机飞手执照需求的具体分析&#xff1a; 一、森林领域 在森林领域&#xff0c;无人机飞手执照对于进行高效、准确的森林资源管理和监测至关重要。无人机…

基于YOLO11/v10/v8/v5深度学习的水面垃圾智能检测识别系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…