赞
踩
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
//c语言和c++的动态内存开辟对比 int main() { //C语言的动态内存开辟 int* p1 = (int*)malloc(sizeof(int); if (p1 == NULL) { perror("malloc"); } //c++动态的内存开辟 int* p2 = new int;//不会初始化 int* p3 = new int(10);//初始化,一个int int* p4 = new int[10];//开辟10个int型的空间 int* p5 = new int[10]{ 1,2,3,4,5, };//开辟10个int型的空间并初始化为{1,2,3,4,5,0,0,0,0,0,0} free(p1); delete p2; delete p3; delete[] p3; delete[] p4; return 0; }
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。
class A { public: A(int a = 0) : _a(a) { cout << "A():" << this << endl; } ~A() { cout << "~A():" << this << endl; } private: int _a; }; struct ListNode { int _val; ListNode* _next; ListNode(int val) :_val(val) , _next(nullptr) {} }; //ListNode BuyListNode(int x) //{ // //... //} int main() { // new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数 A* p1 = (A*)malloc(sizeof(A)); A* p2 = new A(1); free(p1); delete p2; // 内置类型是几乎是一样的 int* p3 = (int*)malloc(sizeof(int)); // C int* p4 = new int; free(p3); delete p4; A* p5 = (A*)malloc(sizeof(A)*10); A* p6 = new A[10]; free(p5); delete[] p6; //与节点的对比,与c语言中 ListNode* n1 = new ListNode(1); ListNode* n2 = new ListNode(2); ListNode* n2 = new ListNode(3); return 0; }
注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是
系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过
operator delete全局函数来释放空间.
/* 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)
通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
class Stack { public: Stack() { cout << "Stack()" << endl; _a = new int[4]; _top = 0; _capacity = 4; } ~Stack() { cout << "~Stack()" << endl; delete[] _a; _top = _capacity = 0; } private: int* _a; int _top; int _capacity; }; class A { }; int main() { int* p1 = (int*)operator new(sizeof(int));//开辟失败会抛出异常 int* p2 = (int*)malloc(sizeof(int));//开辟失败返回nullptr if (p2 == nullptr) { perror("malloc"); } //申请空间-》调用构造函数 //封装malloc到operator new,开辟失败抛出异常,所以new的底层还是mlloc只不过封装了一些异常,保持面向对象的特性 A* p3 = new A; //先调用析构函数-》再释放p5的空间 //operator delete delete p3; //operator new[] 调用了operator new,一样的 A* p4 = new A[10];//调用10次构造函数 //operator delete[] p4指向的空间。 delete[] p4;//调用10次析构函数 //这种不匹配不会报错,都是内置类型 int* p5 = new int[10]; free(p5); //这种不匹配不会报错,A没有资源释放 A* p6 = new A; free(p6);//没有调用析构函数,但是类里面没有需要释放的内存,所以不会内存泄漏 Stack st;//不用释放内存,因为这是自定义类型,生命周期结束会自动调用析构函数 Stack* pst = new Stack;// delete pst;//先调用析构函数,再去释放pst指向的空间 //free(pst);//只释放了pst指向的空间,没有调用析构函数释放类里面开辟的空间,而且c/c++都不会报错 A* p7 = new A[10]; //delete p7;//这个只会释放一个A的空间 delete[] p7; //free(p7);//不会报错,会释放所有空间(这里可能有点问题,用到再看看) return 0; }
如果只delete一个A对象是从p9开始释放一个对象,但是如果有[],它就会减一个位置,然后知道要释放10个对象,仅限vs编译器是这样
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
- new的原理
调用operator new函数申请空间
在申请的空间上执行构造函数,完成对象的构造
- delete的原理
在空间上执行析构函数,完成对象中资源的清理工作
调用operator delete函数释放对象的空间
- **new T[N]**的原理
调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
在申请的空间上执行N次构造函数
- delete[]的原理
在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
class A { public: A(int a = 0) : _a(a) { cout << "A():" << this << endl; } ~A() { cout << "~A():" << this << endl; } private: int _a; }; // 定位new/replacement new int main() { // p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行 A* p1 = (A*)malloc(sizeof(A)); new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参 //对一块已有的空间初始化 --- 定位new //new(p1)A; p1->~A(); free(p1); A* p2 = (A*)operator new(sizeof(A)); new(p2)A(10); p2->~A(); operator delete(p2); return 0; }
class A { public: A() { } A(int a) { } }; int main() { A* p1 = (A*)malloc(sizeof(A)); if (p1 == nullptr) { perror("malloc"); } //对一块已有的空间初始化 --- 定位new //new(p1)A; new(p1)A(1); return 0; A* p2 = new A; p1->~A(); free(p1); delete p2; }
程序需要频繁使用内存,就提出使用内存池的机制。
向操作系统的堆申请空间比较慢,因为操作系统所有的都向这里申请,如果想要快点,就建立一个内存池,有位置就申请,无位置就继续返回操作系统申请
示例:
#include <iostream>
#include <thread>
void thread_func() {
std::cout << "Thread ID: " << std::this_thread::get_id() << '\n';
}
int main() {
std::thread t1(thread_func);
std::thread t2(thread_func);
t1.join();
t2.join();
return 0;
}
示例:
#include <iostream>
#include <fstream>
#include <sys/mman.h>
#include <fcntl.h>
int main() {
const char* filename = "example.txt";
int fd = open(filename, O_RDONLY);
void* map_ptr = mmap(NULL, sizeof(int), PROT_READ, MAP_PRIVATE, fd, 0);
int value = *(int*)map_ptr;
std::cout << "Value from file: " << value << '\n';
munmap(map_ptr, sizeof(int));
close(fd);
return 0;
}
以上示例和信息展示了C/C++内存分布的更多应用场景和技巧。如有更多问题,请随时提问。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。