c++为什么定义了析构函数的类的operator new[]传入的参数会多4字节?
2015-01-25 07:48
337 查看
[b]问题:[/b]
在网上看人写了这么一段代码:
如果定义了析构函数的话:
operator new[]会输出:
call global new[] size:7
否则输出:
call global new[] size:3
[b]解答:[/b]
1. 有析构函数为什么会多4字节
这多出来的空间是为了记录数组的长度以及内存对齐的。
我们来看看C++标准里的表述。
这里提到了delete并不知道数组长度是多少。这个职责由new[]完成。标准库中new[]的实现一般是先申请一块sizeof(T) * n + x的空间,使用最初的空间记录数组长度,从下一个对齐了的地址开始才是对象数组实际使用的空间。这点可以以下通过简单修改程序观测到。构造函数中打印this指针,new[]函数中先记录malloc的值,打印并返回。
多出来的这个x空间具体是多少呢,这个C++标准并没有严格定义,只是给出一些条件。
大意是最终应该保证对象实际所占用的空间必须符合当前平台的内存对齐要求。对于x86来说是4字节对齐,即指针作为一个整数必须能被4整除。对于x64来说是8字节对齐。结合上面那段new[]应该负责记录数组长度这一规定,可以得出如下规则。
1. 由new[]返回的空间中最初空间用于长度记录,答主实测VC上使用的是4字节int。
2. 依据内存对齐要求补齐空间。
3. 对象数组实际占用的空间。
整体情况就是这样: [size][pad][array]
如何检测我们的结论?
在主函数中new和delete之间加入如下代码:
就可以得到数组的大小。
2. 没有析构函数时候的情况
在1中我们提到new[]申请的空间可以大于数组实际所需空间,以记录数组长度,但在缺少有效析构函数(non-trivial destructor,非标准中译)的情况下delete仅仅将这一块连续的内存空间释放就可以了,所以无需记录数组长度,这时new[]申请的空间可以等于数组实际所需的空间。
关于有效析构函数
简单说就是自身及其非静态成员(包括继承的)都必须没有定义或删除了析构函数。申请这样对象的数组时一般不会使用额外空间去记录其长度。
http://www.zhihu.com/question/26765276
在网上看人写了这么一段代码:
class A { public: A() { std::cout<<"call A constructor"<<std::endl; } ~A() { std::cout<<"call A destructor"<<std::endl; } void* operator new(size_t size) { std::cout<<"call A::operator new[] size:"<<size<<std::endl; return malloc(size); } void operator delete[](void* p) { std::cout<<"call A::operator delete[]"<<std::endl; free(p); } void operator delete(void* p) { free(p); } };#include <iostream> #include "A.h" void* operator new[](size_t size) { std::cout<<"call global new[] size: "<<size<<std::endl; return malloc(size); } void operator delete[](void* p) { std::cout<<"call global delete[] "<<std::endl; } int _tmain(int argc, _TCHAR* argv[]) { std::cout<<"sizeof A "<<sizeof(A)<<std::endl; A* p1 = new A[3]; delete []p1; system("pause"); return 0; }
如果定义了析构函数的话:
operator new[]会输出:
call global new[] size:7
否则输出:
call global new[] size:3
[b]解答:[/b]
1. 有析构函数为什么会多4字节
这多出来的空间是为了记录数组的长度以及内存对齐的。
我们来看看C++标准里的表述。
这里提到了delete并不知道数组长度是多少。这个职责由new[]完成。标准库中new[]的实现一般是先申请一块sizeof(T) * n + x的空间,使用最初的空间记录数组长度,从下一个对齐了的地址开始才是对象数组实际使用的空间。这点可以以下通过简单修改程序观测到。构造函数中打印this指针,new[]函数中先记录malloc的值,打印并返回。
多出来的这个x空间具体是多少呢,这个C++标准并没有严格定义,只是给出一些条件。
大意是最终应该保证对象实际所占用的空间必须符合当前平台的内存对齐要求。对于x86来说是4字节对齐,即指针作为一个整数必须能被4整除。对于x64来说是8字节对齐。结合上面那段new[]应该负责记录数组长度这一规定,可以得出如下规则。
1. 由new[]返回的空间中最初空间用于长度记录,答主实测VC上使用的是4字节int。
2. 依据内存对齐要求补齐空间。
3. 对象数组实际占用的空间。
整体情况就是这样: [size][pad][array]
如何检测我们的结论?
在主函数中new和delete之间加入如下代码:
size_t mask = sizeof(void*) - 1; size_t p2 = reinterpret_cast<size_t>(p1 - 1); p2 = p2 & ~mask; std::cout << *reinterpret_cast<int*>(p2) << std::endl;
就可以得到数组的大小。
2. 没有析构函数时候的情况
在1中我们提到new[]申请的空间可以大于数组实际所需空间,以记录数组长度,但在缺少有效析构函数(non-trivial destructor,非标准中译)的情况下delete仅仅将这一块连续的内存空间释放就可以了,所以无需记录数组长度,这时new[]申请的空间可以等于数组实际所需的空间。
关于有效析构函数
简单说就是自身及其非静态成员(包括继承的)都必须没有定义或删除了析构函数。申请这样对象的数组时一般不会使用额外空间去记录其长度。
http://www.zhihu.com/question/26765276
相关文章推荐
- C++析构函数为什么要定义成虚函数
- C++ :函数中无需改变传入参数的值时,定义时把 “Item & x ” 都改成 \"const Item & x\"
- 为什么要把父类的析构函数定义成虚函数?
- 为什么要把父类的析构函数定义成虚函数
- C++中将构造函数或析构函数定义为private
- C++ - 为什么析构函数不允许抛出异常
- C++ 异常 与 ”为什么析构函数不能抛出异常“ 问题
- C++中函数中参数和返回值都是用户定义类型(这里主要是类的情况时的分析)
- C++ 异常 与 ”为什么析构函数不能抛出异常“ 问题
- C++.net 定义类 将类对象作为函数参数传入另一个类 出现错误
- 终于明白为什么无参数函数名,以及函数指针定义时参数列表一定要写上(void)而不是空着了
- C++中析构函数为什么要是虚函数
- 为什么有时类的析构函数要定义为protected呢?
- C++:析构函数写成虚函数是干什么用的,为什么要这么写?
- 为什么C++中的拷贝构造函数参数不能值传递
- c++如果定义了析构函数,则必须有析构函数的函数体,那怕是空的也行,如果没有定义析构函数,则会调用默认的析构函数
- C++中inline函数的定义为什么要放在头文件中?头文件中“只定义一份”是什么意思?const object 和inline函数是“一次定义规则”
- [VB.NET]参数定义为什么出错?
- 析构函数为什么不能有参数???
- 【C/C++】sprintf中如何将容器作为可变参数列表传入