《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异常。它实际上不知一次尝试内存分配,在每次失败后都调用new-handling函数。这里假设new-handling函数能做某些动作将一些内存释放出来。只有指向new-hangling函数的指针为,才会抛出异常。C++规定,即使客户要求0 byte,operator new也要返回一个合法指针。下面是个non-member operator new的伪码(pseudocode):

void* operator new(std::size_t size) throw(std::bad_alloc)
{
    using namespace std;
    if(size==0){//处理0-byte申请
        size=1;
    }
    while(true){
        尝试分配size bytes;
        if(分配成功)
            return 指向分配得来的内存;
        //分配失败,找到当前的new-handling函数
        new_handler globalHandler=set_new_handler(0);
        set_new_handler(globalHandler);

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

如果在多线程环境下,还需要某种锁机制,以便处理new-handling函数背后的global数据结构。

上面包含一个死循环,退出此死循环唯一办法就是内存分配成功(假设new-handling不为null),所以new-handling函数做的事是:让更多内存可用、安装另一个new-handler、卸载new-handler、抛出bad_alloc异常(或其派生类),或承认失败直接return。

上面的operator new成员函数可能会被derived classes继承。注意分配内存大小size,它是函数接收的实参。**条款**50提到,定制内存分配器往往是为了特定的class对象,以此来优化,不是为了该class的derived classes。

class Base{
public:
    static void* operator new(std::size_t size) throw(std::bad_alloc)
    ……
};
class Derived:public Base
{……};//假设为重新定义operator new
Derived* p=new Derived;//这里调用了Base::operator new

如果是class专属的operator new,应该改为这样:

void* Base::operator new(std::size_t size) throw(std::bad_alloc)
{
    if(size!=sizeof(Base))
        return ::operator new(size);//使用标准的operator new
    ……
}

如果你打算控制class专属版的arrays内存分配,那么需要实现operator new[]。编写operator new[]时,唯一要做的一件事就是分配一块未加工的内存(raw memory),因为你无法对array之内迄今尚未存在的元素对象做任何事。甚至我们无法知道这个array含有多少个元素对象。可能你不知道每个对象多大,因为base class的operator new[]有可能经由继承被调用,将内存分配给derived class对象的array使用。

所以不能再Base::operator new[]中假设每个元素对象大小是sizeof(Base),这样就是说你不能假设array元素个数是(bytes申请数/sizeof(Base))。此外,传递给operator new[]的size_t参数,其值有可能比将辈填对象的内存大一些,因为条款 16提过,动态分配的arrays可能包含额外空间用来存放元素个数。

operator delete情况就简单很多,但是要记住,C++保证删除指针永远安全。下面是non-member operator delete的伪码(pseudocode):

void operator delete(void* rawMemory) throw()
{
    if(rawMemory==0) return;

    归还rawMemory所指内存;
}

这个函数的member版本也很简单,只需多加一个检查删除数量。

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

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

总结

  • operator new应该内涵死循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler。它也应该有能力处理0bytes申请。class专属版本的还应该处理“比正确大小更大的(错误)申请”。
  • operator delete应该在收到指针时不做任何事。class专属版本则还应该处理“比正确大小更大的(错误)申请”。
时间: 2024-10-12 22:07:15

《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时需固守常规

(一) 实现一致性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 (一个指针,指向

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;