C++数据内存管理
·
C++/C的内存分布
图:

说明:
- 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
- . 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信。
- 堆用于程序运行时动态内存分配,堆是可以上增长的。
- 数据段--存储全局数据和静态数据。
- 代码段--可执行的代码/只读常量
C语言中的内存管理
malloc
- 功能:用于在堆区申请一块连续的指定大小的内存块区域,并以void*类型返回分配的内存区域地址。
- 初始化:不会进行内存初始化,即分配的内存区域可能包含任意数据。
- 参数:一个参数,即需要分配的内存大小(以字节为单位)。
- 返回值:返回一个指向分配内存的指针,如果分配失败则返回NULL。
- 使用场景:适用于在程序运行时根据需要动态分配内存的情况,特别是当内存不需要初始化时。
- 示例:
#include <stdio.h> #include <stdlib.h> int main() { int *ptr; int n = 5; // 使用 malloc 分配内存 ptr = (int*)malloc(n * sizeof(int)); if (ptr == NULL) { printf("Memory not allocated.\n"); exit(1); } // 初始化内存 for (int i = 0; i < n; i++) { ptr[i] = i + 1; } // 打印数组内容 for (int i = 0; i < n; i++) { printf("%d ", ptr[i]); } // 释放内存 free(ptr); return 0; }
calloc
- 功能:与malloc类似,也是用于动态地分配内存空间。
- 初始化:会在分配内存时自动将内存清零,即将分配的内存区域的每一位都初始化为零。因此,calloc也被称为“零初始化内存分配器”。
- 参数:两个参数,第一个参数表示要分配的元素个数,第二个参数表示每个元素的大小(以字节为单位)。
- 返回值:返回一个指向分配内存的指针,如果分配失败则返回NULL。
- 使用场景:适用于需要分配并初始化内存的情况,特别是在分配数组或结构体等数据结构时,使用calloc可以避免手动初始化的繁琐操作。
- 示例:
#include <stdio.h> #include <stdlib.h> int main() { int *ptr; int n = 5; // 使用 calloc 分配并初始化内存 ptr = (int*)calloc(n, sizeof(int)); if (ptr == NULL) { printf("Memory not allocated.\n"); exit(1); } // 打印数组内容(应为0, 0, 0, 0, 0 因为 calloc 初始化了内存) for (int i = 0; i < n; i++) { printf("%d ", ptr[i]); } // 释放内存 free(ptr); return 0; }
realloc
- 功能:用于重新分配之前通过malloc、calloc或realloc分配的内存块。它允许改变已分配内存块的大小,并根据需要调整内存块的位置。
- 初始化:不直接涉及初始化,因为它是对已分配内存的重新调整。但重新分配的内存部分(如果有的话)可能是未初始化的。
- 参数:两个参数,第一个参数是指向要重新分配的内存块的指针,第二个参数是要为新的内存块分配的字节数。
- 返回值:返回一个指向分配内存的指针,如果分配失败则返回NULL。
- 使用场景:适用于在已分配内存的基础上调整内存大小的情况,特别是在处理可变长数组或动态数据结构时非常有用。
- 示例:
#include <stdio.h> #include <stdlib.h> int main() { int *ptr; int n = 5; // 初始分配内存 ptr = (int*)malloc(n * sizeof(int)); if (ptr == NULL) { printf("Memory not allocated.\n"); exit(1); } // 初始化内存 for (int i = 0; i < n; i++) { ptr[i] = i + 1; } // 打印原始数组内容 printf("Original array: "); for (int i = 0; i < n; i++) { printf("%d ", ptr[i]); } // 使用 realloc 扩大内存 n *= 2; // 扩大为原来的两倍 ptr = (int*)realloc(ptr, n * sizeof(int)); if (ptr == NULL) { printf("Memory not reallocated.\n"); exit(1); } // 初始化新分配的内存部分 for (int i = n / 2; i < n; i++) { ptr[i] = i + 1; } // 打印扩大后的数组内容 printf("\nExpanded array: "); for (int i = 0; i < n; i++) { printf("%d ", ptr[i]); } // 释放内存 free(ptr); return 0; }
C++中的内存管理
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
new/delete操作符:
用于动态分配和释放单个对象的内存,会调用构造函数和析构函数。
new[]/delete[]操作符:
用于动态分配和释放数组的内存,同样会调用构造函数和析构函数。
operator new与operator delete函数operator new 和 operator delete 是 C++ 中用于动态内存分配和释放的运算符函数。它们分别对应于 C 语言中的 malloc 和 free 函数,但提供了更多的灵活性和控制。
operator new
operator new 用于分配指定大小的内存块。它通常返回一个指向分配内存的指针,如果分配失败,则抛出 std::bad_alloc 异常。
自定义 operator new
你可以为自定义类重载 operator new,以提供特定的内存分配策略。例如:
#include <iostream>
#include <new> // for std::bad_alloc
class MyClass {
public:
void* operator new(std::size_t size) {
std::cout << "Custom new operator called, size: " << size << std::endl;
void* p = malloc(size);
if (!p) {
throw std::bad_alloc();
}
return p;
}
void operator delete(void* p) noexcept {
std::cout << "Custom delete operator called" << std::endl;
free(p);
}
};
int main() {
try {
MyClass* obj = new MyClass();
delete obj;
} catch (const std::bad_alloc& e) {
std::cerr << "Memory allocation failed: " << e.what() << std::endl;
}
return 0;
}
operator delete
operator delete 用于释放由 operator new 分配的内存。它接受一个指向要释放内存的指针。
自定义 operator delete
与 operator new 类似,你也可以为自定义类重载 operator delete。
#include <iostream>
#include <cstdlib> // for malloc and free
#include <new> // for std::bad_alloc
// 自定义全局 operator delete
void operator delete(void* p) noexcept {
std::cout << "Global delete operator called for void*\n";
free(p);
}
// 自定义全局 operator delete[](数组形式)
void operator delete[](void* p) noexcept {
std::cout << "Global delete[] operator called for void*\n";
free(p);
}
class MyClass {
public:
// 自定义成员级别的 operator delete
void operator delete(void* p) noexcept {
std::cout << "Member delete operator called for MyClass\n";
::operator delete(p); // 调用全局的 delete 运算符
}
// 自定义成员级别的 operator delete[](通常也需要定义,即使不使用数组)
void operator delete[](void* p) noexcept {
std::cout << "Member delete[] operator called for MyClass (not typically used for single objects)\n";
::operator delete[](p); // 调用全局的 delete[] 运算符
}
// 构造函数(为了完整性)
MyClass() {
std::cout << "MyClass constructor called\n";
}
// 析构函数(为了完整性)
~MyClass() {
std::cout << "MyClass destructor called\n";
}
};
new 运算符的实现原理
new 运算符在C++中用于动态分配内存并调用构造函数(如果适用)。它的实现通常分为以下几个步骤:
- 内存分配:
new运算符首先调用底层的内存分配函数(如malloc或operator new),为对象分配足够的内存空间。- 如果内存分配失败(例如,由于内存不足),
new运算符将抛出一个std::bad_alloc异常。
- 构造对象:
- 在成功分配内存后,
new运算符调用对象的构造函数来初始化对象。 - 对于基本数据类型(如
int、float等),这一步实际上不会执行任何操作,因为基本数据类型不需要构造函数。
- 在成功分配内存后,
- 返回指针:
- 最后,
new运算符返回指向新分配并初始化的对象的指针。
- 最后,
delete 运算符的实现原理
delete 运算符用于释放由 new 运算符分配的内存,并调用析构函数(如果适用)。它的实现通常分为以下几个步骤:
- 析构对象:
delete运算符首先调用对象的析构函数来执行必要的清理操作(如释放资源、关闭文件等)。- 对于基本数据类型,这一步同样不会执行任何操作。
- 释放内存:
- 在析构对象后,
delete运算符调用底层的内存释放函数(如free或operator delete)来释放之前分配的内存空间。
- 在析构对象后,
更多推荐



所有评论(0)