问题:
 
在网上看人写了这么一段代码:
 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. Jupyter notebook介绍以及安装

    一.Jupyter介绍 Jupyter Notebook是以web交互式的编程接口,是IPython notebook的升级版本.主要是针对python,另外支持运行 40 多种编程语言.Jupyte ...

  2. 关于Django中路由层(URL-conf)

    关于路由层 路由层其实就是项目中urls.py那个文件,那里面存放着url和视图函数的对应关系它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于客户端发来 ...

  3. 使用hive数据仓库中遇到的问题

    1. 原因:hive版本过高.我用的是3.1.1最高版本,所以报此错.

  4. python 操作excel 的包 函数

    ###########sample 1 https://blog.csdn.net/chengxuyuanyonghu/article/details/54951399 python操作excel主要 ...

  5. chrome 修改请求头的小工具

    chrome 网上应用店中搜索  ModHeader

  6. readline的用法

    with open(r'C:\Users\admin\pycdtest\wanyue\llduizhang_20180207\33_1517970821000304388_119061116',enc ...

  7. 彻底理解和解决移动WEB开发中CLICK点透问题

    在移动WEB开发中,有时候可能会出现点透问题,本文将围绕这个TAP点透问题,详细的讲述到底什么是点透,为什么会出现点透,如何避免出现点透,如果不可避免的出现了,如何解决解决移动WEB开发中CLICK点 ...

  8. LeetCode 303.区域检索-数组不可变(accumulate()和for循环差异分析)

    给定一个整数数组  nums,求出数组从索引 i 到 j  (i ≤ j) 范围内元素的总和,包含 i,  j 两点. 示例: 给定 nums = [-2, 0, 3, -5, 2, -1],求和函数 ...

  9. JavaScript操作符(=?,)优先级

    JavaScript操作符优先级: 关于最后3个运算符的优先级比较,下面通过一个实例来具体说明: var a,b,c; a = 3,4,5; b = a--,--a,a; c = a ? b++ : ...

  10. mongodb3集群搭建

    三台服务器:先设置hosts 10.0.0.231 node1 10.0.0.232 node2 10.0.0.233 node3 1:下载 mongodb-linux-x86_64-rhel70-3 ...