19_内存管理

内存管理

做好内存管理,可以避免内存碎片的产生,是程序长期稳定,高效的运行

  1. 内存池(防止内存碎片产生)

    从系统中申请足够大小的内存,由程序自己管理

  2. 对象池(防止内存碎片产生)

    创建足够多的对象,减少创建释放对象的消耗

  3. 智能指针(防止内存未被释放)

    保证被创建的对象可以正确的释放

创建自己管理的内存池,内存池的实现和stl的allocator基本一致,原理可以直接观看stl的allocator

使用内存池的原因主要是:

  1. 减少内存碎片:频繁地使用newdelete会导致内存碎片化,这会降低程序和操作系统的性能。内存池通过预先分配一大块内存并逐渐使用,可以有效减少内存碎片的问题1
  2. 提高申请效率:与操作系统频繁交互申请和释放内存会降低效率。内存池允许程序直接从预分配的内存中快速分配和回收内存,从而提高内存分配的效率2
  3. 减少系统调用:直接使用mallocfree等API会涉及到系统调用,而系统调用是昂贵的操作。内存池减少了系统调用的次数,因此可以提高性能3
  4. 提高缓存命中率:内存池可以使得频繁使用的对象在物理内存中保持相对集中,这有助于提高CPU缓存的命中率,进而提高程序运行效率4
  5. 更好的控制内存使用:内存池可以帮助开发者更好地控制和监视程序的内存使用情况,避免过度申请内存导致的资源浪费5

创建allocator文件作为接口

#include"Alloctor.h"
#include"MemoryMgr.hpp"

void* operator new(size_t nSize)
{
    return MemoryMgr::Instance().allocMem(nSize);
}

void operator delete(void* p)
{
    MemoryMgr::Instance().freeMem(p);
}

void* operator new[](size_t nSize)
{
    return MemoryMgr::Instance().allocMem(nSize);
}

void operator delete[](void* p)
{
    MemoryMgr::Instance().freeMem(p);
}

void* mem_alloc(size_t size)
{
    return malloc(size);
}

void mem_free(void* p)
{
    free(p);
}

创建MemoryMgr内存池管理器

#ifndef _MemoryMgr_hpp_
#define _MemoryMgr_hpp_
#include<stdlib.h>
#include<assert.h>

#ifdef _DEBUG
#include<stdio.h>
    #define xPrintf(...) printf(__VA_ARGS__)
#else
    #define xPrintf(...)
#endif // _DEBUG


#define MAX_MEMORY_SZIE 1024

class MemoryAlloc;
//内存块 最小单元
class MemoryBlock
{
public:
    //所属大内存块(池)
    MemoryAlloc* pAlloc;
    //下一块位置
    MemoryBlock* pNext;
    //内存块编号
    int nID;
    //引用次数
    int nRef;
    //是否在内存池中
    bool bPool;
private:
    //预留
    char c1;
    char c2;
    char c3;
};

//内存池
class MemoryAlloc
{
public:
    MemoryAlloc()
    {
        _pBuf = nullptr;
        _pHeader = nullptr;
        _nSzie = 0;
        _nBlockSzie = 0;
    }

    ~MemoryAlloc()
    {
        if (_pBuf)
            free(_pBuf);
    }

    //申请内存
    void* allocMemory(size_t nSize)
    {
        if (!_pBuf)
        {
            initMemory();
        }

        MemoryBlock* pReturn = nullptr;
        if (nullptr == _pHeader)
        {
            pReturn = (MemoryBlock*)malloc(nSize+sizeof(MemoryBlock));
            pReturn->bPool = false;
            pReturn->nID = -1;
            pReturn->nRef = 1;
            pReturn->pAlloc = nullptr;
            pReturn->pNext = nullptr;
        }
        else {
            pReturn = _pHeader;
            _pHeader = _pHeader->pNext;
            assert(0 == pReturn->nRef);
            pReturn->nRef = 1;
        }
        xPrintf("allocMem: %llx, id=%d, size=%d\n", pReturn, pReturn->nID, nSize);
        return ((char*)pReturn + sizeof(MemoryBlock));
    }

    //释放内存
    void freeMemory(void* pMem)
    {
        MemoryBlock* pBlock = (MemoryBlock*)( (char*)pMem  - sizeof(MemoryBlock));
        assert(1 == pBlock->nRef);
        if (--pBlock->nRef != 0)
        {
            return;
        }
        if (pBlock->bPool)
        {
            pBlock->pNext = _pHeader;
            _pHeader = pBlock;
        }
        else {
            free(pBlock);
        }
    }

