文章目录
- 🐨0. 前言
- 🦍1. C/C++内存分布
- 🦈2. C++内存管理
- 🐚2.1 new和delete操作内置类型
- 🐚2.2 new和delete操作自定义类型
- 🦭3. new和delete底层
- 🗿3.1 operator new和operator delete函数
- 🗿3.2 new和delete实现原理
- 🦨4. 定位new
🐨0. 前言
本篇文章会涉及C语言动态内存结合讲解,如果不是很清楚的铁子,可以查看此篇文章——C语言——动态内存管理
🦍1. C/C++内存分布
在C/C++中,程序中需要存储的数据可分为:局部数据、静态数据、全局数据、常量数据和动态申请数据,这些数据都有其独特的存储位置和生命周期。
-
局部数据:
局部数据是在函数内声明的变量,在函数调用时被创建,函数返回时被销毁,存储在栈区
void func() {int x = 1;//局部数据 } int main() {int a = 1;//局部数据return 0; }
-
静态数据:
静态数据使用
static
关键字声明,在整个程序生命周期存在,存储在静态区static int globalVal = 1; //全局静态数据 void func() {static int x = 1; //局部静态数据 }
-
全局数据:
全局变量是在函数外部声明的变量,具有全局作用域和全局可见性,生命周期直到程序结束,存储在全局数据区
int globalVal = 1; //全局变量 void func() {// ... }
-
常量数据:
常量数据是指在程序中使用字面值或使用
const
修饰符声明的变量,它们的值在程序执行期间保持不变。常量数据存储在只读数据区中,通常与全局数据存储在同一区域。const int a = 1; const char ch = 'a';//常量数据 void func() {// ... }
-
动态申请数据:
动态申请数据是通过使用
new
运算符(C++)或malloc()
函数(C)在堆内存中手动分配的数据。动态申请的数据需要通过delete
运算符(C++)或free()
函数(C)显式释放。int main() {//C++动态内存申请int* p1 = new int;*p1 = 10;delete p1;//C语言动态内存申请int* p2 = (int*)malloc(sizeof(int));if (p2 == nullptr){perror("malloc fail");exit(-1);}*p2 = 10;free(p2);return 0; }
Tips:
- **内核空间:**用于操作系统内核的执行和运行,用户无法直接访问内核空间
- **栈:**存放函数调用、局部变量和函数参数等信息,采用后进先出的方式分配和释放内存
- **内存映射段:**用于存储被映射到内存的文件和设备等。包括共享库、动态链接库、内存映射文件等
- **堆:**用于存储动态申请的内存,在程序运行时根据需要进行分配和释放
- **数据段:**存储全局变量、静态变量和静态分配的变量等
- **代码段:**存放可执行程序的机器指令,只读常量
🦈2. C++内存管理
C++中,使用new
和delete
运算符来进行动态内存分配和释放操作。
🐚2.1 new和delete操作内置类型
int main()
{//单个空间申请和释放int* ptr1 = new int; //动态申请int类型的空间int* ptr2 = new int(10); //动态申请int类型的空间,并将其初始化为10delete ptr1;delete ptr2;//连续空间申请和释放int* ptr3 = new int[10]; //动态申请10个int类型的空间char* ptr4 = new char[4] {'R', 'M', 'B'}; //动态申请4个char类型的空间,并且初始化delete[] ptr3;delete[] ptr4;return 0;
}
🐚2.2 new和delete操作自定义类型
class A
{
public:A(int a = 1):_a(a){cout << "A(int a = 1)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
int main()
{//C语言A* p1 = (A*)malloc(sizeof(A));free(p1);//CPPA* p2 = new A(10);delete p2;return 0;
}
C++自定义类型申请动态内存,需要有适当的构造函数和析构函数,以确保对象的正确创建和销毁。分配动态内存分配时,会先调用构造函数,释放时自动调用对象的析构函数。
🦭3. new和delete底层
🗿3.1 operator new和operator delete函数
new
和delete
是用来动态申请和释放内存的操作符,而operator new
和operator delete
是系统提供的全局函数。new
和delete
底层提供调用operator new
和operator delete
来申请或者释放动态内存。
void* operator new(std::size_t sz)
{std::printf("1) new(size_t), size = %zu\n", sz);if (sz == 0)++sz; if (void *ptr = std::malloc(sz))return ptr;//申请失败抛出异常throw std::bad_alloc{};
}void operator delete(void* ptr, std::size_t size) noexcept
{std::printf("4) delete(void*, size_t), size = %zu\n", size);std::free(ptr);
}
来源于C++官网
通过查看operator new
和operator delete
发现,这两个函数实际上也是通过malloc
和free
来实现的。只不过operator new
相比于malloc
,但它申请失败时不是返回空,而是抛出异常(面向过程的语言通常采用抛出异常,而不是返回值)。
🗿3.2 new和delete实现原理
上面提到new
和delete
是底层是通过调用operator new
和operator delete
,而operator new
和operator delete
的底层实现就是malloc
和free
,说来说去还是malloc
和free
。对于内置类型的差别并不是很大,而针对于自定义类型,会去调用构造函数和析构函数。
-
new
-
new
首先调用operator new
函数进行内存分配。operator new
是一个全局函数,通常由编译器提供。它负责分配所需大小的内存块,并返回一个指向分配内存的指针。 -
如果分配内存失败,
operator new
可以抛出std::bad_alloc
异常。 -
new
调用构造函数,用于初始化所分配的内存,创建对象。
-
-
delete
delete
首先调用对象的析构函数,用于释放对象占用的资源。delete
调用operator delete
函数进行内存释放。operator delete
是一个全局函数,通常由编译器提供。它负责释放对象所占用的内存。
malloc / free
和new / delete
的区别:
malloc
和free
是函数,而new
和delete
是操作符
malloc
需要手动计算内存块大小,new
根据类型自动计算
malloc
需要手动进行初始化,new
会调用对象的构造函数,自动进行对象的初始化,释放内存时delete
会调用析构函数
malloc
在分配内存失败时,返回NULL
指针,new
在分配内存失败时,抛出std::bad_alloc
异常,可以通过try-catch
块来处理异常情况。int main() {int* ptr1 = nullptr;try {do{ptr1 = new int[1024 * 1024];cout << ptr1 << endl;} while (ptr1);}catch (const exception& e){cout << e.what() << endl;}return 0; }
🦨4. 定位new
位new
是C++的一种扩展形式,它允许在指定的内存地址上创建对象,而不是使用默认的动态内存分配机制。
//定位new的语法:
new (address) Type [initializer];
其中,address
是指定的内存地址,Type
是要创建的对象类型,initializer
是可选的初始化参数。
使用定位new
时,编译器不会分配新的内存,而是在指定的地址上构造对象。
class A
{
public:A(int a = 0):_a(a){cout << "A(int a = 0)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
int main()
{A* p1 = (A*)malloc(sizeof(A));new(p1)A; //如果类的构造函数有参数时,此处需要传参p1->~A(); //手动调用析构函数free(p1);A* p2 = (A*)operator new(sizeof(A)); //分配内存new(p2)A(10); //指定地址创建对象p2->~A();operator delete(p2);
}
位new
通常在特定场景下使用,如在特定内存池、硬件寄存器或内存映射设备上创建对象等。
一般情况下,使用常规的new
操作符来动态分配内存和构造对象是更常见和方便的做法。
对于new
和delete
要知道他们与malloc
和free
的区别,以及使用动态内存时,需要注意内存泄露。
那本次分享就到这里啦,如果有帮助的话,希望三连支持一下,我们下期再见,如果还有下期的话。