CC++内存管理

Posted 雨轩(爵丶迹)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CC++内存管理相关的知识,希望对你有一定的参考价值。

C/C++内存管理

1、C/C++内存分布

我们先来看内存区域的一个划分

  1. 又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的。

  2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)

  3. 用于程序运行时动态内存分配,堆是可以上增长的。

  4. 数据段–存储全局数据和静态数据。

  5. 代码段–可执行的代码/只读常量。

    • 常规描述:
  6. 全局变量和静态变量都存储与数据段

  7. 常量字符串(只读常量)和可执行代码位于代码段

  8. 动态开辟的位于堆中

  9. 局部变量位于栈帧中

2、C语言中的动态内存管理方式

之前我们在C语言中已经知道malloc/calloc/realloc/free,我们再来复习一下malloc/calloc/realloc的区别。

malloc在堆区中开辟一块连续的内存,返回一个指向被分配的内存块起始位置的指针。返回的类型是不知道的,返回一个类型为void *的指针,返回指向内存的指针不会初始化。

calloc跟malloc类似,主要区别是calloc在返回指向内存的指针之前会把它初始化为0

realloc函数用于修改一个已经分配的内存块的大小。

  1. 在原先的内存块够大时,如果它用于扩大一个内存块,那么原来的内存块的内容依然保留,新增加的内存添加到原来内存块的后面。如果它用于缩小一个内存块,该内存块尾部的内存将被删除,剩余部分内存的原来的内容依然保留
  2. 如果原先内存不够用了,则开辟一块新的内存,并将原理那块的内存的内容复制到新的内存块上(我们这里也要注意需要手动去删除自己原来开辟的内存块
int* a = (int*)malloc(sizeof(int));
printf("%d\\n", *a);
free(a);

int* b = (int*)calloc(4, sizeof(int));
printf("%d", *b);

int* c = (int*)malloc(sizeof(int));
int* d = (int*)realloc(c, sizeof(int) * 40);
free(d);//是否需要释放c,根据实际的情况而定

3、C++内存管理方式

void Test()
{
	// 动态申请一个int类型的空间  未初始化
	int* ptr4 = new int;
	cout << *ptr4 << endl;
	// 动态申请一个int类型的空间并初始化为10
	int* ptr5 = new int(10);
	cout << *ptr5 << endl;
	// 动态申请10个int类型的空间  管理对象数组
	int* ptr6 = new int[10];
	for (int i = 0; i < 10; i++)
	{
		ptr6[i] = i;
		cout << ptr6[i] << " ";
	}
	cout << endl;
	delete ptr4;//释放对象方式
	delete ptr5;
	delete[] ptr6;//释放对象数组方式
}

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[]

4、malloc/free/new/delete的区别

class Test
{
public:
	Test()
		: _data(0)
	{
		cout << "Test():" << this << endl;
	}
	~Test()
	{
		cout << "~Test():" << this << endl;
	}
private:
	int _data;
};
void Test2()
{
	//内置类型   这里malloc和new没有什么区别
	int* a = (int*)malloc(sizeof(int));
	int* b = new int(5);
	free(a);
	delete(b);

	//自定义类型  用new、delete还会调用构造函数和析构函数,malloc和free不会
	// 申请单个Test类型的空间
	Test* p1 = (Test*)malloc(sizeof(Test));
	free(p1);
	// 申请10个Test类型的空间
	Test* p2 = (Test*)malloc(sizeof(Test) * 10);
	free(p2);

	// 申请单个Test类型的对象
	Test* p3 = new Test;
	delete p3;
	// 申请10个Test类型的对象
	Test* p4 = new Test[10];
	delete[] p4;
}

图示,我们可以看出几点:

  1. 对于内置类型,用malloc和new没有太大的区别
  2. 对于自定义类型,malloc、free和new、delete有很大的区别,new会调用构造函数,delete会调用析构函数,而malloc和free不会

5、operator new 与 operator delete函数

  1. new和delete是用户进行动态内存申请和释放的操作符
  2. operator new 和operator delete是系统提供的全局函数
  3. new在底层调用operator new全局函数来申请空间,delete在底层通operator delete全局函数来释放空间
  4. operator new:实际是通过malloc来申请空间,申请成功直接返回,申请失败了会去指向空间不足的应对措施,如果用户设置了相应的应对措施,则继续申请,或者会抛异常(异常后后面在说)
  5. operator delete:该函数最后是通过free去释放空间的
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
	// try to allocate size bytes
    void *p;
    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 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_FINALLY
    return;
}
//free的实现
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

总的来说:new和delete就是经过C++一定的装饰,其本质还是离不开malloc和free。

6、new和delete的实现原理

上面我们已经剖析了new和delete的原理,我们这里在总结下

  1. 对于内置类型

    如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

  2. 对于自定义类型

    1. new的原理

      1. 调用operator new函数申请空间
      2. 在申请的空间上调用构造函数
    2. delete的原理

      1. 在空间上执行析构函数,完成对象中资源的清理工作
      2. 调用operator delete函数释放对象的空间
    3. new T[N]的原理

      1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请

      2. 在申请的空间上执行N次构造函数

    4. delete[]的原理

      1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理

      2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

7、定位new表达式

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象

使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表

使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义
类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化

class Test
{
    public:
    Test()
    : _data(0)
    {
    	cout<<"Test():"<<this<<endl;
    }
    ~Test()
    {
    	cout<<"~Test():"<<this<<endl;
    }
private:
	int _data;
};
void Test()
{
// pt现在指向的只不过是与Test对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
    Test* pt = (Test*)malloc(sizeof(Test));
    new(pt) Test; // 注意:如果Test类的构造函数有参数时,此处需要传参
}


好久没更新了,更新一波

以上是关于CC++内存管理的主要内容,如果未能解决你的问题,请参考以下文章

CC++内存管理

CC++内存管理

内存管理 相关宏

Java内存结构

C 中的共享内存代码片段

如何使用模块化代码片段中的LeakCanary检测内存泄漏?