effective c++ 08 ——定制 new/delete

东阳的学习笔记

  1. 多线程环境下的内存管理遭受单线程系统不曾有过的挑战。
  2. STL所使用的heap内存是由容器所拥有的分配器对象(allocator objects)管理

条款49:了解new-handler的行为

  1. 当operator new 抛出异常以反映一个未获满足的内存需求之前,它会先调用一个客户指定的错误处理函数,一个所谓的 new-handler。为了指定这个 “用以处理内存不足的”函数,客户必须调用 set_new_handler,其声明于头文件 。
namespace std {
   typedef void (*new_handler) ( );
   new_handler set_new_handler(new_handler p) throw();
}
  1. set_new_handler 的参数是个指针,指向 operator 无法分配足够内存时该被调用的函数。其返回值也是个指针。
void outOfMem()
{
	std::cerr << "Unable to satisfy request for memory\n";
	std::abort();
}

int main()
{
	
	std::set_new_handler(outOfMem);  // 当 new 无法分配足够内存时调用
	int *pBigDataArray = new int[10000000000L];
	std::cout << "i am alive" << std::endl;
	return 0;
}
  1. 一个设计良好的 new-handler 必须做以下事情:
    i. 让更多的内存可被使用。实现此策略的一个办法是:程序一开始就分配一大块内存(提前占用),当new_handler 第一次被调用时,将他们释放返还给程序使用。
    ii. 安装另一个 new_handler。为达此目的,做法之一就是让当前 new_handler 修改“会影响new_handler行为” 的static数据、namespace数据、global数据等。
    iii. 卸载 new_handler。也就是将 null 指针传给 set_new_handle。一旦没有安装任何的 new_handler,下次调用就会抛出异常。
    iv. 抛出 bad_alloc,将异常传播到内存索求处
    v. 不返回,通常调用abort() / exit()
  2. DO IT FOR ME:
class Widget: public NewHandlerSupport<Widget> { ... }
  1. 旧版本的C++,分配失败就返回 null :(使用 std::nothrow 声明使用返回null的版本)
Widget *pw2 = new ( std::nothrow ) Widget;  // 如果分配失败,则返回0
if (pw2 == 0) ...   // 侦测 null
  1. Nothrow new 是一个颇为局限的工具,因为它只适用于内存分配;后面的构造函数调用还是可能抛出异常

条款50:了解 new / delete 的合理替换时机

  1. 什么时候需要替换编译器提供的 new / delete 呢?以下是三个常见的理由:
    i. 用来检测运用上的错误。比如我们自行定义一个 new,以额外空间(位于客户所得区块之前或后)放置特定的 byte pattern (即签名,signatures)。delete 通过检查签名是否原封不动来判断是否发生了 overrununderrun,并可志记(log)下来。
    ii. 为了强化效能。编译器自带的 new / delete 采取 中庸之道。不对特定任何人有最佳表现。通过定制版本,是获得最大效能提升的方法之一。
    iii. 为了收集使用上的统计数据。了解程序对内存的使用习惯。
  2. 编写自己的 new / delete时注意齐位
  3. operator new 都应该包含一个循环,反复调用某个 new_handling 函数
  4. 何时替换缺省的 new / delete:
    i. 为了增加分配和归还的速度
    ii. 为了降低缺省内存管理器带来的额外空间开销
    iii. 为了弥补缺省内存管理器中的非最佳齐位
    iv. 为了将相关对象成簇集中(在更少的内存页上)
    v. 为了获得非传统的行为
    vi. 为了检测运用错误
    vii. 为了收集使用统计信息

