C++中new/delete和new[]/delete[]实例详解
直接进入正题!
首先,明确new和delete不是函数,是C++的运算符,支持重载。
其次,先简单了解一下new和delete的工作机制;
string *sp = new string("hello"); string *arr = new string[10];
上述代码实际执行分为三步:
1、new表达式调用名为operator new(或者operator new[])的标准库函数。该函数分配一块足够大小、原始的、为命名的内存空间;
2、编译器运行相应的构造函数,初始化第一步中申请的内存空间,注意内置数据类型不会调用构造函数;
3、对象分配的空间构造完成,返回一个指向对象的指针;
当使用delete语句删除动态分配的对象时:
delete sp; delete [] arr;
实际上执行了两步操作:
1、对sp所指向的对象或者arr所指向的数组中的元素执行对应的析构函数;
2、通过标准库函数operator delete释放掉内存空间;
那么delete[]如何知道应该调用多少次析构函数呢?通过下面的演示将会很直观的知道答案static long long times= 0;
class A { public: A(); ~A(); int aa; int bb; private: }; A::A() { aa = 100; bb = 10000; } A::~A() { aa = 10; bb = 1; times++; }
首先在main函数中,打印出new和new[]对象的地址,到内存中看一下对应的值:
pa所指向地址010F7CD8可以看到aa的值为0x64,bb的值为0x2710,转化为十进制分别是100, 1000,正确构造了,而010F7CD8的前面四个字节是fdfdfdfd,并没有存储需要析构的次数;
pas所指向地址010D578C,前四个直接0x0a指出了需要析构的次数,而之后的80字节则是对应的数据,也能看到正确完成了构造;
看到这里,上面的疑问应该是已经解决了。
如果将数据类型换成内置数据类型,比如int,结果又会是如何?
可以看到内置数据类型不需要调用构造函数和析构函数。
那么如果new/delete,new[]/delete[]不成对使用会出现什么情况?
针对于内置数据类型,是不会出现异常情况的;本文主要针对自定义类型:
可以看到delete去释放new[]申请的对象时,只对数组的第一个类对象调用了析构函数,后面的两个对象均没调用析构函数,如果类对象中申请了大量的内存需要在析构函数中释放,而你却在销毁数组对象时少调用了析构函数,这会造成内存泄漏。
再看看new/delete[]的情况,因为这种情况下析构函数会访问非法地址,需要将上面的析构函数改写一下;
A::~A() { times++; }
这里程序需要执行一段时间,执行之后我的VS报错了,然后我们看一下内存数据:
此时还需要看一下times的值,times是用来记录析构被调用次数的,
答案已经很明显了。这里调用了0xfdfdfdfd次数的析构函数,根据delete[]的原理,会多回收4字节的内存。
总结一下:
1、针对自定义的数据结构,我们可以自己重载operator new和operator delete覆盖标准函数库的定义;
2、new[]在申请对象的时候会多申请四字节的内存用于记录需要调用析构函数的次数,delete[]在释放对象的时候会根据对应的四字节调用N次的析构函数,并且free的时候会多释放4字节的内存;
3、new和delete,new[]和delete[]最好成对使用,避免错误;