> 文章列表 > new/delete内存分配操作符

new/delete内存分配操作符

new/delete内存分配操作符

目录

一、C/C++的内存分布

二、new与delete操作符

1.new/delete 的使用

2.new申请失败抛异常

3.new/delete操作内置类型

4.new/delete 操作自定义类型 

三、operator new与operator delete函数

四、new和delete的实现原理

1.对于内置类型

2.对于自定义类型

①new的实现原理

②delete的实现原理

③new T[n] 的实现原理

④delete[] 的实现原理

五、定位 new 表达式(placement new)

六、面试题

1. malloc/free和new/delete的区别

2. 内存泄漏

①内存泄漏的体现

②内存泄漏分类


 

一、C/C++的内存分布

C/C++在内存分布这块是相同的

new/delete内存分配操作符

栈由高地址向低地址增加

堆于数据段的末端向高地址增加

数据段通常包括三个部分:BSS、已初始化的数据段和只读数据段BBS(未初始化数据段)是一块未初始化的内存区域(例如:static int i 未指定值会被分配至数据段),用于存储全局变量和静态变量,它的内容在程序启动时被设置为0或者为空。在可执行文件中,BSS段通常表示为一个有固定大小的区块,并且在程序加载时会被分配对应的内存空间。

 

二、new与delete操作符

C语言内存管理方式在C++中可以继续使用,但是C++中new和delete运算符还可提供一些高级功能,例如支持类和对象的构造和析构函数。

new/delete 不适用于realloc的内存分配。

1.new/delete 的使用

【new】

分配一个新的内存块

new type;

分配动态分配数组

new type[size];

【delete】

——只能释放由new创建的内存块

删除单个分配的内存块

delete pointer_variable;

删除动态分配的数组(分配的内存块)

delete[] pointer_variable;

 

2.new申请失败抛异常

在 C++ 中,使用 new 运算符申请内存时,如果内存不足,会抛出 std::bad_alloc 异常。因此,在使用 new 运算符申请内存时,为了避免程序崩溃,我们需要使用 try-catch 块来捕获可能抛出的异常具体来说,可以在申请内存的语句前面加上 try,然后在 catch 块中处理异常,如打印错误信息、释放之前申请的内存等,以确保程序可以正常运行。

以下是一个示例:

try {int* ptr = new int[100];// 申请内存成功,进行后续操作
} catch (const std::bad_alloc& e) {std::cerr << "Memory allocation failed: " << e.what() << std::endl;// 处理异常,如打印错误信息或释放之前申请的内存
}

需要注意的是,即使你的程序没有显式使用 try-catch 块来捕获 std::bad_alloc 异常,在发生内存不足时,操作系统也会自动向程序抛出 SIGSEGV 信号,导致程序崩溃。因此,为了保证程序的健壮性,建议在使用 new 运算符时始终使用 try-catch 块来捕获可能的异常。

 

3.new/delete操作内置类型

代码如下:

	int* p1 = new int;//通过列表初始化,可对分配到的内存直接初始化int* p2 = new int(9);int* pa1 = new int[10];//通过列表初始化,可对分配到的内存直接初始化int* pa2 = new int[10] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};cout << *p2 << endl;for (int i = 0; i < 10; ++i){cout << pa2[i] << " ";}cout << endl;delete p1;delete p2;delete[] pa1;delete[] pa2;

输出:

9
0 1 2 3 4 5 6 7 8 9

操作符 new/delete 和函数 malloc/free 针对内置类型没有任何差别,只是用法不一样。

 

4.new/delete 操作自定义类型 

在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,但是,使用malloc和free来分配和释放内存时,不会调用该类型的构造函数和析构函数。

 

三、operator new与operator delete函数

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0OTE4MDkw,size_16,color_FFFFFF,t_70

new 和 delete 是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间, delete 在底层通过operator delete全局函数来释放空间。

实际上 operator new 和 operator delete 的用法跟 malloc 和 free 是完全是一样的功能,都是在堆上申请释放空间,但是失败了处理方式不一样,malloc失败返回NULL,operator new失败以后抛异常。

以下三种方式开辟空间和释放空间的效果是一样的

 

	int* p1 = (int*)malloc(sizeof(int));      //malloc和freefree(p1);int* p2 = new int;                        //new和deletedelete p2;int* p3 = (int*)operator new(sizeof(int));//operator new与operator deleteoperator delete (p3);

 

四、new和delete的实现原理

1.对于内置类型

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

 

2.对于自定义类型

假设自定义类型为T 

①new的实现原理

  • 调用 operator new 函数申请内存空间。
  • 调用类型T的构造函数,初始化对象。
  • 返回指向已分配的内存空间的指针。

②delete的实现原理

  • 调用类型 T 的析构函数,释放对象占用的资源。
  • 调用 operator delete 函数释放内存空间。

③new T[n] 的实现原理

  • 调用 operator new[] 函数申请 n 个对象所需的内存空间。
  • 对于每个对象,调用类型 T 的构造函数,初始化对象。
  • 返回指向第一个对象的指针。

④delete[] 的实现原理

  • 对于每个对象,调用类型 T 的析构函数,释放对象占用的资源。
  • 调用 operator delete[] 函数释放内存空间。

 

五、定位 new 表达式(placement new)

定位 new 表达式(placement new)是一种特殊的 new 表达式,它允许我们将对象构造在指定的内存地址上

使用场景:
这种方式通常用于特定的场景,比如在某些嵌入式系统中需要将对象构造在固定的内存地址上,或者需要管理自己分配的内存池等等。

 

new (pointer) type (arguments)

其中,pointer 是一个指向已分配的内存块的指针,type 是对象的类型,arguments 是传递给 type 构造函数的参数。

 

注意事项:

使用定位 new 表达式时,我们必须保证 pointer 指向的内存块已经被正确地分配,并且能够容纳 type 类型的对象。

 

示例如下:

//...
class MyClass {
public:MyClass(int val) : m_value(val) {cout << "Constructing MyClass object with value " << m_value << endl;}~MyClass() {cout << "Destructing MyClass object with value " << m_value << endl;}private:int m_value;
};//...// 分配一块内存char* buffer = new char[sizeof(MyClass)];// 在分配的内存上构造对象MyClass* pObject = new (buffer) MyClass(42);// 手动销毁对象pObject->~MyClass();// 释放分配的内存delete[] buffer;

在这个例子中,我们首先使用 new char[] 表达式分配了一块内存,然后使用定位 new 表达式在这块内存上构造了一个 MyClass 对象。最后,我们手动调用了析构函数来销毁对象,并使用 delete[] 表达式释放了分配的内存。

 

六、面试题

1. malloc/free和new/delete的区别

共同点:

  • 都是从堆上申请空间,并且需要用户手动释放。

不同点:

  • malloc和free是函数,new和delete是操作符
  • malloc申请的空间不会初始化,new可以初始化。
  • malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型。
  • malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  • 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。
     

2. 内存泄漏

①内存泄漏的体现

代码如下:

void MemoryLeaks()
{// 1.内存申请了忘记释放int* p1 = (int*)malloc(sizeof(int));int* p2 = new int;// 2.异常安全问题int* p3 = new int[10];Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.delete[] p3;
}

异常安全性是指程序在抛出异常后仍能保持正确状态的能力。

②内存泄漏分类

C/C++程序中一般我们关心两种方面的内存泄漏:

堆内存泄漏(Heap Leak)
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。
 

系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。