ApH
ApH
知名人士
知名人士
  • 铜币0枚
  • 威望0点
  • 贡献值0点
阅读:2282回复:11

恐怖C++之四

楼主#
更多 发布于:2004-07-16 14:57
New, Delete. "Just this !?".

上次讨论了一下 malloc、free 和 heap 的操作,这次翻翻箱底,把 new 和 delete 也来个小节。

“new 和 delete 有什么好总结的? new 完了记得 delete 就好了!”

首先看最简单的操作,在堆中初始化一个内建类型(primary type)数据:


int *p = new int();
cout << "*p is " << *p << endl;
delete p;


猜猜输出是多少,我就没猜对,在 VC6 上得到的结果是:“*p is -842150451”,看来编译器内部生成的 default constructor 并没有初始化数据呀!(细节见恐怖C++之二)

当然你也可以给 int() 传递一个参数,类似 new int(517)。

然后稍复杂一点,在堆中初始化一个自定义类型数据:


class Point {
public: int x, y;
};
Point *pj = new Point();
cout << "Point is (" << x << ", " << y << ")" << endl;
delete pj;


结果是:“Point is (-842150451, -842150451)”,两个数据成员都没有被初始化,不过这都是构造函数的问题,我只是想说:“别以为 new 调用了构造函数,数据就一定会被初始化!”

new 和 malloc 的最大不同就是 new 在从堆中分配完内存后,还会在指定的内存上调用构造函数,编译器为每个new表达式提供类似如下码:


void *p = ::operator new(sizeof(Point));
Point::Point(p, other_para...);
return Point::operator &(p);


new 首先调用 ::operator new(int size) 函数从堆中分配内存,然后调用类的某个构造函数,将所得到的内存地址作为第一个参数传入(实际上的 this 指针),然后调用 operator &() 得到对象的有效地址。

如果用户自定义了某个类的 operator new() 操作符,编译器就会用自定义的操作符来替代上面的全局操作符 ::operator new(),在本例中为: void *p = Point::operator new(sizeof(Point))。另外,编译器默认提供的 ::operator new() 就相当于 malloc,给定尺寸、分配内存,而 ::operator delete() 也相当于 free。

我们还可以通过显示的调用特定的 new 操作来选择 operator new()。如:“Point *pj = ::new Point()”,这时即使用户定义了 Point::operator new(int size) 也不会被调用,因为我们显示的选择了全局的 ::operator new(int size)。

对于每一个类,编译器都会自动为其生成一个 operator &(),用来进行取地址运算。

对于 delete 也有类似的伪码:


Point::~Point(ptr);
::operator delete(ptr);


除了 new 和 delete 之外还有一套 new[] 和 delete[] 用来对数组进行操作,只要不将两种东东混用就可以了。其实我们根本不可能将 new 用于数组的分配(我又在断言了,呵呵),然而 new[] 用于单个对象的情况还是有的,很容易写出下面这样的代码:


Point *p = new Point[1];  // och!
delete p;


对于这样的代码,即便在所有现有的编译器上测试都能够通过,我也宁可认为这是一个因编译器而异的、未定义行为的代码。

new 和 new[] 调用的操作符函数也不相同,new[] 调用的是 operator new[](int size)。

出错的总是 delete 与 new 的配合问题,在写每一个 delete 时我们都应该仔细的查看相应的 new 操作,delete 与 delete[] 的区别并不在于其释放的内存大小(还记得恐怖C++之三中 free 的运行方法么),而在于他们调用的析构函数的数量。delete 只会调用一个析构函数,而 delete[] 会对有效地址空间(待释放地址空间)内的所有对象调用析构函数。

试想如果析构函数中带有 File Close、Memory Release 或者 Object Counting 等代码,那么错误的将 delete 用作 delete[] 将导致的后果。恐怖!


回头看看上面写过的所有的代码,有没有发现什么问题,哈哈。

new 操作并不总是会成功的,至少在超过 4G 地址空间的时候会出错,所以必须要进行错误检测,像这样:


Point *p = new Point();
if (p == 0) {
    error_exit();
}
...
delete p;


可是,这样的代码也不正确,虽然这是我们最常见的。这样的代码在VC6中运行良好,可是却不是标准的。作为标准的拥护者,我们都希望自己的所有代码都符合标准,而不是随编译器的不同而有不同表现。

在标准 C++ 中 new 操作失败时会抛出异常 std::bad_alloc,而不保证返回 0。所以上面的检测代码在一些编译器上可能不起任何作用,为了支持这种返回 0 的检测方式标准 C++ 还提供了一个非异常方式的 new 操作:


