分享
 
 
 

C++ Gotchas 条款60:没能区分单体内存分配与数组内存分配

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

Gotcha #60: Failure to Distinguish Scalar and Array Allocation

Gotcha条款60:没能区分单体内存分配与数组内存分配

单一个Widget与一个Widget数组是等同的吗?当然不是。那为何这么多C++程序员在发现“数组(arrays)与单个量(scalars)采用不同的运算符进行空间的分配和释放”时会感到吃惊?

我们都知道如何对单一个Widget进行空间分配和释放。我们使用new operator和delete operator:

Widget *w = new Widget( arg );

// . . .

delete w;

new operator与C++中大部分其它运算符不同的是,其行为无法经由重载而被改变。new operator总是调用一个名为operator new的函数来(可想而知的)获取一些存储空间,并可能对该空间进行初始化。对于上述代码中Widget的情形,使用new operator将首先导致一个对operator new函数的调用,该函数接收一个size_t型别作为参数,然后唤起Widget构造函数施行于由operator new函数返回的未初始化存储空间,从而产生一个Widget对象。

代码中的delete operator 针对Widget唤起一个析构函数,然后调用一个名为operator delete的函数来(可想而知的)去配“现在已经消失的Widget对象原来所占用的那片存储空间”。

内存分配与去配的行为变化,是经由重载、替换或者隐藏operator new函数与operator delete函数来实现的,而不是通过修改new operator与delete operator的行为使然。

我们同样也知道如何对Widgets数组进行空间分配与释放。但我们不使用new operator和delete operator:

w = new Widget[n];

// . . .

delete [] w;

不同之处在于,这里我们使用了new [] operator和delete [] operator(发音分别为“array new”和“array delete”)。与new和delete相同,array new operator和array delete operator的行为是不可改变的。Array new首先唤起一个名为operator new[]的函数以获取一些存储空间,然后(如果必要的话)从数组的第一个元素直至最后一个元素,对每一个已分配空间的数组元素施行缺省的初始化动作。Array delete首先按照与初始化动作相反的次序销毁数组中的每一个元素,然后唤起一个名为operator delete[]的函数以归还(reclaim)存储空间。

顺带一提:应该注意,使用标准程序库提供的vector而非array,通常是更好的设计决策。vector 的效能几乎与array一样,并且还更加安全、灵活。一般说来,vector可以被看作是“灵巧的”array,与array具有相近的语义。唯有不同的是,vector在被销毁时,其元素是从头到尾被销毁的;而在array中,元素是按照相反的次序被销毁的。

内存管理函数必须妥当的配对使用。如果使用了new来获取存储空间,那么就应该使用delete来释放。如果使用了malloc来获取存储空间,那么就应该使用free来释放。将new和free一起,或者将malloc和delete一起使用于某个特定平台上的某些型别,在有些时候是可以“工作”的,但不存在任何保证使得这样的代码会继续正常工作下去。

int *ip = new int(12);

// . . .

free( ip ); // 错的!

ip = static_cast<int *>(malloc( sizeof(int) ));

*ip = 12;

// . . .

delete ip; // 错的!

数组的分配和删除遵循同样的配对规则。有一个常见的错误就是,使用array new分配一个数组之后又用scalar delete(用于单个量的delete)来进行释放。这种代码正如new和free的错配使用一样,或许在某个特定的情形之下可以侥幸工作,但诚然是不正确的做法,将来很可能会导致错误发生。

double *dp = new double[1];

// . . .

delete dp; // 错的!

应该注意的是,对于使用scalar delete(用于单个量的delete)删除数组的错误做法,编译器不会给出警告,因为其无法区分“指向数组的指针”与“指向单个元素的指针”。一般来说,array new会在为数组分配的内存之邻接位置插入一些额外信息,用以标明存储区块(block)的大小以及所分配数组中包含的元素个数。Array delete正是通过检视这些信息并由此来删除数组的。

(经由array new得到的)这种信息之格式也许不同于经由scalar new得到的信息之格式。如果唤起scalar delete来释放array new分配的存储空间,那么相应的关于空间大小和元素个数的信息就可能被scalar delete错误的解析,从而导致未定义结果——而其本来应该是由array delete进行解析的。Scalar形式的分配与array形式的分配也有可能使用了不同的内存池(memory pools)。倘若如此,那么使用scalar delete向scalar pool归还一片分配于array pool的存储空间,就会导致灾难性的结果。

delete [] dp; // 对了!

这种在array分配与scalar分配两个概念上的闪失大意,也经常出现在用于内存管理的成员函数之设计中:

class Widget {

public:

void *operator new( size_t );

void operator delete( void *, size_t );

// . . .

};

Widget class的作者决心自己定制Widgets内存管理,但是却没有考虑到:array operator new函数和array operator delete函数与其对应的scalar形式的函数(scalar operator new和scalar operator delete),实际上具有不同的名称(译注:scalar形式分别为operator new()和operator delete();而array形式分别为operator new[]()和operator delete[](),详见下文所述)。

Widget *w = new Widget( arg ); // OK

// . . .

delete w; // OK

w = new Widget[n]; // oops!

// . . .

delete [] w; // oops!

由于Widget class没有声明operator new[]和operator delete[]函数,因此Widgets数组的内存管理将使用这些函数的global版本。这样可能就造成不正确的行为,而Widget class的作者则应该提供operator new[]和operator delete[]的成员函数版本。

如果事实恰恰相反,上述恰好是意欲之中的正确行为,那么该类别的作者就应该向以后的维护者清楚的说明这个情况;否则的话,维护者就很可能会提供那些“遗失”的函数,试图“修正”这个“问题”。将这种设计决策文档化的最佳方法不是加注释,而是直接通过代码体现:

class Widget {

public:

void *operator new( size_t );

void operator delete( void *, size_t );

void *operator new[]( size_t n )

{ return ::operator new[](n); }

void operator delete[]( void *p, size_t )

{ ::operator delete[](p); }

// . . .

};

Array new和array delete的inline member function版本在运行期不会导致任何额外消耗;同时,即便是最粗心大意的维护者也能不二的确信作者的意图乃是“为Widgets唤起array new和array delete函数的global版本”,而不至于反复猜疑。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有