分享
 
 
 

Guru of the Week 条款10:内存管理(下篇)

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

GotW #10 Memory Management - Part II

著者:Herb Sutter

翻译:kingofark

[声明]:本文内容取自www.gotw.ca网站上的Guru of the Week栏目,其著作权归原著者本人所有。译者kingofark在未经原著者本人同意的情况下翻译本文。本翻译内容仅供自学和参考用,请所有阅读过本文的人不要擅自转载、传播本翻译内容;下载本翻译内容的人请在阅读浏览后,立即删除其备份。译者kingofark对违反上述两条原则的人不负任何责任。特此声明。

Revision 1.0

Guru of the Week 条款10:内存管理(下篇)

难度:6 / 10

(你在考虑对某个类实现你自己特定的内存管理方案吗?甚或是想干脆替换掉C++的全局操作符new和delete?先试试下面这个问题再说吧!)

[问题]

下面的代码摘自某个程序,这个程序有一些类使用它们自己的内存管理方案。请尽可能的找出与内存有关的错误,并回答其注释中的附加题。

// 为什么B的delete还有第二个参数?

// 为什么D的delete却没有第二个参数?

//

class B {

public:

virtual ~B();

void operator delete ( void*, size_t ) throw();

void operator delete[]( void*, size_t ) throw();

void f( void*, size_t ) throw();

};

class D : public B {

public:

void operator delete ( void* ) throw();

void operator delete[]( void* ) throw();

};

void f()

{

// 下面各个语句中,到底哪一个delete被调用了?为什么?

// 调用时的参数是什么?

//

D* pd1 = new D;

delete pd1;

B* pb1 = new D;

delete pb1;

D* pd2 = new D[10];

delete[] pd2;

B* pb2 = new D[10];

delete[] pb2;

// 下面两个赋值语句合法吗?

//

B b;

typedef void (B::*PMF)(void*, size_t);

PMF p1 = &B::f;

PMF p2 = &B::operator delete;

}

class X {

public:

void* operator new( size_t s, int )

throw( bad_alloc ) {

return ::operator new( s );

}

};

class SharedMemory {

public:

static void* Allocate( size_t s ) {

return OsSpecificSharedMemAllocation( s );

}

static void Deallocate( void* p, int i ) {

OsSpecificSharedMemDeallocation( p, i );

}

};

class Y {

public:

void* operator new( size_t s,

SharedMemory& m ) throw( bad_alloc ) {

return m.Allocate( s );

}

void operator delete( void* p,

SharedMemory& m,

int i ) throw() {

m.Deallocate( p, i );

}

};

void operator delete( void* p ) throw() {

SharedMemory::Deallocate( p );

}

void operator delete( void* p,

std::nothrow_t& ) throw() {

SharedMemory::Deallocate( p );

}

[解答]

// 为什么B的delete还有第二个参数?

// 为什么D的delete却没有第二个参数?

//

class B {

public:

virtual ~B();

void operator delete ( void*, size_t ) throw();

void operator delete[]( void*, size_t ) throw();

void f( void*, size_t ) throw();

};

class D : public B {

public:

void operator delete ( void* ) throw();

void operator delete[]( void* ) throw();

};

附加题答案:这是出于个人喜好(preference)。两种都是正常的回收(译注:作者在这里用了“deallocation(去配)”一词,鄙人一律翻译为“回收”)函数,而不是placement deletes(译注:关于placement delete和placement new,可以阅读Stanley Lippman的《Inside The C++ Object Model(深度探索C++对象模型)》一书中的6.2节:Placement Operator New的语意)。

另外,这两个类都提供了delete和delete[],却没有提供相对应的new和new[]。这是非常危险的。你可以试想,当更下层的派生类提供了它自己的new和new[]时会发生什么!

void f()

{

// 下面各个语句中,到底哪一个delete被调用了?为什么?

// 调用时的参数是什么?

//

D* pd1 = new D;

delete pd1;

这里调用了D::operator delete(void*)。

B* pb1 = new D;

delete pb1;

这里调用D::operator delete(void*)。因为B的析构函数(destructor)被声明成virtual,所以D的析构函数(destructor)理所当然会被正常调用,但同时这也意味着,即使B::operator delete()不声明成virtual,D::operator delete()也必将被调用。

D* pd2 = new D[10];

delete[] pd2;

这里D::operator delete[](void*)被调用。

B* pb2 = new D[10];

delete[] pb2;

这里的行为是不可预料的。C++语言要求,传递给delete的指针之静态类型必须与其之动态类型一样。关于这个问题的进一步谈论,可以参看Scott Meyers在《Effective C++》或者《More Effective C++》中有关“Never Treat Arrays Polymorphically”的部分(译注:这里指的应该是《More Effective C++》中的条款3:绝对不要以多态方式处理数组)。

// 下面两个赋值语句合法吗?

//

B b;

typedef void (B::*PMF)(void*, size_t);

PMF p1 = &B::f;

PMF p2 = &B::operator delete;

}

第一个赋值语句没问题,但是第二个是不合法的,因为 “void operator delete(void*,size_t)throw()”并不是B的成员函数,即使它被写在上面使其看上去很像是。这里有一个小伎俩需要弄清楚,即new和delete总是静态的,即使它们不被显式的声明为static。总是把它们声明为static是个很好的习惯,这可以让所有阅读你代码的程序员们明白无误的认识到这一点。

class X {

public:

void* operator new( size_t s, int )

throw( bad_alloc ) {

return ::operator new( s );

}

};

这会产生内存泄漏,因为没有相应的placement delete存在。下面的代码也是一样:

class SharedMemory {

public:

static void* Allocate( size_t s ) {

return OsSpecificSharedMemAllocation( s );

}

static void Deallocate( void* p, int i ) {

OsSpecificSharedMemDeallocation( p, i );

}

};

class Y {

public:

void* operator new( size_t s,

SharedMemory& m ) throw( bad_alloc ) {

return m.Allocate( s );

}

这里也产生内存泄漏,因为没有对应的delete。如果在用这个函数分配的内存里放置对象的构造过程中抛出了异常,内存就不会被正常释放。例如:

SharedMemory shared;

...

new (shared) T; // if T::T() throws, memory is leaked

在这里,内存还无法被安全的删除,因为类中没有提供正常的operator delete。这意味着,基类或者派生类的operator delete(或者是那个全局的delete)将会试图处理这个回收操作(这几乎肯定会失败,除非你替换掉周围环境中所有类似的delete——这实在是一件繁琐和可怕的事情)。

void operator delete( void* p,

SharedMemory& m,

int i ) throw() {

m.Deallocate( p, i );

}

};

这里的这个delete毫无用处,因为它从不会被调用。

void operator delete( void* p ) throw() {

SharedMemory::Deallocate( p );

}

这是一个严重的错误,因为它将要删除那些被缺省的::operator new分配出来的内存,而不是被SharedMemory::Allocate()分配的内存。你顶多只能盼来一个迅速的core dump。真是见鬼!

void operator delete( void* p,

std::nothrow_t& ) throw() {

SharedMemory::Deallocate( p );

}

这里也是一样,只是更为微妙。这里的delete只会在“new(nothrow)T”失败的时候才会被调用,因为T的构造函数(constructor)会带着一个异常来终止,并企图回收那些不是被SharedMemory::Allocate()分配的内存。这真是又邪恶又阴险!

好,如果你回答出了以上所有的问题,那你就肯定是走在成为内存管理机制专家的光明大道上了。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有