条款51:编写 new / delete时需固守常规

  1. new 不只一次尝试分配内存,并在每次失败后调用 new-handling函数
  2. operator new 应该包含一个无限循环,退出循环的唯一方法是:内存分配成功抛出异常
  3. 如果你打算控制class专属之 array 控制行为,那么你需要实现 operator new []。记住:唯一要做的事就是分配一块未加工内存(row memory)。因为你无法知道每个对象有多大(在即成体系中, base / derived 的大小不一样)
  4. delete 更简单:你需要记住的唯一事情就是C++保证删除 null 指针永远安全。如果删除的是个null指针,则什么也不做
void operator delete(void *rawMemory) throw()
{
	if (rawMemory == 0) return;  // 如果删除的是个null指针,则什么也不做
}
  1. member版本的 delete,只需多一个动作检查删除数量。万一你的 class 专属的 operator new 将大小有误的分配行为转交给 ::operator new执行,你也必须将删除行为转交给 ::operator delete执行。

条款52:写了placement new 也要写 placement delete

Widget *pw = new Widget;
  1. 上述语句共调用了两个函数:new 和 构造函数
  2. 假设第一个函数调用成功,第二个函数却抛出异常。这样将会导致 pw 未被成功赋值,客户手上也没有能够指针指向该被归还的内存。取消步骤一并恢复旧观的责任因此落到C++运行期系统上
  3. 运行期系统会高高兴兴地调用步骤一调用的 new 的相应 delete 版本。
void* operator new(std::size_t) throw(std::bad_alloc);

void operator delete(void* rawMemory) throw();//global 作用域中的正常签名式

void operator delete(void* rawMemory, std::size_t size) throw();//class作用域中的典型签名式。
  1. 当你自定义自己的 new 时, 也就是带了附加参数的 new 时,究竟哪一个 delete 应该被调用就是一个问题了
class Widget{
public:
    static void* operator new(std::size_t size, std::ostream& logStream) throw(std::bad_alloc);
    static void* operator delete(void* pMemory, std::size_t size) throw();
};
  1. 如上例,一个有额外参数的 new 却定义了一个 正常的 delete。此时C++运行期系统无法正常 delete,因此需要定义一个与自定义new对应的 placement delete。此时,对应的 placement delete会被调用,抛出异常,但是,此时却并不会泄漏任何内存
class Widget{
public:
    static void* operator new(std::size_t size, std::ostream& logStream) throw(std::bad_alloc);
    static void* operator delete(void* pMemory, std::size_t size) throw();
    static void* operator delete(void* pMemory, std::ostream& logStream) throw();
};
  1. placement delete只有在 “伴随 placement new 调用而触发的构造函数”出现异常时才会被调用,对一个指针施行 delete 绝对不对调用 placement delete
  2. 由于成员函数的名称会 外围作用域中的相同名称,因此:
    i. 建立一个 base class 内含所有正常形式的 new / delete
    ii. 继承上述类,并使用 using 声明
class StandardNewDeleteForms{
public:
    //normal new/delete
    static void* operator new(std::size_t size)throw(std::bad_alloc)
    {return ::operator new(size);}
    static void operator delete(void* pMemory) throw()
    {::operator delete(pMemory);}
    //placement new/delete
    static void*  operator new(std::size_t size, void* ptr) throw()
    {return ::operator new(size, ptr);}
    static void operator delete(void* pMemory, void* ptr)throw()
    {return ::operator delete(pMemory, ptr);}
    //nothrow new/delete
    static void* operator new(std::size_t size, const std::nothrow_t& nt)throw()
    {return ::operator new(size, nt);}
    static void operator delete(void* pMemory, const std::nothrow_t&) throw()
    {::operator delete(pMemory);}
};

class Widget: public StandardNewDeleteForms{
public:
    using StandardNewDeleteForms::operator new;
    using StandardNewDeleteForms::operator delete;
    static void* operator new(std::size_t size, std::ostream& logStream)throw(std::bad_alloc);
    static void operator delete(void* pMemory, std::ostream& logStream) throw();
};
  1. 当你声明 placement new / delete,请确定不要无意识(非故意)地遮掩了他们的正常版本
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东阳z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值