C_and_CPP 版 (精华区)

发信人: seaboy (浪小), 信区: C_and_CPP
标  题: Solmyr和Zero的故事 —— 内存,最后一块
发信站: 哈工大紫丁香 (2003年08月30日12:19:54 星期六), 站内信件

 “Shit!又死机了。我已经在这平台上工作了一个月了。可死机的次数比我在这个月里被
女孩甩的次数还多。为什么?还不是这该死的平台,为什么掌上系统的内存就和愿意和我
说话的女孩一样少?”Solmyr抱怨道。 
 

“兄弟,怎么了。”Zero问。 

 

      “Zero是我们这组的主程序员,他懂得很多,人长的也帅,很讨女孩子的欢心。甚
至连美工组的Lili(长得比孙燕姿还好看)也暗恋他。”。Solmyr一边想,一边说,“老
大,你看,又死机了。为什么我每次用动态内存超过10次,就crash了呢?” 

 

“我知道你原来是Java程序员,也许到现在,你还念念不忘那GC (垃圾收集机制,我说还
不如叫上帝也哭泣-God  Cry),可你要知道,你现在是在用C++编程,重要的是效率。” 

 

      “效率,我……”忽然,Solmyr觉得嘴角边似乎有液体流过。那是Solmyr的口水。
每次提到效率,Solmyr总要流口水,就像看到漂亮MM,Solmyr要喷鼻血一样。 

 

      “C++中,关于动态内存的是new and delete。” 

 

“我知道,”Solmyr急于表现自己,想证明自己对C++并不是一无所知,“我正在读Scott 
Meyer的More Effective C++。在C++中,new operator是C++内建的行为。任何人(也许除
了Bjarne Stroustrup)都无法改变。new operator先调用一个名为operator new的函数动
态申请内存。标准模式就像这样: 

 

      void* operator new(size_t size); 

      然后在传回的void*指针上进行构造的行为。而delete operator则先析构对象,然
后调用名为operator delete的函数,标准形式就像这样: 

      void operator delete(void* pToDeAlloc) 

// GotW中说即使在指针参数后加上size_t size,仍然是标准形式。

// size_t size的作用是检查所要卸除的内存是否是期望的大小,如果在类层次中定义的
话,只要基类是virtual destructor,那么size可以// 确保是正确大小。

而placement new……。” 

 

      “唔,说得不错,有进步。关于placement new/delete,先知Meyer[1]有详细的论
述。原来的placement new仅仅是这样: 

      void* operator new(size_t, void* pMem) { return pMem; } 

‘ 随着时间过去,任何‘要求额外引数’的 operator new 版本,也都渐渐采用 
placement new 这个术语。事实上这个术语已经被铭记於 C++ 标准规格中。因此,当 
C++ 程序员谈到所谓的 placement new 函数,他们所谈的可能是上述那个‘需要额外一个
 void* 参数,用以指出物件置于何处’的版本,但也可能是指那些‘所需参数比单一而必
要之 size_t 引数更多’的任何 operator new 版本,包括上述函数,也包括其他‘参数
更多’的 operator new 函数。’——引自[1]。 

 

现在我们来考虑一个问题,如果在operator new结束申请内存后,构造函数抛出了异常,
那么已经申请的内存谁来回收,答案当然是编译器。因为整个new operator还未结束。所
以你无法获得控制权。如果你对operator new/delete进行了重载,那么编译器会调用那个
operator  delete呢?由于不同的operator new可能通过不同的方法获得内存,而让不知
道怎样分配的operator delete去释放内存显然是不负责任的。所以编译器假定哪个operat
or delete与operator new有相同的参数(除了size_t size和void* pDeAlloc),那么那
个operator delete便知道operator new怎样获得内存。构造函数抛出异常后,也会调用与
operator new相同参数的operator delete来释放内存。”

 

      “那么,我该怎么解决现在的问题呢?” 

“别那么急,已经下午五点了。该回家了,别让人以为程序员是夜游神。回家看看Effecti
ve C++第二部分和GotW9, GotW10。明天再说吧。” 

 

第二天一早,Solmyr啃着大饼走进了办公室,看到Guru Zero早已姿势优雅地坐在电脑前收
发Email,不禁自惭形秽,连忙放下手中的大饼,跑去和Zero说:“老大,昨天晚上,我挑
灯夜读,总结出两点:

1.     如果你写了一个operator new,请对应写一个operator delete。

2.     不要delete不是自己new来的内存。

“嗯,不错,怎么我看上去,就象是Effective C++中的条款呢?你有没有自己想过关于pl
acement delete的问题?”

“placement delete有什么问题吗?”Solmyr一脸茫然的问道。

“你有没有试过把一块用placement new申请得到的内存用placement delete卸除掉呢?不
妨你现在试试看。”

 

只见Solmyr跑到一台电脑前,两手如飞在键盘上敲击,可是我们能听到的只有他的唉声叹
气和编译器的哇哇大叫。Solmyr实在是没办法,只能向Zero求教。Zero喝了一口咖啡,说
道:“我们平时写的那些要求额外参数的operator delete只有在构造函数抛出异常时,才
会被编译器自动调用,而我们是不可能手工调用到任何带有额外参数的delete的,为什么
没有一个内建的‘placement delete’来与‘placement new’相匹配的原因在于没有办法
保证它被正确使用,在C++类型系统中,无法推断一个指针从哪里获得它指向的内存,可能
是指向heap,也可能是stack。所以……”

 

“所以当我们确实知道一个指针它是怎么获得它所指向的内存时,我们可以这样:

template<class T> void destroy(T* p, Arena& a)

        {

                if (p) {

                        p->~T();      // explicit destructor call

                        a.deallocate(p);

                }

        }

这样,就不会有资源泄漏了。”[2]

“可是我怎么解决我那稀有的内存的问题呢?”

“标准库中的allocator和boost中的pool可以解决内存管理的问题,不必每次调用operato
r new,从而少了一些开销,不过早上我刚收到客户的mail,他们说准备在他们的设备上再
加12MB的内存。”

“……”Solmyr郁闷中。

 

      [1]Object Counting in C++ CUJ 1998.4 中文译文 陈崴 http://www.jjhou.com/
(程序员杂志:2001.8 杂志上少了placement new and placement delete)

      [2] Bjarne Stroustrup's C++ Style and Technique FAQ  Is there a 
"placement delete"?

 
--
才知道   
原来
自己需要的是   
100万
份勇气。。。

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.239.104]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:4.096毫秒