首页 > 代码库 > new delete 关键字深度解析
new delete 关键字深度解析
1、new调用了构造函数,delete调用了析构函数? 实际上这是一个错误的想法。
2、new确实是对malloc进行了包装,看不到源码,我们只能猜测一下,C++标准库中规定的operator new 操作有没有调用构造函数?我也还不知道。唯一正确就是"operator new typename(parameter) "实际上是分解为三个步骤:
看懂这句话就可以啦。operator new /*参数1:*/ typename ( /*"参数2 为:typename调用构造函数的参数,不是new 操作的“*/ parameter,...)。
2.1)编译器根据我们提供参数1:typename,去计算字节长度,然后通过operator new(size_t sz,...)去申请内存。这过程中通过调用标准库malloc或相关函数向OS申请内存资源。(内存分配的细节属于操作系统领域,今天不深化)
2.2) 假设成功编译器生成在目标地址调用构造函数的代码,实际操作类似于placement new 操作:new(this) typename( parameter,...);这里才是调用构造函数的地方,调用构造函数不属于关键字new的行为,是编译器根据typename的解析出来的行为,增加在new分配内存地址之后的跟随操作。placement new称为原地构造操作,对应的方式调用析构函数,如果显示使用了这个构造式:new(this)Constructor(),则也要显式使用:this->Constructor()::~Constructor().(C++11)之后this为常量,所以要用其他指针来显式调用析构函数。
2.3)返回目标地址。(不探讨失败的情况,因为没看过编译器实现new的源码,所以无法正确猜想异常的情况,就不误人子弟)。
以上思想主要来自 Thinking in C++和百度到的链接博客,经过本人深化,从此不在担心new操作不适当使用了。
同理delete的操作则是:
编译器通过指针类型区调用目标类型的析构函数去析构对象,然后才执行类似free的行为,将内存区域会交给OS分配给程序的堆表中(OS和进程之间怎么关于内存管理这块我也不了解,希望了解-的朋友在评论中给我个链接,谢啦)。
这也是delete关键字处理void类型的指针可能出现内存泄露的原因。因为编译器无法推导该类型的内存区域大小,无法用某个格式化的方式去映射这片区域(实际上函数表中对应的析构函数都是void类型的)。编译器只能执行 free (void*)操作,向堆区提交单个void*长度地址,跟机器有关32位是4Bytes。除非delete的关键字的实现能访问OS的内存表,才能正确回收改块内存而不泄露。这也是C++标准委员会要解决内存回收机制的问题,实现难度实在太高了。
//仔细看delete参数即可知,delete关键字的操作是没有类型,不执行析构操作。
void * operator new(size_t count);
void operator delete(void *p);
真正地重载new delete。很多种方式可供重载,同样new [] 数组 和 delete [] 数组也是很多种,少说有5种,目前没看过源码的api或标准库的说明资料。其他百度上面找得到的基本最后利用malloc和free去替换,而不是调用C++ 标准关键字 new/delelte去实现重载。
先看一小段:delete和delete[]的分析:
// TestNewDeleteArray.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <string> #include <iostream> #include <windows.h> using namespace std; class A { public: ~A() { cout << "Goodbye A" << endl; } void * operator new[](size_t count) { cout << "size:" << count<<endl; //84 return malloc(count); } private: int i; int s; }; void d(A*p) { delete[] p; } int _tmain(int argc, _TCHAR* argv[]) { A* p; cout.setf(ios::hex); while (true) { p = new A[10]; cout << p << endl; //011CC164,除了id一次不同之外 delete p; p = NULL; Sleep(5000); } return 0; }
1:delete 和 delete[]不能混用。
如图:
因为他们的内存分配方式不一致,new []分配的长度加上数组的结束符。也就是平时我与在string字符串中经常碰见的"\0"。实际上自定义类数据数组形式结束符长度为4字节(win32编译器)。待续。。
new delete 关键字深度解析