Effective C++ 条款 51:编写new和delete时需固守常规

(一)

实现一致性operator new必须返回正确的值,内存不足时必须调用new_handling函数,必须有对付零内存的准备,还需要避免不慎掩盖正常形式的new。

void* operator new(std::size_t size) throw(std::bad_alloc) {
    using namespace std;
    if (size == 0) {
        size = 1;
    }
    while (true) {
        尝试分配size byte
        if (分配成功)
            return (一个指针,指向分配得来的内存)
        // 分配失败
        new_handler globalHandler = set_new_handler(0);
        set_new_handler(globalHandler);

        if (globalHandler) (*globalHandler)();
        else throw std::bad_alloc();
    }
}

没有任何办法直接取得new_handling函数指针,所以必须调用set_new_handler找它出来。拙略,但有效——至少对单线程程序而言,多线程或许需要某种机锁以便安全处置new_handling函数背后的global数据结构。

operator
new有个无穷循环,退出次循环的唯一办法是:内存成功分配或new_handling函数做了一件描述于条款49的事情:让更多内存可用、安装另一个new_handler、卸除new-handler、抛出bad_alloc异常、或者承认失败而直接return。

(二)

operator
new成员函数会被derived classes继承。如果针对class X而设计的operator new,只为大小为sizeof(X)的对象而设计。一旦被继承下去,base class的operator new被调用用以分配derived class对象:

class Base {
public:
    static void* operator new(std::size_t size) throw(std::bad_alloc);
};

class Derived : public Base {
    ...
};
Derived* p = new Derived;        //调用base::operator new

如果base
class专属的operator new并非被设计用来对付上述情况,处理此情势最佳做法是:“内存申请量错误”的调用改采用标准operator new:

void* Base::operator new(std::size_t size) throw(std::bad_alloc)
{
    if (size != sizeof(Base))
        return ::operator new(size);
    ...
}

如果size
为0,这份申请转交到::operator new手上,因为sizeof(Base)的大小不可能为0。

如果打算控制class专属“array内存分配行为”,那么你要实现operator new的array兄弟版;operator new[]。通常被称为“array new”,如果决定写个operator new[],唯一要做的一件事就是分配一块未加工内存,因为你无法对array之内迄今尚未存在的元素对象做任何事情。实际上,你甚至无法计算那个array将含多少个元素对象。你不知道每个对象多大,毕竟base class的operator new[]有可能经由继承被调用,将内存分配给“元素为derived class对象”的array使用。

因此,你不能在Base::operator new[] 内假设array的每个元素对象的大小是sizeof(base),这也意味着你不能假设array的元素个数是(byte申请数)/sizeof(base)。此外,传递给operator new[]的size_t参数,其值有可能比“将被填以对象”的内存数量多,条款16说过,动态分配的arrays可能包含额外空间用来存放元素个数。

(三)

operator
delete的情况更简单,只要记住唯一的事情就是C++保证删除null指针永远安全,所以你必须兑现这项保证。

下面是non-member
operator delete的伪码:

void operator delete(void *rawMemory) throw() {
    if (rawMemory == 0) return;
    现在,归还rawMemory所指内存
}

下面是member
operator delete,member版本也简单,只需多加一个动作检查删除数量。

万一你的class专属operator
new将大小有误的分配转交::operator new执行,你必须也将大小有误的删除行为转交::operator delete执行:

class Base {
public:
	static void* operator new(std::size_t size) throw(std::bad_alloc);
	static void operator delete(void* rawMemory, std::size_t size) throw();
};

void Base::operator delete(void* rawMemory, std::size_t size) throw() {
	if(rawMemory == 0) return 0;
	if(size != sizeof(Base)) {
		::operator delete(rawMemory);
		return;
	}
	现在,归还rawMemory所指的内存;
	return ;
}

如果即将被删除的对象派生自某个base
class而后者欠缺virtual析构函数,那么c++传给operator delete的size_t数值可能不正确。

此时,operator
delete可能无法正确运作。

请记住:

(1)operator new应该内含一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler。它也应该有能力处理0bytes申请,class专属版本则还应该处理“比正确大小更大的(错误)申请”。

(2) operator delete应该在收到null指针时不做任何事情。class专属版本则还应该处理“比正确大小更大的(错误)申请”。

时间: 2024-08-29 08:15:43

