问题:
 
在网上看人写了这么一段代码:
 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[];
delete []p1; system("pause");
return ;
}
如果定义了析构函数的话:
operator new[]会输出:
call global new[] size:7
否则输出:
call global new[] size:3

解答:
 
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*) - ;
size_t p2 = reinterpret_cast<size_t>(p1 - );
p2 = p2 & ~mask;
std::cout << *reinterpret_cast<int*>(p2) << std::endl;
就可以得到数组的大小。
2. 没有析构函数时候的情况
在1中我们提到new[]申请的空间可以大于数组实际所需空间,以记录数组长度,但在缺少有效析构函数(non-trivial destructor,非标准中译)的情况下delete仅仅将这一块连续的内存空间释放就可以了,所以无需记录数组长度,这时new[]申请的空间可以等于数组实际所需的空间。
关于有效析构函数
简单说就是自身及其非静态成员(包括继承的)都必须没有定义或删除了析构函数。申请这样对象的数组时一般不会使用额外空间去记录其长度。

c++为什么定义了析构函数的类的operator new[]传入的参数会多4字节?的更多相关文章

  1. C++中定义使用受限的类

    1.只能在堆上使用的类 栈上对象通过自动调用析构函数释放空间,将该类的析构函数定义为private就可以阻止其被自动调用,从而阻止在栈上使用该类的对象,为了避免资源泄漏问题,在堆上使用该类的对象时我们 ...

  2. 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)

    [源码下载] 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成 ...

  3. 设计、定义并实现Complex类

    设计.定义并实现Complex类 #include <iostream> #include <cmath> using namespace std; class MyCompl ...

  4. 【c++ primer, 5e】设计Sales_data类 & 定义改进的Sales_data类

    [设计Sales_data类] 1.考虑Sales_data类应该有那些接口. isbn.combine.add.read.print... 2.考虑如何使用这些接口. Sales_data tota ...

  5. 2017.9.2Java中的自定义类型的定义及使用&&自定义类的内存图

    今日内容介绍 1.自定义类型的定义及使用 2.自定义类的内存图 3.ArrayList集合的基本功能 4.随机点名器案例及库存案例代码优化 01引用数据类型_类 * A: 数据类型 * a: java ...

  6. Java学习笔记十四:如何定义Java中的类以及使用对象的属性

    如何定义Java中的类以及使用对象的属性 一:类的重要性: 所有Java程序都以类class为组织单元: 二:什么是类: 类是模子,确定对象将会拥有的特征(属性)和行为(方法): 三:类的组成: 属性 ...

  7. java: Comparable比较器,定义二叉操作类

    //定义二叉操作类 class BinaryTree{ class Node{ private Node left; //左指数 private Node right; //右指数 private C ...

  8. 怎样找出自己定义标签的java类

    怎样找出自己定义标签的java类 这是一个逆推的过程(建立自己定义标签能够查看下面连接:http://blog.csdn.net/antoniochan/article/details/3810990 ...

  9. JSP自己定义标签继承哪个类

    JSP自己定义标签继承哪个类 解:JSP自己定义标签继承TagSupport

随机推荐

  1. C#是类型安全语言

    C#是一种类型安全语言:所有的表达式都解析成某个类型的一个实例,在编译器生成的代码中,只会执行对这个类型来说有效的操作. [优势] 许多错误能在编译时就检测到,确保代码在执行这段代码前是正确的: 生成 ...

  2. 一行CMD命令kill(杀)掉你的进程

    查看进程 pi@raspberry:~ $ ps -ef | grep python3 UID PID PPID C STIME TTY TIME CMD pi 4678 4666 0 11:57 p ...

  3. .netcore在linux下使用P/invoke方式调用linux动态库

    http://www.mamicode.com/info-detail-2358309.html   .netcore下已经实现了通过p/invoke方式调用linux的动态链接库(*.so)文件 1 ...

  4. 使用 Moq 测试.NET Core 应用

    第一篇文章, 关于Mock的概念介绍: https://www.cnblogs.com/cgzl/p/9294431.html 第二篇文章, 关于方法Mock的介绍: https://www.cnbl ...

  5. HTTP的请求头标签 If-Modified-Since

    一直以来没有留意过HTTP请求头的IMS(If-Modified-Since)标签. 最近在分析Squid的access.log日志文件时,发现了一个现象.就是即使是对同一个文件进行HTTP请求,第一 ...

  6. Silverlight TreeView 带 checkbox和图片

    前段时间做Silverlight TreeView 控件,但是要带checkbox和图片,在网上到处找相关的例子,效果图如下 xaml代码 <UserControl x:Class=" ...

  7. 分类模型输出y值

    y=w0+w1x1+w2x2+....+wnxn coef_:存储w1,w2,...wn. intercept_:存储w0 dual_coef_*support_vectors_=coef_ (1)S ...

  8. equals和等号的区别

    如果是基本类型,等号比较的是数值.如果是引用类型,等号比较的是地址.而equals如果没有重写的话默认比较的是地址,可以重写equals来自定义比较两个对象的逻辑.

  9. CharacterController控制的物体移动

    CharacterController控制的物体移动: public class playerMove  : MonoBehaviour { public float Speed; private C ...

  10. 微信web开发者工具 && 微信调试页面

    微信开发者工具 做微信公众号的过程中,自然避免不了登录账号然后进行调试,但是在chrome上我们没有办法登录,这是一个令人头疼的问题,比如这个公众号网页,只会提示出错,因为开发者限制了公众号网页的登录 ...