《Effective C++》第八章:定制new和delete
- 了解new-handler的行为。new和delete不是函数,是申请和释放内存的操作符。当new提出获得内存申请失败时会发生什么?老旧的编译器是返回null指针。现在呢,如果申请失败,会先调用一个错误处理函数,那就是new-handler。这就像一个回调函数,系统有一个默认的,用户也可以自行编写一个错误处理函数并使用set_new_handler函数设置之。通常自行编写的错误处理函数可以使用这些策略:
1) 多次尝试申请内存。
2) 让更多内存可用。
3) 安装另一个new-handler。
4) 卸除new-handler,以null替换之,那么如果失败,程序就会抛出异常。
5) 抛出bad-alloc异常。
6) 调用abort或exit退出。
用户在类中实现自己的错误处理函数时,需要设置为静态的,并且最好将这个错误处理函数的指针当做是资源,使用RAII技术处理。析构函数里当然是将原来缺省的new-handler设置回去,还可以把错误处理函数包装到一个模板类里,让所有需要自行定制new操作符的类继承之。那么设置new-handler的时机呢?当然是自行定制的new操作符里真正请求内存之前的一刻。
- 了解new和delete的合理替换时机。替换缺省的new和delete操作符的理由有:
1) 改善效能。
2) 对heap运用错误进行调试。
3) 收集heap使用信息。
- 编写new和delete时需固守常规。需要注意的要点有:
1) 一致性operator new必须返回正确的值。
2) 内存不足时必须调用new-handling函数。
3) 必须对付0内存申请。返回1个byte…
4) 如果打算控制某个类的数组分配行为,那么需要编写operator new[]操作符,别忘了,它们是不一样的。
5) 如果在基类中定制了operator new,那么小心这个new可能会被子类调用,而子类调用时可能申请到的内存不是你要的大小,通常是偏小了,那么在基类里改写时,sizeof操作符可以提供一些帮助,避免犯错,但是还是要小心。如果基类还改写了operator new[],那就更要小心,确保子类申请到的内存大小没有错。
6) operator delete嘛,保证删除一个null指针不会出错就好了。但是也要保证删除的内存大小要正确,特别是有继承在旁边观看的时候。
- 写了placement new也要写placement delete。operator new如果接受了除必须指定的表示申请内存大小之外的参数,那么就叫做placement new。标准程序库里有一个接受void类型指针版本的placement new。正常情况下,new一个新对象会导致分配内存的operator new和构造函数被调用,如果构造函数发生异常,用户没法释放operator new申请到的内存,这时候C++运行系统会自动调用对应版本的operator delete版本取消内存分配动作。因此,如果定制了一个placement new,也要写对应版本的placement delete(除了需要释放的指针外,还要把placement new的额外参数也作为它的参数)。否则一旦内存分配动作需要取消时,系统会因找不到对应的placement delete版本而导致内存泄露。所以一旦写了一个placement new,也要写一个placement delete。另一个需要注意的问题是函数屏蔽的问题,一个类写了placement new,会屏蔽它的父类和全局的new操作符,解决的办法是为标准的operator new和operator delete建立一个类,让改写new和delete的类继承之,然后在derived class类里使用using语句确保屏蔽问题不会发生。
时间: 2024-10-14 19:09:39