没有合适的资源?快使用搜索试试~ 我知道了~
首页C++内存管理详解,new,delete的合理使用
C++内存管理详解,new,delete的合理使用
5星 · 超过95%的资源 需积分: 20 70 下载量 100 浏览量
更新于2023-03-03
评论 1
收藏 115KB DOC 举报
对应的new和delete要采用相同的形式 下面的语句有什么错? string *stringarray = new string[100]; ... delete stringarray; 一切好象都井然有序——一个new对应着一个delete——然而却隐藏着很大的错误:程序的运行情况将是不可猜测的。至少,stringarray指向的100个string对象中的99个不会被正确地摧毁,因为他们的析构函数永远不会被调用。
资源详情
资源评论
资源推荐
C++内存管理详解
资料引用:http://www.knowsky.com/395404.html
1.对应的 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 时也不要用[]。
在写一个包含指针数据成员,并且提供多个构造函数的类时,牢记这一规则尤其重要。因
为这样的话,你就必须在所有初始化指针成员的构造函数里采用相同的 new 的形式。否则,
析构函数里将采用什么形式的 delete 呢?关于这一话题的进一步阐述,参见条款 11。
这个规则对喜欢用 typedef 的人来说也很重要,因为写 typedef 的程序员必须告诉别人,用
new 创建了一个 typedef 定义的类型的对象后,该用什么形式的 delete 来删除。举例如下:
typedef string addresslines[4]; //一个人的地址,共 4 行,每行一个 string
//因为 addresslines 是个数组,使用 new:
string *pal = new addresslines; // 注重"new addresslines"返回 string*, 和
// "new string[4]"返回的一样
delete 时必须以数组形式与之对应:
delete pal;// 错误!
delete [] pal;// 正确
为了避免混乱,最好杜绝对数组类型用 typedefs。这其实很轻易,因为标准 c++库(见条款
49)包含有 stirng 和 vector 模板,使用他们将会使对数组的需求减少到几乎零。举例来说,
addresslines 可以定义为一个字符串(string)的向量(vector),即 addresslines 可定义为 vector 类
型。
2.析构函数里对指针成员调用 delete
大多数情况下,执行动态内存分配的的类都在构造函数里用 new 分配内存,然后在析构函
数里用 delete 释放内存。最初写这个类的时候当然不难做,你会记得最后对在所有构造函
数里分配了内存的所有成员使用 delete。
然而,这个类经过维护、升级后,情况就会变得困难了,因为对类的代码进行修改的程序
员不一定就是最早写这个类的人。而增加一个指针成员意味着几乎都要进行下面的工作:
·在每个构造函数里对指针进行初始化。对于一些构造函数,假如没有内存要分配给指针的
话,指针要被初始化为 0(即空指针)。
·删除现有的内存,通过赋值操作符分配给指针新的内存。
·在析构函数里删除指针。
假如在构造函数里忘了初始化某个指针,或者在赋值操作的过程中忘了处理它,问题会出
现得很快,很明显,所以在实践中这两个问题不会那么折磨你。但是,假如在析构函数里
没有删除指针,它不会表现出很明显的外部症状。相反,它可能只是表现为一点微小的内
存泄露,并且不断增长,最后吞噬了你的地址空间,导致程序夭折。因为这种情况经常不
那么引人注重,所以每增加一个指针成员到类里时一定要记清楚。
另外,删除空指针是安全的(因为它什么也没做)。所以,在写构造函数,赋值操作符,或
其他成员函数时,类的每个指针成员要么指向有效的内存,要么就指向空,那在你的析构
函数里你就可以只用简单地 delete 掉他们,而不用担心他们是不是被 new 过。
当然对本条款的使用也不要绝对。例如,你当然不会用 delete 去删除一个没有用 new 来初
始化的指针,而且,就象用智能指针对象时不用劳你去删除一样,你也永远不会去删除一
个传递给你的指针。换句话说,除非类成员最初用了 new,否则是不用在析构函数里用
delete 的。
说到智能指针,这里介绍一种避免必须删除指针成员的方法,即把这些成员用智能指针对
象来代替,比如 c++标准库里的 auto_ptr。想知道它是如何工作的,看看条款 m9 和 m10。
3.预先预备好内存不够的情况
operator new 在无法完成内存分配请求时会抛出异常(以前的做法一般是返回 0,一些旧
一点的编译器还这么做。你愿意的话也可以把你的编译器设置成这样。关于这个话题我将
推迟到本条款的结尾处讨论)。大家都知道,处理内存不够所产生的异常真可以算得上是个
道德上的行为,但实际做起来又会象刀架在脖子上那样痛苦。所以,你有时会不去管它,
也许一直没去管它。但你心里一定还是深深地隐藏着一种罪恶感:万一 new 真的产生了异
常怎么办?
你会很自然地想到处理这种情况的一种方法,即回到以前的老路上去,使用预处理。例如
c 的一种常用的做法是,定义一个类型无关的宏来分配内存并检查分配是否成功。对于 c++
来说,这个宏看起来可能象这样:
#define new(ptr, type)
try { (ptr) = new type; }
catch (std::bad_alloc&) { assert(0); }
(“慢! std::bad_alloc 是做什么的?”你会问。bad_alloc 是 operator new 不能满足内存分配
请求时抛出的异常类型,std 是 bad_alloc 所在的名字空间(见条款 28)的名称。“好!”你会继
续问,“assert 又有什么用?”假如你看看标准 c 头文件(或与它相等价的用到了名字空间的版
本,见条款 49),就会发现 assert 是个宏。这个宏检查传给它的表达式是否非零,假如不是
非零值,就会发出一条出错信息并调用 abort。assert 只是在没定义标准宏 ndebug 的时候,
即在调试状态下才这么做。在产品发布状态下,即定义了 ndebug 的时候,assert 什么也不
做,相当于一条空语句。所以你只能在调试时才能检查断言(assertion))。
new 宏不但有着上面所说的通病,即用 assert 去检查可能发生在已发布程序里的状态(然而
任何时候都可能发生内存不够的情况),同时,它还在 c ++里有另外一个缺陷:它没有考虑
到 new 有各种各样的使用方式。例如,想创建类型 t 对象,一般有三种常见的语法形式,
你必须对每种形式可能产生的异常都要进行处理:
new t;
new t(constrUCtor arguments);
new t[size];
这里对问题大大进行了简化,因为有人还会自定义(重载)operator new,所以程序里会包含
任意个使用 new 的语法形式。
那么,怎么办?假如想用一个很简单的出错处理方法,可以这么做:当内存分配请求不能
满足时,调用你预先指定的一个出错处理函数。这个方法基于一个常规,即当 operator new
不能满足请求时,会在抛出异常之前调用客户指定的一个出错处理函数——一般称为 new-
handler 函数。(operator new 实际工作起来要复杂一些,详见条款 8)
指定出错处理函数时要用到 set_new_handler 函数,它在头文件里大致是象下面这样定义的:
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
可以看到,new_handler 是一个自定义的函数指针类型,它指向一个没有输入参数也没有返
回值的函数。set_new_handler 则是一个输入并返回 new_handler 类型的函数。
set_new_handler 的输入参数是 operator new 分配内存失败时要调用的出错处理函数的指针,
返回值是 set_new_handler 没调用之前就已经在起作用的旧的出错处理函数的指针。
可以象下面这样使用 set_new_handler:
// function to call if operator new can't allocate enough memory
void nomorememory()
{
cerr << "unable to satisfy request for memory
";
abort();
}
int main()
{
set_new_handler(nomorememory);
int *pbigdataarray = new int[100000000];
...
}
假如 operator new 不能为 100,000,000 个整数分配空间,nomorememory 将会被调用,程序
发出一条出错信息后终止。这就比简单地让系统内核产生错误信息来结束程序要好。(顺便
考虑一下,假如 cerr 在写错误信息的过程中要动态分配内存,那将会发生什么...)
operator new 不能满足内存分配请求时,new-handler 函数不只调用一次,而是不断重复,
直至找到足够的内存。实现重复调用的代码在条款 8 里可以看到,这里我用描述性的的语
言来说明:一个设计得好的 new-handler 函数必须实现下面功能中的一种。
·产生更多的可用内存。这将使 operator new 下一次分配内存的尝试有可能获得成功。实施
这一策略的一个方法是:在程序启动时分配一个大的内存块,然后在第一次调用 new-
handler 时释放。释放时伴随着一些对用户的警告信息,如内存数量太少,下次请求可能会
失败,除非又有更多的可用空间。
·安装另一个不同的 new-handler 函数。假如当前的 new-handler 函数不能产生更多的可用内
存,可能它会知道另一个 new- handler 函数可以提供更多的资源。这样的话,当前的 new-
handler 可以安装另一个 new-handler 来取代它(通过调用 set_new_handler)。下一次 operator
new 调用 new-handler 时,会使用最近安装的那个。(这一策略的另一个变通办法是让 new-
handler 可以改变它自己的运行行为,那么下次调用时,它将做不同的事。方法是使 new-
handler 可以修改那些影响它自身行为的静态或全局数据。)
·卸除 new-handler。也就是传递空指针给 set_new_handler。没有安装 new-handler,operator
new 分配内存不成功时就会抛出一个标准的 std::bad_alloc 类型的异常。
·抛出 std::bad_alloc 或从 std::bad_alloc 继续的其他类型的异常。这样的异常不会被 operator
new 捕捉,所以它们会被送到最初进行内存请求的地方。(抛出别的不同类型的异常会违反
operator new 异常规范。规范中的缺省行为是调用 abort,所以 new-handler 要抛出一个异常
时,一定要确信它是从 std::bad_alloc 继续来的。想更多地了解异常规范,参见条款 m14。)
·没有返回。典型做法是调用 abort 或 exit。abort/exit 可以在标准 c 库中找到(还有标准 c++库,
参见条款 49)。
上面的选择给了你实现 new-handler 函数极大的灵活性。
处理内存分配失败的情况时采取什么方法,取决于要分配的对象的类:
class x {
public:
static void
outofmemory();
...
};
class y {
public:
static void outofmemory();
...
};
x* p1 = new x; // 若分配成功,调用 x::outofmemory
y* p2 = new y; // 若分配不成功,调用 y::outofmemory
c++不支持专门针对于类的 new-handler 函数,而且也不需要。你可以自己来实现它,只要
在每个类中提供自己版本的 set_new_handler 和 operator new。类的 set_new_handler 可以为
类指定 new-handler(就象标准的 set_new_handler 指定全局 new-handler 一样)。类的 operator
new 则保证为类的对象分配内存时用类的 new-handler 取代全局 new-handler。
假设处理类 x 内存分配失败的情况。因为 operator new 对类型 x 的对象分配内存失败时,每
次都必须调用出错处理函数,所以要在类里声明一个 new_handler 类型的静态成员。那么
类 x 看起来会象这样:
剩余21页未读,继续阅读
coyida
- 粉丝: 1
- 资源: 3
上传资源 快速赚钱
- 我的内容管理 收起
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
会员权益专享
最新资源
- 2023年中国辣条食品行业创新及消费需求洞察报告.pptx
- 2023年半导体行业20强品牌.pptx
- 2023年全球电力行业评论.pptx
- 2023年全球网络安全现状-劳动力资源和网络运营的全球发展新态势.pptx
- 毕业设计-基于单片机的液体密度检测系统设计.doc
- 家用清扫机器人设计.doc
- 基于VB+数据库SQL的教师信息管理系统设计与实现 计算机专业设计范文模板参考资料.pdf
- 官塘驿林场林防火(资源监管)“空天地人”四位一体监测系统方案.doc
- 基于专利语义表征的技术预见方法及其应用.docx
- 浅谈电子商务的现状及发展趋势学习总结.doc
- 基于单片机的智能仓库温湿度控制系统 (2).pdf
- 基于SSM框架知识产权管理系统 (2).pdf
- 9年终工作总结新年计划PPT模板.pptx
- Hytera海能达CH04L01 说明书.pdf
- 数据中心运维操作标准及流程.pdf
- 报告模板 -成本分析与报告培训之三.pptx
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论1