Effective C++ 条款 51:编写new和delete时需固守常规的相关文章

Effective C++ 条款51 编写new和delete时需固守常规

1. 实现定制的operator new和operator delete需要满足一定的要求. 以operator new而言:实现一致性operator new必须返回正确的值;内存不足时必得调用new-handling函数;必须有对付零内存需求的准备;需避免不慎掩盖正常形式的new;如果有能力供应客户申请的内存,就返回一个指针指向该内存,反之就遵循条款49的规则并抛出bad_alloc异常;应该内含一个无限循环,知道成功分配内存或new-handling完成其功能...... 以上的要求中"必

《Effective C++》:条款51:编写new和delete时需固守常规

条款 50已经说明为什么要写自己的operator new和operator delete,本条款解释在编写时遵循什么守则. 从operator new开始.operator new必须返回正确的值,内存不足时必须调用new-handling函数,要有对付零内存需求的准备,避免不慎掩盖正常形式的new–这比较偏近class接口的要求而非实现要求.正常形式的new描述与条款 52. operator new如果申请内存成功,就返回指向那块内存的指针,失败则遵循条款 49描述,抛出bad_alloc

Effective C++ -----条款51:编写new 和delete 时需固守常规

operator new 应该内含一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler.它也应该有能力处理0 bytes 申请.Class专属版本则还应该处理“比正确大小更大的(错误)申请”. operator delete 应该在收到null指针时不做任何事.Class 专属版本则还应该处理“比正确大小更大的(错误)申请”.

条款八: 写operator new和operator delete时要遵循常规

自己重写operator new时(条款10解释了为什么有时要重写它),很重要的一点是函数提供的行为要和系统缺省的operator new一致.实际做起来也就是:要有正确的返回值:可用内存不够时要调用出错处理函数(见条款7):处理好0字节内存请求的情况.此外,还要避免不小心隐藏了标准形式的new,不过这是条款9的话题. 有关返回值的部分很简单.如果内存分配请求成功,就返回指向内存的指针:如果失败,则遵循条款7的规定抛出一个std::bad_alloc类型的异常.operator new实际上会不

Effective C++ 条款50 了解new和delete的合理替换时机

1. 替换标准库提供的operator new或operator delete通常基于以下三个理由: 1). 用来检测运行上的错误.将"new 所得内存"delete掉却不幸失败会导致内存泄露,多次对同一块"new所得内存"施行delete会导致未定义行为,如果让operator new持有一串动态分配所得地址,而operator delete将地址从中移走,就可以很容易检测出上述错误;各式各样的变成错误会导致数据"overruns"(写入点在分

读书笔记 effective c++ Item 51 实现new和delete的时候要遵守约定

Item 50中解释了在什么情况下你可能想实现自己版本的operator new和operator delete,但是没有解释当你实现的时候需要遵守的约定.遵守这些规则并不是很困难,但是它们其中有一些并不直观,所以知道这些规则是什么很重要. 1. 定义operator new的约定 1.1 约定列举 我们以operator new开始.实现一个一致的operator new需要有正确的返回值,在没有足够内存的时候调用new-handling函数(见Item 49),并且做好准备处理没有内存可分配

Effective C++ -----条款21:必须返回对象时,别妄想返回其reference

绝不要返回pointer或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象.条款4已经为“在单线程环境中合理返回reference指向一个local static对象”提供了一份设计实例.

Effective C++:条款16:成对使用new和delete时要采取相同形式

(一) 先看下面的代码: string* stringArray = new std::string[100]; ... delete stringArray; 这样的做法是错误的,因为stringArray所含的100个string对象中的99个可能并没有被适当地删除,因为它们的析构函数很可能没有被调用. (二) 使用new时发生的事情: (1)内存被分配出来: (2)针对此内存会有一个或更多个构造函数被调用: 使用delete,也有两个动作: (1)针对此内存会有一个或更多个析构函数被调用:

Effective C++ 条款 50:了解new和delete的合理替换时机

(一) 为什么有人想要替换operator new 和 operator delete呢?三个常见的理由: (1)用来检测运用上的错误. (2)为了强化效果. (3)为了收集使用上的统计数据. (二) 下面是个快速发展得出的初阶段global operator new,促进并协助检测"overruns"或"underruns". static const int signature = 0xDEADBEEF; typedef unsigned char Byte;