问题:
 
在网上看人写了这么一段代码:
 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. git学习--远程分支删除

    查看远程分支 git branch -r  使用下面两条命令来删除远程分支 git branch -r -d origin/branch-name git push origin :branch-na ...

  2. css过渡transition

    定义 过渡transition是一个复合属性,包括transition-property.transition-duration.transition-timing-function.transiti ...

  3. day15 面向对象 成员

    成员 1. 变量 1.实例变量 格式: 变量.xxx=xx (称为实例变量,也叫属性,字段)给对象用的 2.类变量 类变量:直接写在类中的变量就是类变量,类变量一般用类名来访问 其实就是类中相同的属性 ...

  4. Jmeter源代码学习心得

    1.TestPlan和WorkBench GUI类是直接加载的,因此左边的树形菜单开始启动Jmeter时显示也是这两个,默认写死了的!可以改源码!在MenuFactory中有相应代码. 2.其它的GU ...

  5. php数组·的方法1-数组统计函数

    /** * 下面是数组统计函数 * * * **/ //count() 数组的长度 print_r(count($arr3)); echo '<hr>'; //max() min() 数组 ...

  6. 8php字符串的方法

    <?php/** * Created by PhpStorm. * User: DY040 * Date: 2017/9/8 * Time: 16:46 *//*php也有转义字符/*///ec ...

  7. Jenkins遇到哪些坑~

    1Jenkins关闭和重启实现方式. 1.关闭Jenkins ​ 只需要在访问jenkins服务器的网址url地址后加上exit.例如我jenkins的地址http://localhost:8080/ ...

  8. 笔记-spring aop 原理学习2

    InstantiationAwareBeanPostProcessor AnnotationAwareAspectJAutoProxyCreator https://blog.csdn.net/qq_ ...

  9. Oracle基础篇--03DML语言

    1.数据准备: --创建表格的 create table dept as select * from scott.dept; create table emp as select * from sco ...

  10. PlayMaker Destroy Self 和 Destroy Object 和 Set Visibility

    1. 这个销毁是销毁状态机所在的游戏物体,不能销毁父物体. 2. 这个销毁只要把想销毁的游戏物体拖进去就可以. 3. 这个其实不是真正的销毁游戏对象,只是把它的 MeshRenderer 组件关上了, ...