
文章目录
?new和delete操作自定义类型? operator new与operator delete函数?operator new与operator delete函数 ?new和delete的实现原理?内置类型?自定义类型 ?定位new表达式(placement-new)?总结
?new和delete操作自定义类型
我们先看malloc与free,调试可以发现并不会调用析构函数
class A{public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}private:int _a;};int main(){A* p1 = (A*)malloc(sizeof(A));free(p1);return 0;} 
再看new和delete
A* p2 = new A(1);delete p2; 
总结:
new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数
而对于内置类型几乎是一样的
int* p3 = (int*)malloc(sizeof(int));int* p4 = new int;free(p3);delete p4; 这是汇编一览图:
此时多出来了一个operator new这是什么,为什么new会去调用operator new?
00482C36 call operator new(048114Ah ) 
? operator new与operator delete函数
?operator new与operator delete函数
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc){// try to allocate size bytesvoid* 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 new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem; 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_FINALLYreturn;} free的实现
#define free(p) _free_dbg(p, _NORMAL_BLOCK) 如抛异常例子:
double Division(int a, int b){// 当b == 0时抛出异常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);}void Func(){int len, time;cin >> len >> time;cout << Division(len, time) << endl;}int main(){try {Func();}catch(const char* errmsg){//当 Division(len, time) 函数抛出这种异常时,异常对象会被赋值给 errmsg 变量。//然后,这个 catch 块会输出 errmsg 的内容,即 "Division by zero condition!"cout << errmsg << endl;}catch (...){//这个 catch 块用于捕获任何其他类型的未知异常。//当 try 块中发生任何其他类型的异常时,这个 catch 块会被执行。//它会输出 "unkown exception"。cout << "unkown exception" << endl;}return 0;} 当你输入两个数让b == 0时程序抛异常,抛出"Division by zero condition!"他不会再回到Func()函数中的cout << Division(len, time) << endl;而是会跳到catch(const char* errmsg)中,异常对象会被赋值给 errmsg 变量。然后,这个 catch 块会输出 errmsg 的内容,即 "Division by zero condition!",程序结束。
总结:
通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
?new和delete的实现原理
?内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
?自定义类型
new的原理
operator new函数申请空间在申请的空间上执行构造函数,完成对象的构造
delete的原理在空间上执行析构函数,完成对象中资源的清理工作调用operator delete函数释放对象的空间 class Stack {public: // 构造函数 Stack(int initialCapacity = 10) : _a(new int[initialCapacity]) , _top(0) , _capacity(initialCapacity) {} // 析构函数 ~Stack() { delete[] _a; } // 压入元素 void push(int value) { // 如果栈已满,需要扩容 if (_top == _capacity) { resize(2 * _capacity); } // 将元素压入栈顶 _a[_top++] = value; } // 弹出栈顶元素 int pop() { // 如果栈为空,抛出异常 if (_top == 0) { throw runtime_error("Stack is empty"); } // 弹出栈顶元素并返回 return _a[--_top]; } // 判断栈是否为空 bool empty() const { return _top == 0; } // 返回栈中元素的个数 int size() const { return _top; }private: // 扩容函数,分配新的数组空间并移动元素 void resize(int newCapacity) { // 分配新的数组空间 int* newArray = new int[newCapacity]; // 使用 std::move 将原数组中的元素移动到新数组中 move(_a, _a + _top, newArray); // 释放原数组空间 delete[] _a; // 更新数组指针和容量 _a = newArray; _capacity = newCapacity; } // 存储元素的数组指针 int* _a; // 栈顶元素的索引 int _top; // 数组的容量 int _capacity;};int main() { // 创建一个栈 Stack* p3 = new Stack; // 释放栈的内存 delete p3; 压入三个元素 //p3->push(1); //p3->push(2); //p3->push(3); 依次弹出并打印元素 //while (!p3->empty()) //{ // cout << p3->pop() << " "; //} //cout << endl; return 0;} 
先析构_a指向的空间,再释放p3指向的空间
new T[N]的原理
operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请在申请的空间上执行
N次构造函数 delete[]的原理
3. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
4. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
class A{public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}private:int _a;};int main(){A* p1 = new A;A* p2 = new A[10];delete p1;delete[] p2;return 0;} 
A类只有一个4字节大小的int成员变量。那么,创建一个A类型的对象应该需要4字节的内存空间。A* p2 = new A[10];这我们动态创建了一个包含10个A对象的数组。10 * 4 = 40 bytes,为什么是44bite呢?
在动态分配数组内存时,编译器通常会在实际的数组内存之前分配一些额外的空间,用于存储数组的元素个数等信息。这样做的目的是为了在执行delete[]操作时,能够正确地调用所有元素的析构函数。
总结:都开4byte存储对象个数,方便delete[]时,知道有多少个对象,要调用多少次析构函数

内置类型就没有额外开空间:
因为,内置类型已经固定好,无需调用析构函数
int* p3 = new int[10];delete[] p3; 
?定位new表达式(placement-new)
定位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 newint main(){// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行A* p1 = (A*)malloc(sizeof(A));new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参p1->~A();free(p1);A* p2 = (A*)operator new(sizeof(A));new(p2)A(10);p2->~A();operator delete(p2);return 0;} A* p1 = (A*)malloc(sizeof(A)); 使用malloc()函数分配了一块与A类对象大小相同的内存空间,但此时p1指向的只是一块内存空间,还不是一个真正的A对象,因为A的构造函数还没有被调用。
new(p1)A; 使用"定位new"的语法在已分配的内存空间上构造一个A对象。如果A类的构造函数有参数,需要在这里传入参数,例如new(p2)A(10);。
p1->~A(); 显式地调用A对象的析构函数,释放对象占用的资源。
free(p1); free()函数释放之前使用malloc()分配的内存空间。
不要错配使用delete和free,一定匹配使用,否则结果是不确定
?总结
