- 浏览: 500889 次
- 性别:
- 来自: 北京
-
文章分类
最新评论
-
jkxydp:
算法运行的结果根本就不对。
BM算法. -
soarwindzhang:
感谢博主的分享,我今天看了您的UFSET非递归的路径压缩时感觉 ...
并查集 -
zhangning290:
楼主好像只考虑了坏字符规则,。没有考虑好后缀
BM算法. -
lsm0622:
文字描述有错误 误导新学者
求有向图的强连通分量(scc):Tarjan算法 -
knightchen:
博主,你太强了!这篇文章对我学习C++多线程很有帮助!谢谢
并发学习之一_windows下ZThread在CodeBlocks上的安装与配置
<<c++ primer>>练习 14.11 中提到:
Account *parray=new Account[100];
delete parray;
delete [] parray;
方括号的存在会使编译器获取数组大小(size)然后析构函数再被依次应用在每个元素上,一共size次。否则,只有一个元素被析构。
无论哪种情况,分配的全部空间被返还给自由存储区。
我的问题是:为什么无论哪种情况,分配的全部空间被返还给自由存储区?
对于delete parray,为什么不是删除单个Account元素,而是删除了100个.
编译器怎么知道parray这个指针实际指向的是数组还是单个元素,即便知道指向的是自由存储区的数组,这个数组的大小又怎么知道。
难道是编译器辅助行为?
总结:空间释放(肯定有个log记录分配的大小)和调用析构函数(类型识别,不同的编译器实现不同)采用不同的机制.
(1)一般在分配时分配器会自动写一个日志(一般在分配使用得内存之前又一个结构),用于记录分配的大小,分配内容的sizeof等等。
直观得想想,delete和delete[]都是传入一个void*如果不保存日志就无法知道分配时到底是分配了一个还是多个单元.
所以虽然delete和delete[]不同但是分配器在执行释放过程中都会读取这个日志,从而了解到底应该释放多少内存,但是从程序员的角度来说,既然分配了数组,就应该用delete[]
(2)在VC下用汇编跟过delete[]的执行情况,发现这个 "日志 "就是一个4字节长的整数记录数组元素个数,紧挨在数组第一个元素之前.
但是有个前提:对象类型(或其基类)有显式析构函数.换句话说,析构函数是非trivial的.
否则的话,数组前面是没有这个日志的.其实对于析构函数是trivial的情况,delete[]时无需调用其析构函数,因此此时VC把delete[]当做delete同样处理.
(3)因为释放数组空间和为数组调用析构函数是两个独立的部分,可以使用不同的机制来实现。
释放空间的机制是需要绝对保证的。因此,即使你不写delete[],它也会将所有空间释放,其机制可以是前置的长度信息,也可以不是(如后置的特征分割符等等)。
而调用析构函数可以一般采用前置长度信息的方式(当然也可以有其他方式)。在没有[]提示时,编译器在调用析构就将它当一个元素,而不会使用数组方式来调用每一个析构函数了。
LS:“但是有个前提:对象类型(或其基类)有显式析构函数.换句话说,析构函数是非trivial的. 否则的话,数组前面是没有这个日志的.”
——这说明,LS使用的编译器在释放数组空间时,并没有用前置的长度信息的方式。由此可见,释放数组空间和为数组调用析构函数确实可以使用不同的机制
(4)delete parray,编译器得到类型信息是Account单个的指针,那么释放时,只调用一次析构函数。
delete[] parray,编译器得到的类型信息是Account[]类型,则按照Account数组来处理,依次调用每个元素的析构函数。
注意,以上是在编译期间就确定下来的,编译器识别到类型信息的不同会决定调用析构函数的情况有不同。
但是对于内存释放,delete操作则不是通过类型信息来确定分配的内存大小,那么内存大小的信息从什么地方得到呢?
当我们使用operator new为一个自定义类型对象分配内存时,实际上我们得到的内存要比实际对象的内存大一些,这些内存除了要存储对象数据外,还需要记录这片内存的大小,此方法称为 cookie。这一点上的实现依据不同的编译器不同。(例如 MFC 选择在所分配内存的头部存储对象实际数据,而后面的部分存储边界标志和内存大小信息。g++ 则采用在所分配内存的头4个自己存储相关信息,而后面的内存存储对象实际数据。)当我们使用 delete operator 进行内存释放操作时,delete operator 就可以根据这些信息正确的释放指针所指向的内存块。
对于parray指针,可以根据这样的cookie信息来得到指向内存空间的大小,delete parray和delete[] parray都是一样的,同样一个指针,cookie信息是相同的,所以对应的内存都会被释放掉。但是由于编译器理解两种情况下的类型是不同的,所以调用析构函数会有不同。
(5)难道是编译器辅助行为?
没错,就是。不同的编译器可能采用的具体方法有可能不一样,但不管采用什么方法,编译器必须记住那块大小。
(6)转自<<effective c++>>
条款5:对应的new和delete要采用相同的形式
下面的语句有什么错?
string *stringarray = new string[100];
delete stringarray;
一切好象都井然有序——一个new对应着一个delete——然而却隐藏着很大的错误:程序的运行情况将是不可预测的。至少,stringarray指向的100个string对象中的99个不会被正确地摧毁,因为他们的析构函数永远不会被调用。
用new的时候会发生两件事。首先,内存被分配(通过operator new 函数,详见条款7-10和条款m8),然后,为被分配的内存调用一个或多个构造函数。用delete的时候,也有两件事发生:首先,为将被释放的内存调用一个或多个析构函数,然后,释放内存(通过operator delete 函数,详见条款8和m8)。对于 delete来说会有这样一个重要的问题:内存中有多少个对象要被删除?答案决定了将有多少个析构函数会被调用。
这个问题简单来说就是:要被删除的指针指向的是单个对象呢,还是对象数组?这只有你来告诉delete。如果你在用delete时没用括号,delete就会认为指向的是单个对象,否则,它就会认为指向的是一个数组:
string *stringptr1 = new string;
string *stringptr2 = new string[100];
...
delete stringptr1;// 删除一个对象
delete [] stringptr2;// 删除对象数组
如果你在stringptr1前加了"[]"会怎样呢?答案是:那将是不可预测的;
如果你没在stringptr2前没加上"[]"又会怎样呢?答案也是:不可预测。
int这样的固定类型来说,结果也是不可预测的,即使这样的类型没有析构函数。所以,解决这类问题的规则很简单:如果你调用new时用了[],调用delete时也要用[]。如果调用new时没有用[],那调用delete时也不要用[]。
Account *parray=new Account[100];
delete parray;
delete [] parray;
方括号的存在会使编译器获取数组大小(size)然后析构函数再被依次应用在每个元素上,一共size次。否则,只有一个元素被析构。
无论哪种情况,分配的全部空间被返还给自由存储区。
我的问题是:为什么无论哪种情况,分配的全部空间被返还给自由存储区?
对于delete parray,为什么不是删除单个Account元素,而是删除了100个.
编译器怎么知道parray这个指针实际指向的是数组还是单个元素,即便知道指向的是自由存储区的数组,这个数组的大小又怎么知道。
难道是编译器辅助行为?
总结:空间释放(肯定有个log记录分配的大小)和调用析构函数(类型识别,不同的编译器实现不同)采用不同的机制.
(1)一般在分配时分配器会自动写一个日志(一般在分配使用得内存之前又一个结构),用于记录分配的大小,分配内容的sizeof等等。
直观得想想,delete和delete[]都是传入一个void*如果不保存日志就无法知道分配时到底是分配了一个还是多个单元.
所以虽然delete和delete[]不同但是分配器在执行释放过程中都会读取这个日志,从而了解到底应该释放多少内存,但是从程序员的角度来说,既然分配了数组,就应该用delete[]
(2)在VC下用汇编跟过delete[]的执行情况,发现这个 "日志 "就是一个4字节长的整数记录数组元素个数,紧挨在数组第一个元素之前.
但是有个前提:对象类型(或其基类)有显式析构函数.换句话说,析构函数是非trivial的.
否则的话,数组前面是没有这个日志的.其实对于析构函数是trivial的情况,delete[]时无需调用其析构函数,因此此时VC把delete[]当做delete同样处理.
(3)因为释放数组空间和为数组调用析构函数是两个独立的部分,可以使用不同的机制来实现。
释放空间的机制是需要绝对保证的。因此,即使你不写delete[],它也会将所有空间释放,其机制可以是前置的长度信息,也可以不是(如后置的特征分割符等等)。
而调用析构函数可以一般采用前置长度信息的方式(当然也可以有其他方式)。在没有[]提示时,编译器在调用析构就将它当一个元素,而不会使用数组方式来调用每一个析构函数了。
LS:“但是有个前提:对象类型(或其基类)有显式析构函数.换句话说,析构函数是非trivial的. 否则的话,数组前面是没有这个日志的.”
——这说明,LS使用的编译器在释放数组空间时,并没有用前置的长度信息的方式。由此可见,释放数组空间和为数组调用析构函数确实可以使用不同的机制
(4)delete parray,编译器得到类型信息是Account单个的指针,那么释放时,只调用一次析构函数。
delete[] parray,编译器得到的类型信息是Account[]类型,则按照Account数组来处理,依次调用每个元素的析构函数。
注意,以上是在编译期间就确定下来的,编译器识别到类型信息的不同会决定调用析构函数的情况有不同。
但是对于内存释放,delete操作则不是通过类型信息来确定分配的内存大小,那么内存大小的信息从什么地方得到呢?
当我们使用operator new为一个自定义类型对象分配内存时,实际上我们得到的内存要比实际对象的内存大一些,这些内存除了要存储对象数据外,还需要记录这片内存的大小,此方法称为 cookie。这一点上的实现依据不同的编译器不同。(例如 MFC 选择在所分配内存的头部存储对象实际数据,而后面的部分存储边界标志和内存大小信息。g++ 则采用在所分配内存的头4个自己存储相关信息,而后面的内存存储对象实际数据。)当我们使用 delete operator 进行内存释放操作时,delete operator 就可以根据这些信息正确的释放指针所指向的内存块。
对于parray指针,可以根据这样的cookie信息来得到指向内存空间的大小,delete parray和delete[] parray都是一样的,同样一个指针,cookie信息是相同的,所以对应的内存都会被释放掉。但是由于编译器理解两种情况下的类型是不同的,所以调用析构函数会有不同。
(5)难道是编译器辅助行为?
没错,就是。不同的编译器可能采用的具体方法有可能不一样,但不管采用什么方法,编译器必须记住那块大小。
(6)转自<<effective c++>>
条款5:对应的new和delete要采用相同的形式
下面的语句有什么错?
string *stringarray = new string[100];
delete stringarray;
一切好象都井然有序——一个new对应着一个delete——然而却隐藏着很大的错误:程序的运行情况将是不可预测的。至少,stringarray指向的100个string对象中的99个不会被正确地摧毁,因为他们的析构函数永远不会被调用。
用new的时候会发生两件事。首先,内存被分配(通过operator new 函数,详见条款7-10和条款m8),然后,为被分配的内存调用一个或多个构造函数。用delete的时候,也有两件事发生:首先,为将被释放的内存调用一个或多个析构函数,然后,释放内存(通过operator delete 函数,详见条款8和m8)。对于 delete来说会有这样一个重要的问题:内存中有多少个对象要被删除?答案决定了将有多少个析构函数会被调用。
这个问题简单来说就是:要被删除的指针指向的是单个对象呢,还是对象数组?这只有你来告诉delete。如果你在用delete时没用括号,delete就会认为指向的是单个对象,否则,它就会认为指向的是一个数组:
string *stringptr1 = new string;
string *stringptr2 = new string[100];
...
delete stringptr1;// 删除一个对象
delete [] stringptr2;// 删除对象数组
如果你在stringptr1前加了"[]"会怎样呢?答案是:那将是不可预测的;
如果你没在stringptr2前没加上"[]"又会怎样呢?答案也是:不可预测。
int这样的固定类型来说,结果也是不可预测的,即使这样的类型没有析构函数。所以,解决这类问题的规则很简单:如果你调用new时用了[],调用delete时也要用[]。如果调用new时没有用[],那调用delete时也不要用[]。
#include <iostream> using namespace std; struct foo { ~foo(){}; //去掉后,就不会记录个数了. }; int main(int argc,char *argv[]) { foo* f = new foo; delete f; f=0; delete f; foo* fa = new foo[8]; printf("%u\n", *((char *)fa - 4)); //输出8:辅助析构函数. /* 对于有显式析构函数的对象的数组, 编译器会在数组前分配4个字节储存数组元素的个数 (也就是需要调用析构函数的次数), 因为必须知道数组实际的元素合个数, delete[]才能知道需要调用几次析构函数。 */ delete fa; //有的编译器这里会有异常. //用delete而不是delete[]释放用new[]分配的空间这种行为是undefined的, //也就是由编译器实现所决定的 fa = 0; delete fa; return 0; }
发表评论
-
C语言中的不定参数
2010-12-15 12:28 130371,最近刚刚知道C语言还有不定参数这么个东东。 2,解决方法 ... -
剖析浮点数
2010-12-10 10:39 1124[转载]http://blog.csdn.net/masefe ... -
存在虚基类时,类对象的大小
2010-12-09 19:38 11691, 实例代码: #include <iostrea ... -
关于父类的构造的顺序
2010-12-09 19:33 6181,没有虚基类的情况: #include <iost ... -
关于empty class的大小
2010-12-09 19:32 10751,首先简要说明下为什么empty class的大小不是0? ... -
JVM实现机制及内部基本概念
2010-12-09 19:27 818兴趣所致,搜了一些关于JVM的简单介绍,算是扫盲吧。 1.J ... -
指向对象成员变量的指针。
2010-12-09 19:18 11051,工作定了后,把《Inside the C++ object ... -
面向对象的一些常见问题总结。
2010-12-09 19:08 8771,构造函数中调用虚函数的问题。 注:一般编译器都会在执行完b ... -
关于C++的名字查找规则
2010-12-09 19:05 13261,总结一句话:由内向外,找到合适的即刻停止。 实例代码: ... -
继承下的名字查找规则,最近作用域
2010-11-06 20:04 8501, #include <iostream> ... -
虚基类和多重继承总结
2010-11-06 19:54 8261,虚基类,其构造函数总是在最后一个层次最先并且真正被执行。 ... -
为什么构造函数,不能是virtual?
2010-09-25 15:24 16391,C++语言是静态语言,而把构造函数写成虚函数意味这可以动态 ... -
大小端对应字节, 注意和一个字节内位序的区分
2010-09-09 10:11 13801,一个例子: #include <stdio.h& ... -
实现:不能被继承的类
2010-08-31 17:35 8201,首先想到的是在C++ 中 ... -
提升为N的倍数(N为2的指数次)
2010-07-18 10:16 7321,可以这么做: int num=15; num = (num ... -
C++ 中:new的用法
2010-07-17 16:26 1866先放这儿,有时间了,在 ... -
句柄的使用实例
2010-06-02 11:24 11311,这个实例采用了使用计数策略. 2,实例代码: #in ... -
关于类的一些语法:
2010-06-02 09:26 677所谓"书越读越薄",这话说得真是有道理. ... -
sizeof(string对象)的大小
2010-06-01 11:32 15071,无语了,string对象的大小固定就是4啊. 2,实例代码 ... -
C语言中不同增值语句的区别
2010-05-12 12:50 6351, mango[i++]+=y; 被当作 mango[i]= ...
相关推荐
2. 动态分配的数组可以在自由存储区中创建和释放。 3. 动态数组可以使用 new 和 delete 表达式来实现。 动态数组的定义: 动态数组的定义需要指定类型和数组长度,但不需要为数组对象命名。例如: ```cpp int *pia...
对象数组初始化和 Student 类设计 在软件开发中,对象数组初始化是一种常见的编程技术,特别是在设计学生信息管理系统时。在本文中,我们将讨论如何设计 Student 类,实现对象数组的初始化和成员函数的使用。 ...
一、new和delete创建和释放动态数组:数组类型的变量有三个重要的限制:数组长度固定,在编译时必须知道其长度,数组只在定义它的语句内存在。动态数组:长度固定,编译时不必知道其长度,通常是运行时确定;一直...
* 使用delete关键字释放对象数组占用内存:delete []指针名 * 注意:new和delete必须成对出现,且delete释放对象时,会调用析构函数。 引用数据类型 * 引用数据类型的定义:别名 = 对象名 * 示例:int a = 56; int...
* 使用delete[]关键字释放对象数组占用的内存,例如:delete [] 指针名; * 注意:new和delete必须成对出现,且delete释放对象时,会调用析构函数。 二、引用 * 引用是一种别名,例如:int a = 56; int &b = a; 则b...
C++内存管理与数组 C++是一种静态类型、编译型、通用目的的高级语言,支持面向对象编程、泛型编程等多种编程范式。C++语言的内存管理机制是理解C++语言的关键所在,本文将详细介绍C++语言的内存管理机制和数组的...
我们还使用delete操作符来释放该对象。 我们还使用了malloc和free函数来分配和释放内存。malloc函数会分配一块内存,但是不会调用类的构造函数,而free函数会释放内存,但是也不会调用类的析构函数。 5.结论 在这...
统计记录:是丢失的内存跟踪:是构造函数:是的析构函数:是处理指向基元的指针:是处理指向对象的指针:是处理指向对象静态数组的指针:是处理指向动态数组的指针:是在没有外部引用的情况下检测并释放任意图:...
C++学习笔记:关于C++类与动态内存分配中布局new操作符 ...我们了解到,布局new操作符用于在现有的内存块上创建对象,需要使用delete[]操作符来释放内存,并且需要显式地调用析构函数以释放对象占用的资源。
"new与delete和malloc与free之间的关系与区别" 在C++编程语言中,new和delete是C++的关键字,是一种操作符,可以被重载,而malloc和free是C语言的库函数,并且不能重载。这两对函数都是用于内存管理的,但是它们...
释放对象数组时,需要使用delete[]关键字来释放所有对象。 4.4 拷贝构造函数 拷贝构造函数是一种特殊的构造函数,具有一般构造函数的特点,其作用是用一个已经存在的对象去初始化一个新的同类对象。拷贝构造函数...
* 实验任务6_6:声明包含5个元素的对象数组,每个元素都是`Employee`类型的对象。 * 实验任务6_7:修改`people`类,具有姓名、编号、性别、生日和身份证号等属性,并实现人员信息的录入和显示。
2.系统资源泄露(Resource Leak) 3.没有将基类的析构函数定义为虚函数 4.在释放对象数组时没有使delete[]是使了delete 5.缺少拷构
2、每一个New都有一个与之对应的delete进行释放。 3、new 和malloc的区别在于new不但分配了内存还同时创建对象,而malloc只负责分配内存。 4、直接声明数组和声明的new int[5];区别只在于他们存在的位置。...
* new 运算从堆中分配一块与<类型>相适应的大小为<元素个数>的数组存储空间 * 使用格式为:<指针变量名>=new <类型>[<元素个数>]; * delete 运算符用来释放<指针变量名>指向的动态存储空间 * 使用格式如下:delete ...
在编程中,可能会遇到一些问题,如动态分配失败、指针删除与堆空间释放、内存泄漏和重复释放、动态分配的变量或对象的生命期等。这些问题都需要程序员小心处理,以免出现严重的错误。 函数指针是一种特殊的指针,它...
《C++面向对象程序设计》谭浩强版复习总结.pdf 本资源总结了C++程序设计的基础知识和面向对象程序设计的基本概念。下面是详细的知识点总结: ...2. delete运算符:释放<指针变量名>指向的动态存储空间
=============下面先给出一个new和delete基本应用的例子,回顾一下它的基本用法============ 代码如下:#include<iostream>using ...//开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址 int i; for(i=0
delete:指向⼀个动态独享的指针,销毁对象,并释放与之关联的内存。 使⽤堆内存是⾮常频繁的操作,容易造成堆内存泄露、⼆次释放等问题,为了更加容易和更加安全的使⽤动态内存,C++11中引⼊了智 能指针的概念,⽅...
delete和delete[]的主要区别在于它们对数组对象的处理方式。delete只会调用一次析构函数,而delete[]会调用每一个数组元素的析构函数。 三、C和C++的共同点和不同之处 C语言是一种面向过程的语言,而C++是一种面向...