    //初始化
    void initMemory()
    {   //断言
        assert(nullptr == _pBuf);
        if (_pBuf)
            return;
        //计算内存池的大小
        size_t realSzie = _nSzie + sizeof(MemoryBlock);
        size_t bufSize = realSzie*_nBlockSzie;
        //向系统申请池的内存
        _pBuf = (char*)malloc(bufSize);

        //初始化内存池
        _pHeader = (MemoryBlock*)_pBuf;
        _pHeader->bPool = true;
        _pHeader->nID = 0;
        _pHeader->nRef = 0;
        _pHeader->pAlloc = this;
        _pHeader->pNext = nullptr;
        //遍历内存块进行初始化
        MemoryBlock* pTemp1 = _pHeader;

        for (size_t n = 1; n < _nBlockSzie; n++)
        {
            MemoryBlock* pTemp2 = (MemoryBlock*)(_pBuf + (n* realSzie));
            pTemp2->bPool = true;
            pTemp2->nID = n;
            pTemp2->nRef = 0;
            pTemp2->pAlloc = this;
            pTemp2->pNext = nullptr;
            pTemp1->pNext = pTemp2;
            pTemp1 = pTemp2;
        }
    }
protected:
    //内存池地址
    char* _pBuf;
    //头部内存单元
    MemoryBlock* _pHeader;
    //内存单元的大小
    size_t _nSzie;
    //内存单元的数量
    size_t _nBlockSzie;
};

//便于在声明类成员变量时初始化MemoryAlloc的成员数据
template<size_t nSzie,size_t nBlockSzie>
class MemoryAlloctor :public MemoryAlloc
{
public:
    MemoryAlloctor()
    {
        //8 4   61/8=7  61%8=5
        const size_t n = sizeof(void*);
        //(7*8)+8 
        _nSzie = (nSzie/n)*n +(nSzie % n ? n : 0);
        _nBlockSzie = nBlockSzie;
    }

};

//内存管理工具
class MemoryMgr
{
private:
    MemoryMgr()
    {
        init_szAlloc(0, 64, &_mem64);
        init_szAlloc(65, 128, &_mem128);
        init_szAlloc(129, 256, &_mem256);
        init_szAlloc(257, 512, &_mem512);
        init_szAlloc(513, 1024, &_mem1024);
    }

    ~MemoryMgr()
    {

    }

public:
    static MemoryMgr& Instance()
    {//单例模式 静态
        static MemoryMgr mgr;
        return mgr;
    }
    //申请内存
    void* allocMem(size_t nSize)
    {
        if (nSize <= MAX_MEMORY_SZIE)
        {
            return _szAlloc[nSize]->allocMemory(nSize);
        }
        else 
        {
            MemoryBlock* pReturn = (MemoryBlock*)malloc(nSize + sizeof(MemoryBlock));
            pReturn->bPool = false;
            pReturn->nID = -1;
            pReturn->nRef = 1;
            pReturn->pAlloc = nullptr;
            pReturn->pNext = nullptr;
            xPrintf("allocMem: %llx, id=%d, size=%d\n", pReturn , pReturn->nID, nSize);
            return ((char*)pReturn + sizeof(MemoryBlock));
        }

    }

    //释放内存
    void freeMem(void* pMem)
    {
        MemoryBlock* pBlock = (MemoryBlock*)((char*)pMem - sizeof(MemoryBlock));
        xPrintf("freeMem: %llx, id=%d\n", pBlock, pBlock->nID);
        if (pBlock->bPool)
        {
            pBlock->pAlloc->freeMemory(pMem);
        }
        else 
        {
            if (--pBlock->nRef == 0)
                free(pBlock);
        }
    }

    //增加内存块的引用计数
    void addRef(void* pMem)
    {
        MemoryBlock* pBlock = (MemoryBlock*)((char*)pMem - sizeof(MemoryBlock));
        ++pBlock->nRef;
    }
private:
    //初始化内存池映射数组
    void init_szAlloc(int nBegin, int nEnd, MemoryAlloc* pMemA)
    {
        for (int n = nBegin; n <= nEnd; n++)
        {
            _szAlloc[n] = pMemA;
        }
    }
private:
    MemoryAlloctor<64, 100000> _mem64;
    MemoryAlloctor<128, 100000> _mem128;
    MemoryAlloctor<256, 100000> _mem256;
    MemoryAlloctor<512, 100000> _mem512;
    MemoryAlloctor<1024, 100000> _mem1024;
    MemoryAlloc* _szAlloc[MAX_MEMORY_SZIE + 1];
};