Point *p = new (nothrow) Point();
if (p==0) {
...


nothrow 并不是关键字,事实上是一个 std::nothrow_t 类型的全局对象,new 表达式会调用 operator new(int size, &nothrow_t) 操作符函数,此函数不抛出任何异常,而是以返回 0 作为出错标识。

所以正确的检测方法应该是:


Point *p = new (nothrow) Point();
if (p==0) {
...
// 或者
try {
    Point *p = new Point();
}
catch(std::bad_alloc &) {
...


除此之外,C++ 中还有一个 new_handle 机制来对内存分配的错误进行处理,用户可以通过 set_new_handle() 函数向系统注册一个 new_handle 处理程序,当内存分配发生异常时这个函数就会被调用。准确说是循环调用,直到获得有效内存。这时上面这些异常、返回 0 之类都不会起作用!

new_handle 可以用在自定义的内存池上,用来处理类似 GC 机制。

对 new 和 delete 的小节基本到此,但还差一些东西,如:编写自定义的 operator new,以及 placement new 操作,只能下次再说了。
下网卡
知名人士
知名人士
  • 铜币1枚
  • 威望1点
  • 贡献值0点
1C#
发布于:2004-07-16 18:04
Re:恐怖C++之四
抢二楼!!
APH我画了一个星期的电路板终于画好了, 能帮我看看吗? 我可能有一些线间距小于20mil, 你们那里的做板工艺支持吗?
回复我, -------------------- 我的签名不占地方吧?
特别想念kmwang, APH, momo, 松松,旺旺
闲得没事
普通会员
普通会员
  • 铜币1枚
  • 威望0点
  • 贡献值0点
2C#
发布于:2004-07-16 22:06
Re:恐怖C++之四
[em050]
看来楼主对 c++很熟息
我做你的徒弟吧!
一直想认真的学习C++但是每回都是半途而废[em050][em061]因为有时候程序编译的时候很多错误,自己根本无法解决掉
当然,这是我为自己想出来的借口。
希望能得到你的帮助
0000
作家
作家
  • 铜币143枚
  • 威望34点
  • 贡献值1点
3C#
发布于:2004-07-17 09:06
Re:恐怖C++之四

结果是:“Point is (-842150451, -842150451)”,两个数据成员都没有被初始化,不过这都是构造函数的问题,我只是想说:“别以为 new 调用了构造函数,数据就一定会被初始化!”

我能看懂的地方差不多就到这里了,有点不明白的是:构造函数根本就没有初始化数据,难道构造函数会把变量初始化为变量的数据类型的默认值吗?不是很清楚 -------------------- [em074][em074][em074][em074][em074][em074][em074][em074][em074][em074][em074][em074][em074]
[em074]趴在楼下的背上睡得呼呼的Zzzzzz............[em074]
[em074][em074][em074][em074][em074][em074][em074][em074][em074][em074][em074][em074][em074]

[a=http://home.itdrp.com/go2west/]感情欠费中
           爱情停机中
                       工作寻找中[/a]
.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/
kmwang
小有名气
小有名气
  • 铜币0枚
  • 威望0点
  • 贡献值0点
4C#
发布于:2004-07-22 23:40
Re:恐怖C++之四
只有VC在Debug时会将其初始化为0xcdcdcdcd,谁听过我的课还记得的来补充一下,MS为什么选这个值.
mumu
写手
写手
  • 铜币0枚
  • 威望0点
  • 贡献值0点
5C#
发布于:2004-07-23 09:19
Re:恐怖C++之四
0xcdcdcdcd 永远不是一个可用的内存地址。
_nh_malloc_dbg这样做是为了更方便地捕捉错误。
王小波说:“中年妇女在中国是一种自然灾害,这倒不是因为她们不好看,而是因为她们故意要恶心人。” 一天,我乘坐公交车,一位MM突然转过头来对我说:“你帅吗?”我说:“我不帅!”MM突然给我一巴掌,并说:“我最讨厌说谎的人了!” 如果你更热爱金钱而非自由,更习惯于被奴役的安宁而畏惧令人充满活力的争取自由的抗争,那么,请你静静地走开。我们不会乞求你的建议或是帮助。伏下身去讨好那喂养你的人吧。但愿身上的锁链不会给你造成太多的痛苦,但愿未来的人们不会记起你曾经是我们的国人 Samuel Adams: 18世纪美国独立革命重要领袖,著有“殖民者的权利”
mumu
写手
写手
  • 铜币0枚
  • 威望0点
  • 贡献值0点
6C#
发布于:2004-07-23 09:23
Re:恐怖C++之四
_heap_alloc_dbg initializes each byte of the allocated block to 0xCD. It also initializes the "no-man's land"—extra bytes that belong to the internal block allocated, but not the block you requested—with 0xFD. (The internal block can be larger than what you requested.) Similarly, the debug version of free called from ::delete initializes freed bytes with yet another special value, 0xDD. Figure 1 is a snippet from the C runtime source file dbgheap.c that explains why Microsoft chose these particular values.

Figure 1   dbgheap.c Fragment

/*
 * The following values are non-zero, constant, odd, large, and atypical
 *      Non-zero values help find bugs assuming zero filled data.
 *      Constant values are good so that memory filling is deterministic
 *          (to help make bugs reproducible).  Of course it is bad if
 *          the constant filling of weird values masks a bug.
 *      Mathematically odd numbers are good for finding bugs assuming a cleared
 *          lower bit, as well as useful for trapping on the Mac.
 *      Large numbers (byte values at least) are less typical, and are good
 *          at finding bad addresses.
 *      Atypical values (i.e. not too often) are good since they typically
 *          cause early detection in code.
 *      For the case of no-man's land and free blocks, if you store to any
 *          of these locations, the memory integrity checker will detect it.
 */

static unsigned char _bNoMansLandFill = 0xFD;   /* fill no-man's land w/this */
static unsigned char _bDeadLandFill   = 0xDD;   /* fill free objects w/this */
static unsigned char _bCleanLandFill  = 0xCD;   /* fill new objects w/this */

王小波说:“中年妇女在中国是一种自然灾害,这倒不是因为她们不好看,而是因为她们故意要恶心人。” 一天,我乘坐公交车,一位MM突然转过头来对我说:“你帅吗?”我说:“我不帅!”MM突然给我一巴掌,并说:“我最讨厌说谎的人了!” 如果你更热爱金钱而非自由,更习惯于被奴役的安宁而畏惧令人充满活力的争取自由的抗争,那么,请你静静地走开。我们不会乞求你的建议或是帮助。伏下身去讨好那喂养你的人吧。但愿身上的锁链不会给你造成太多的痛苦,但愿未来的人们不会记起你曾经是我们的国人 Samuel Adams: 18世纪美国独立革命重要领袖,著有“殖民者的权利”
southeast
著名写手
著名写手
  • 铜币0枚
  • 威望0点
  • 贡献值0点
7C#
发布于:2004-07-23 16:52
Re:恐怖C++之四
c++
不只是恐怖,一听到简直就是恐惧阿。非常PF学c的,高深莫测的样子。
还是java简单阿,构造函数会自动初始化。 -------------------- 好好吃饭,天天睡觉
努力赚钱,娶个老婆
[img]http://www.wait4c.com/bbs/userfiles/2196/2005051614543457820.jpg[/img]
0000
作家
作家
  • 铜币143枚
  • 威望34点
  • 贡献值1点
8C#
发布于:2007-03-18 13:51
Re:恐怖C++之四
0xcdcdcdcd 永远不是一个可用的内存地址。
_nh_malloc_dbg这样做是为了更方便地捕捉错误。

为什么不是 0x0000 ....

----

好像还差几个 0x00000000 [ 2007-03-18 13:52:21 0000 修改 ]
.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/
KxjIron
普通会员
普通会员
  • 铜币0枚
  • 威望0点
  • 贡献值1点
9C#
发布于:2007-04-24 20:10
Re:恐怖C++之四
0xcd == { int 3; }
并且是个负数
是不是这个原因,老牛?
I like running
zhaoshi
写手
写手
  • 铜币44枚
  • 威望10点
  • 贡献值1点
  • 社区居民
10C#
发布于:2007-04-24 21:03
Re:恐怖C++之四
欢迎来扫盲!!!


Foolish,stupid,are you out of your mind? 站得更高,尿得更远~
sigepluto
小有名气
小有名气
  • 铜币0枚
  • 威望0点
  • 贡献值0点
11C#
发布于:2007-05-24 09:29
Re:恐怖C++之四
引用:

class Point {
public: int x, y;
};
Point *pj = new Point();
cout << "Point is (" << x << ", " << y << ")" << endl;
delete pj;
 


结果是:“Point is (-842150451, -842150451)”,两个数据成员都没有被初始化,不过这都是构造函数的问题,我只是想说:“别以为 new 调用了构造函数,数据就一定会被初始化!”

这是很正常的,new的调用,会引发构造函数,但如果构造函数为空,类成员肯定不会被初始化。只是被声明。
http://spaces.msn.com/sigepluto/ If you love something, set it free. If it comes back, it is yours. If it doesn't, it never was.
游客

返回顶部