C++的动态内存申请new和delete
- 引言
- 一、动态分配内存的概述
- 二、静态分配和动态分配
- 三、new和delete
- 3.1、new和delete操作基本类型空间
- 3.2、new和delete操作数组空间
- 四、new和delete的重载
- 五、动态分配内存的优缺点
- 总结
引言
💡 作者简介:专注于C/C++高性能程序设计和开发,理论与代码实践结合,让世界没有难学的技术。包括C/C++、Linux、MySQL、Redis、TCP/IP、协程、网络编程等。
👉
🎖️ CSDN实力新星,社区专家博主
👉
🔔 专栏介绍:从零到c++精通的学习之路。内容包括C++基础编程、中级编程、高级编程;掌握各个知识点。
👉
🔔 专栏地址:C++从零开始到精通
👉
🔔 博客主页:https://blog.csdn.net/Long_xu
🔔 上一篇:【019】C++的指针与函数
一、动态分配内存的概述
C++中的动态内存分配是在程序运行时根据需要进行内存空间的分配和释放,与静态内存分配相比,具有更大的灵活性和适应性。动态内存分配可以通过new
和delete
或者malloc()
和free()
等函数实现。
动态内存分配通常用于以下情况:
- 在程序运行时无法确定所需的内存大小。
- 需要在多个函数之间共享数据,并且数据可能被频繁地创建和销毁。
- 静态数组无法满足需求,例如需要支持可变长度数组。
如果在动态分配内存后忘记了释放它,则会导致内存泄漏。为避免内存泄漏,可以使用智能指针或手动管理内存的方式来释放动态分配的内存。
二、静态分配和动态分配
(1)静态分配:
- 在程序编译或运行过程中,按事先规定大小分配内存空间的分配方式。
- 必须事先知道所需空间的大小。
- 分配在栈区或全局变量区,一般以数组的形式。
(2)动态分配:
- 在程序运行过程中,根据需要大小自由分配所需空间。
- 按需分配。
- 分配在堆区,一般使用特定的关键字进行分配。
(3)堆和栈的区别:
- 分配方式:堆是动态分配,栈是静态分配。
- 内存大小:堆内存大小没有限制(除了计算机硬件限制),而栈的大小受到编译器、操作系统等因素的影响。
- 空间分配:堆空间由程序员手动申请、释放;栈空间由编译器自动分配、回收。
- 访问速度:由于栈采用先进先出原则,访问速度比堆要快。
- 管理方式:堆内存由程序员手动管理,容易出错;栈内存由编译器自动管理,减轻了程序员的负担。
三、new和delete
在 C++ 中,动态内存的申请和释放需要使用关键字 new
和 delete
。
new
是一个操作符,可以用来在堆上分配一段内存,并返回该内存块的地址。它的语法为:
new 数据类型;
例如:
int* p = new int; // 在堆上分配一个 int 类型的空间
double* q = new double; // 在堆上分配一个 double 类型的空间
使用了 new
操作符在堆上分别申请了一个 int
类型和一个 double
类型的空间,并将其地址赋值给指针变量 p
和 q
。
如果要同时初始化所申请的内存块,可以使用如下形式:
数据类型* 指针名 = new 数据类型(初值);
例如:
int* p = new int(10); // 在堆上分配一个 int 类型的空间,并初始化为 10
char* q = new char('A'); // 在堆上分配一个 char 类型的空间,并初始化为 'A'
使用 new
分配了内存后,在不再需要这些内存时应该手动释放。释放所申请的内存可以使用关键字 delete
。它有两种用法:一种是只删除单个对象的内存块,另一种是删除数组的内存块。
- 删除单个对象。要删除单个对象,在
delete
后面加上指向该对象的指针即可:
delete 指针名;
例如:
int* p = new int(10); // 在堆上分配一个 int 类型的空间,并初始化为 10
delete p; // 释放申请的空间
注意:使用完 new
分配的内存后,一定要记得使用 delete
将其释放掉,否则会造成内存泄漏。
- 删除数组。如果使用了带有方括号 [] 的
new
来分配数组,则需要使用带有方括号 [] 的delete
来释放所申请的内存。语法如下:
delete[] 数组指针;
例如:
int* p = new int[5]; // 在堆上分配一个长度为 5 的 int 数组
delete[] p; // 释放申请的空间
注意:在使用带有方括号 [] 的 new
分配数组时,必须使用带有方括号 [] 的 delete
进行释放,否则会导致未定义行为。
示例:
#include <iostream>
using namespace std;
int main() {
int* p1 = new int;
*p1 = 10;
cout << "p1: " << *p1 << endl;
double* p2 = new double(3.14);
cout << "p2: " << *p2 << endl;
int* arr = new int[5];
for (int i = 0; i < 5; i++) {
arr[i] = i + 1;
cout << "arr[" << i << "]: " << arr[i] << endl;
}
delete[] arr;
delete p1;
delete p2;
return 0;
}
输出结果:
p1: 10
p2: 3.14
arr[0]: 1
arr[1]: 2
arr[2]: 3
arr[3]: 4
arr[4]: 5
3.1、new和delete操作基本类型空间
new负责申请空间,delete负责释放空间。
#include <iostream>
using namespace std;
int main()
{
int *x=new int;//从堆区申请int类型大小的空间
x=100;
cout<<"*x = "<<*x<<endl;
int *x2=new int(1000);//从堆区申请int类型大小的空间,并初始化
cout<<"*x2 = "<<*x2<<endl;
delete x2;//释放空间
delete x;//释放空间
return 0;
}
3.2、new和delete操作数组空间
new与[]结合表示申请数组空间,delete释放的时候也需要[]。
#include <iostream>
using namespace std;
int main()
{
int *arr = new int[5]{ 1,2,3,4,5 };
for (int i = 0; i < 5; i++)
{
cout << arr[i] << " ";
}
cout << endl;
delete[] arr;
return 0;
}
输出:
1 2 3 4 5
四、new和delete的重载
C++ 中可以通过重载 new
和 delete
操作符来实现自定义的内存管理。重载后的操作符可以用于在不同情况下分配和释放内存。
- 重载 new 操作符可以使用
operator new
函数来重载new
操作符。它是一个静态成员函数,其原型如下:
void* operator new (size_t size);
其中,参数 size
表示要申请的内存块大小,返回值为指向申请到的内存块的指针。以下是一个简单的例子:
#include <iostream>
using namespace std;
class MyClass {
public:
void* operator new (size_t size) {
cout << "Customized allocation of " << size << " bytes memory." << endl;
return malloc(size);
}
};
int main() {
MyClass* p = new MyClass();
delete p;
return 0;
}
输出结果:
Customized allocation of 1 bytes memory.
上述代码中,定义了一个名为 MyClass
的类,并在其中重载了 new
操作符。在这个例子中,没有真正地改变分配内存的方式,而是只是增加了一些输出信息以便观察调用情况。
- 重载 delete 操作符也可以使用
operator delete
函数来重载delete
操作符。它也是一个静态成员函数,其原型如下:
void operator delete (void* ptr);
其中,参数 ptr
表示要释放的内存块地址。
简单的例子:
#include <iostream>
using namespace std;
class MyClass {
public:
void* operator new (size_t size) {
cout << "Customized allocation of " << size << " bytes memory." << endl;
return malloc(size);
}
void operator delete (void* ptr) {
cout << "Customized deallocation." << endl;
free(ptr);
}
};
int main() {
MyClass* p = new MyClass();
delete p;
return 0;
}
输出结果:
Customized allocation of 1 bytes memory.
Customized deallocation.
上述代码中,不仅重载了 new
操作符,还重载了 delete
操作符。在这个例子中,通过使用 free()
函数来释放内存。
需要注意的是,在重载 delete
操作符时,必须与重载 new
操作符配对使用。也就是说,如果想自定义分配方式,则需要同时自定义释放方式。
上述是在类中重载,也可以在非类中重载new
和delete
操作符实现自定义的内存分配和释放方式。例如:
void* operator new(size_t size) {
void* p = malloc(size);
if(!p) throw std::bad_alloc();
return p;
}
void operator delete(void* p) noexcept {
free(p);
}
以上代码实现了一个自定义的new
和delete
操作符,其中使用了标准库中的异常类std::bad_alloc()
来处理内存申请失败的情况,并使用了C语言中的malloc()
和free()
函数来进行内存分配和释放。
需要注意的是,在重载操作符时需要保证与标准库中的操作符具有相同的语义,否则可能会导致意外行为或程序崩溃。
五、动态分配内存的优缺点
动态分配内存的优点:
-
灵活性:动态分配内存允许程序根据需要在运行时分配和释放内存,从而可以适应不同的数据量和操作需求。
-
节省空间:动态分配内存只会占用实际需要的空间,避免了静态分配过多空间造成的浪费。
-
提高效率:合理利用动态内存分配可以提高程序的效率,尤其是对于大型数据结构或需要多次重复调用某个函数的情况。
-
可扩展性:由于动态内存分配可以根据需求增加或减少内存空间,因此程序具有很好的可扩展性。
动态分配内存的缺点:
-
内存泄漏:如果不正确地使用和管理动态分配的内存,就可能导致内存泄漏问题,这会严重影响程序运行效率和稳定性。
-
复杂性:相比静态内存分配,在动态内存分配中需要进行更多复杂的操作,如申请、释放、回收等。同时也需要考虑到指针、数组等相关问题。
-
代码容易出错:由于涉及到指针操作和较为复杂的编程技巧,因此代码容易出现错误,增加了程序的调试难度。
-
程序安全性:由于动态内存分配可以被黑客用来进行缓冲区溢出等攻击,因此需要谨慎使用,并做好相应的安全措施。
总结
C++中的动态内存分配是在程序运行时根据需要进行内存空间的分配和释放,与静态内存分配相比,具有更大的灵活性和适应性。动态内存分配可以通过new
和delete
或者malloc()
和free()
等函数实现。
new
和delete
。
使用new
关键字可以在堆上分配指定类型大小的内存空间,并返回指向该空间起始位置的指针。例如:
int *p = new int; // 分配一个int类型大小的空间
*p = 10; // 对该空间赋值
delete p; // 释放该空间
使用new[]
关键字可以在堆上分配指定数量、类型大小相同的连续内存空间,并返回指向该空间起始位置的指针。例如:
int size = 10;
int *arr = new int[size]; // 分配一个包含10个int类型元素的数组
for(int i = 0; i < size; i++) {
arr[i] = i;
}
delete[] arr; // 释放该数组
需要注意的是,使用new
和delete
时需要保证分配和释放的内存大小和类型一致,否则会导致内存泄漏或程序崩溃。
-
内存泄漏。如果在动态分配内存后忘记了释放它,则会导致内存泄漏。为避免内存泄漏,可以使用智能指针或手动管理内存的方式来释放动态分配的内存。
-
其他注意事项:
- 动态分配出来的对象必须通过调用其析构函数来释放资源。
- 如果动态分配出来的对象要被当作参数传递给函数,则最好将其封装成智能指针,以确保资源能够正确地被释放。
- 动态分配时应该根据实际需要进行内存分配,避免过度分配导致资源浪费或不足导致程序崩溃。