#endif // !_MemoryMgr_hpp_

创建main对内存池调试

#include<stdlib.h>
#include"Alloctor.h"

int main()
{
    char* data[1100];
    for (size_t i = 0; i < 1100; i++)
    {
        data[i] = new char[1+i];
    }
    for (size_t i = 0; i < 1100; i++)
    {
        delete[] data[i];
    }
    return 0;
}

多线程下的内存池修改

#ifndef _MemoryMgr_hpp_
#define _MemoryMgr_hpp_
#include<stdlib.h>
#include<assert.h>
#include<mutex>//锁

#ifdef _DEBUG
#include<stdio.h>
    #define xPrintf(...) printf(__VA_ARGS__)
#else
    #define xPrintf(...)
#endif // _DEBUG


#define MAX_MEMORY_SZIE 1024

class MemoryAlloc;
//内存块 最小单元
class MemoryBlock
{
public:
    //所属大内存块(池)
    MemoryAlloc* pAlloc;
    //下一块位置
    MemoryBlock* pNext;
    //内存块编号
    int nID;
    //引用次数
    int nRef;
    //是否在内存池中
    bool bPool;
private:
    //预留
    char c1;
    char c2;
    char c3;
};

//内存池
class MemoryAlloc
{
public:
    MemoryAlloc()
    {
        _pBuf = nullptr;
        _pHeader = nullptr;
        _nSzie = 0;
        _nBlockSzie = 0;
        xPrintf("MemoryAlloc\n");
    }

    ~MemoryAlloc()
    {
        if (_pBuf)
            free(_pBuf);
    }

    //申请内存
    void* allocMemory(size_t nSize)
    {
        std::lock_guard<std::mutex> lg(_mutex);
        if (!_pBuf)
        {
            initMemory();
        }

        MemoryBlock* pReturn = nullptr;
        if (nullptr == _pHeader)
        {
            pReturn = (MemoryBlock*)malloc(nSize+sizeof(MemoryBlock));
            pReturn->bPool = false;
            pReturn->nID = -1;
            pReturn->nRef = 1;
            pReturn->pAlloc = nullptr;
            pReturn->pNext = nullptr;
        }
        else {
            pReturn = _pHeader;
            _pHeader = _pHeader->pNext;
            assert(0 == pReturn->nRef);
            pReturn->nRef = 1;
        }
        //xPrintf("allocMem: %llx, id=%d, size=%d\n", pReturn, pReturn->nID, nSize);
        return ((char*)pReturn + sizeof(MemoryBlock));
    }

    //释放内存
    void freeMemory(void* pMem)
    {
        MemoryBlock* pBlock = (MemoryBlock*)( (char*)pMem  - sizeof(MemoryBlock));
        assert(1 == pBlock->nRef);
        if (pBlock->bPool)
        {
            std::lock_guard<std::mutex> lg(_mutex);
            if (--pBlock->nRef != 0)
            {
                return;
            }
            pBlock->pNext = _pHeader;
            _pHeader = pBlock;
        }
        else {
            if (--pBlock->nRef != 0)
            {
                return;
            }
            free(pBlock);
        }
    }

    //初始化
    void initMemory()
    {
        xPrintf("initMemory:_nSzie=%d,_nBlockSzie=%d\n", _nSzie, _nBlockSzie);
        //断言
        assert(nullptr == _pBuf);
        if (_pBuf)
            return;
        //计算内存池的大小
        size_t realSzie = _nSzie + sizeof(MemoryBlock);
        size_t bufSize = realSzie*_nBlockSzie;
        //向系统申请池的内存
        _pBuf = (char*)malloc(bufSize);

        //初始化内存池
        _pHeader = (MemoryBlock*)_pBuf;
        _pHeader->bPool = true;
        _pHeader->nID = 0;
        _pHeader->nRef = 0;
        _pHeader->pAlloc = this;
        _pHeader->pNext = nullptr;
        //遍历内存块进行初始化
        MemoryBlock* pTemp1 = _pHeader;

        for (size_t n = 1; n < _nBlockSzie; n++)
        {
            MemoryBlock* pTemp2 = (MemoryBlock*)(_pBuf + (n* realSzie));
            pTemp2->bPool = true;
            pTemp2->nID = n;
            pTemp2->nRef = 0;
            pTemp2->pAlloc = this;
            pTemp2->pNext = nullptr;
            pTemp1->pNext = pTemp2;
            pTemp1 = pTemp2;
        }
    }
protected:
    //内存池地址
    char* _pBuf;
    //头部内存单元
    MemoryBlock* _pHeader;
    //内存单元的大小
    size_t _nSzie;
    //内存单元的数量
    size_t _nBlockSzie;
    std::mutex _mutex;
};

//便于在声明类成员变量时初始化MemoryAlloc的成员数据
template<size_t nSzie,size_t nBlockSzie>
class MemoryAlloctor :public MemoryAlloc
{
public:
    MemoryAlloctor()
    {
        //8 4   61/8=7  61%8=5
        const size_t n = sizeof(void*);
        //(7*8)+8 
        _nSzie = (nSzie/n)*n +(nSzie % n ? n : 0);
        _nBlockSzie = nBlockSzie;
    }

};

//内存管理工具
class MemoryMgr
{
private:
    MemoryMgr()
    {
        init_szAlloc(0, 64, &_mem64);
        init_szAlloc(65, 128, &_mem128);
        init_szAlloc(129, 256, &_mem256);
        init_szAlloc(257, 512, &_mem512);
        init_szAlloc(513, 1024, &_mem1024);
        xPrintf("MemoryMgr\n");
    }

    ~MemoryMgr()
    {

    }

public:
    static MemoryMgr& Instance()
    {//单例模式 静态
        static MemoryMgr mgr;
        return mgr;
    }
    //申请内存
    void* allocMem(size_t nSize)
    {
        if (nSize <= MAX_MEMORY_SZIE)
        {
            return _szAlloc[nSize]->allocMemory(nSize);
        }
        else 
        {
            MemoryBlock* pReturn = (MemoryBlock*)malloc(nSize + sizeof(MemoryBlock));
            pReturn->bPool = false;
            pReturn->nID = -1;
            pReturn->nRef = 1;
            pReturn->pAlloc = nullptr;
            pReturn->pNext = nullptr;
            //xPrintf("allocMem: %llx, id=%d, size=%d\n", pReturn , pReturn->nID, nSize);
            return ((char*)pReturn + sizeof(MemoryBlock));
        }

    }

    //释放内存
    void freeMem(void* pMem)
    {
        MemoryBlock* pBlock = (MemoryBlock*)((char*)pMem - sizeof(MemoryBlock));
        //xPrintf("freeMem: %llx, id=%d\n", pBlock, pBlock->nID);
        if (pBlock->bPool)
        {
            pBlock->pAlloc->freeMemory(pMem);
        }
        else 
        {
            if (--pBlock->nRef == 0)
                free(pBlock);
        }
    }

    //增加内存块的引用计数
    void addRef(void* pMem)
    {
        MemoryBlock* pBlock = (MemoryBlock*)((char*)pMem - sizeof(MemoryBlock));
        ++pBlock->nRef;
    }
private:
    //初始化内存池映射数组
    void init_szAlloc(int nBegin, int nEnd, MemoryAlloc* pMemA)
    {
        for (int n = nBegin; n <= nEnd; n++)
        {
            _szAlloc[n] = pMemA;
        }
    }
private:
    MemoryAlloctor<64, 100000> _mem64;
    MemoryAlloctor<128, 100000> _mem128;
    MemoryAlloctor<256, 100000> _mem256;
    MemoryAlloctor<512, 100000> _mem512;
    MemoryAlloctor<1024, 100000> _mem1024;
    MemoryAlloc* _szAlloc[MAX_MEMORY_SZIE + 1];
};

#endif // !_MemoryMgr_hpp_

c++类中static函数

静态成员变量和静态方法是类的两个重要组成部分,它们与类的实例无关,而是与类本身相关联。

静态成员变量

  • 静态成员变量是类的所有对象共享的数据成员。不管创建了多少个类的对象,静态成员变量只有一份存储空间。
  • 静态成员变量在程序开始时创建,在程序结束时销毁,它们的生命周期贯穿整个程序运行期间。
  • 静态成员变量通常用于存储类的所有实例共享的信息,或者作为类的全局数据。

静态方法

  • 静态方法是不依赖于类的实例而可以直接调用的方法。它们只能访问静态成员变量和其他静态方法。
  • 静态方法通常用于执行不依赖于对象状态的操作,或者管理静态成员变量。

静态方法中的静态变量

  • 如果在静态方法中定义了一个静态变量,这个变量实际上是函数作用域的静态变量,它只在该静态方法中可见。
  • 这种静态变量在第一次调用该方法时被创建,并在程序结束时销毁。每次调用该方法时,该变量都会保持其值,不会重新初始化。

与类中静态成员变量的区别

  • 类中的静态成员变量是类的所有实例共享的,而静态方法中的静态变量只在该方法的作用域内共享。
  • 静态方法中的静态变量不能通过类名直接访问,因为它不是类的成员,而是方法的局部变量。
class MyClass {
public:
    static int classVar; // 类的静态成员变量

    static void staticMethod() {
        static int methodVar = 0; // 静态方法中的静态变量
        methodVar++;
        std::cout << "静态方法中的静态变量值: " << methodVar << std::endl;
    }
};

int MyClass::classVar = 0; // 初始化类的静态成员变量

int main() {
    MyClass::classVar = 5; // 通过类名访问静态成员变量
    MyClass::staticMethod(); // 调用静态方法
    MyClass::staticMethod();
    return 0;
}
//output:
//静态方法中的静态变量值:1
//静态方法中的静态变量值:2

在单例模式下,需要在静态函数中定义静态变量作为返回值,而不是在类中定义一个静态成员变量

主要是为了限制变量的作用域和访问控制。这样做有几个好处:

  1. 封装性:在静态函数内定义静态变量可以保持变量的私有性。这意味着这个变量只能通过特定的函数访问和修改,而不是在类的任何地方都可以访问。这有助于避免不必要的依赖和潜在的错误。
  2. 延迟初始化:静态函数内的静态变量只有在第一次调用该函数时才会被初始化。这种延迟初始化可以提高效率,特别是当变量的创建成本较高或不一定需要立即使用时。
  3. 局部化管理:如果一个静态变量只在某个函数中有用,那么将其定义在该函数内可以使代码更加清晰和集中。这样,相关的逻辑和数据都封装在同一个地方,便于管理和维护。
  4. 避免全局状态:类的静态成员变量是全局状态的一种形式。在软件设计中,过多的全局状态可能会导致代码难以理解和维护。通过将静态变量限制在函数内部,可以减少全局状态,使系统更加模块化。

C++11标准保证了局部静态变量的初始化是线程安全的。

c++类中包含其它对象的声明顺序

子类继承父类时,在子类构造函数的实现时需要关注父类的构造函数,需要注意的是,若子类中同时定义了其它类的成员变量,这些成员变量也是需要使用初始化列表进行手动初始化的(除非这些成员变量的构造函数没有参数)。

一个概念错误

include<iostream>
class A
{
        public:
                A()
                {
                        _a = 0;
                        _aa = 0;
                }
                A(int a, int aa)
                {
                        _a = a;
                        _aa = aa;
                }
                int _a;
                int _aa;

};
template<int T_a, int T_aa>
class B:public A
{
        public:
                B()
                {
                        B(T_a, T_aa);  //目的是调用有参构造,但是这样的写法是错误的,因为这样相当于创建了一个B的临时对象
                        _b = 0;
                }
                B(int a, int aa):A(a, aa)
                {
                        _b = 0;
                }
                int _b;

};

int main()
{
        B<10, 5> b;
        std::cout << b._a << std::endl;
}

正确的写法,使用委托构造

在C++11及以后的版本中,可以在一个构造函数中调用另一个构造函数,这被称为委托构造(Delegating Constructors)。这样做可以减少代码重复,并允许一个构造函数利用另一个构造函数已经完成的初始化工作。

委托构造的需求通常出现在以下情况:

  1. 当有多个构造函数有共同的初始化代码时,可以将共同的代码放在一个构造函数中,然后从其他构造函数中调用它。
  2. 当希望在多个构造函数之间提供不同的初始化路径,但又想共享某些初始化步骤时。
class MyClass {
public:
    MyClass() : MyClass(0) { // 委托给另一个构造函数
        // 这里可以添加只有在默认构造函数中需要的代码
    }

    MyClass(int value) : _value(value) {
        // 这里是共享的初始化代码
    }

private:
    int _value;
};

如果一个类中包含用户自定义的类对象作为其成员,对象成员构造函数的调用发生在类的构造函数调用之前,在进行对象成员的定义时(Object obj(11) error)不能直接初始化,因为类定义时不分配空间。

需要有一种机制来表示,“构造已分配了空间的对象成员,而不是创建一个新对象成员”,该机制需要在建立对象空间的结构时反映出来,既需要出现在构造函数调用转入之时,构造函数体执行(开括号)之前。即初始化表,初始化表也是类的const和引用类型数据成员初始化的唯一途径。按照类对象成员的声明顺序依次调用对象成员的构造函数进行初始化。

从该章开始不再虚拟机更新

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top