Effective C++学习笔记八: 定制new和delete

四十九:Understand the behaviour of the new-handler. 了解new-handler的行为

operator new抛出异常之前会调用一个客户指定的错误处理函数,new-handler。

为了指定这个用以处理内存不足的函数,客户必须调用set_new_handler。

例子:

namespace std
{
	typedef void(*new_handler)();
	new_handler set_new_handler(new_handler p) throw();
}

void OutOfMem()
{
	std::cerr << "Unable to satisfy request for memory\n";
	std::abort();
}

int main
{
	std::set_new_handler(OutOfMem);
	//...
}

一个设计良好的new-handler函数必须做以下事情:

1.让更多的内存可被使用。 实现此策略的一个做法:程序一开始执行就分配一大块内存,然后当new-handler第一次被调用,将他们释放归还给程序使用。

2.安装另一个new-handler

3.卸除new-handler 将null指针传给set_new_handler  一旦没有安装任何new-handler,operator new 会在内存分配不成功时抛出异常。

4.抛出bad_alloc或派生自bad_alloc的异常。

5.不返回,通常调用abort或exit。

五十:Understand when it makes sense to replace new and delete.了解new和delete的合理替换时机。

替换编译器提供的operator new或operator delete的原因。

1.用来检测运用上的错误。 

2.为了强化效能。

3.为了收集使用上的统计数据。

摘要:

1.为了检测运用错误。

2.为了收集动态分配内存的使用统计信息。

3.为了增加分配和归还的速度。定制的内存分配器一般都有更好的速度。

4.为了降低缺省内存管理器带来的空间额外开销。

5.为了弥补缺省分配器中的非最佳齐位。

6.为了将相关对象成簇集中。

7.为了获得非传统的行为。 

五十一:Adhere to convention when writong new and delete。编写new和delete时需固守常规。

只有当指向new-handing函数的指针是null,operator new才会抛出异常,否则每次尝试分配内存后都会调用new-handler函数。

下面是一个non-member operator new伪代码

void* operator new(std::size_t size) throw(std::bad_alloc)
{
	using namespace std;
	if (size == 0)
		size = 1; // 处理0-byte申请将它视为1-byte申请。

	while (true)
	{
		//尝试分配 size bytes;
		if (分配成功)
			return (一个指针,指向分配的来的内存);

		//分配失败: 找出目前的new-handing函数
		new_handler globalHandler = set_new_handler(0);
		set_new_handler(globalHandler);

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

上面的置空再恢复new_handler的操作是为了获得handler指针。

注意:operator new成员函数会被derived classes继承。以下

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

class Derived :public Base {//假设Derived未声明operator new

};

Derived* p = new Derived;//这里调用的是Base::operator new

可以将基类的operator new:

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

	//...
}

operator delete 情况更简单,c++保证删除null指针永远安全。 

void operator delete(void* rawMenory) throw()
{
	if (rawMenory == 0) return; //如果将被删除的是个null指针,那就什么都不做

	//现在,归还rawMemory所指的内存。
}

注:

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

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

五十二:Write placement delete if you write placement new.写了placement new 也要写 placement delete

如果operator new接受的参数除了一定会有的那个size_t之外还有其他,这就是所谓的placement new。

众多placement new版本中特别有用的一个是"接受一个指针指向对象该被构造之处",如下:

void* operator new(std::size_t, void* pMemory) throw();

这个版本的new已经被纳入C++标准,在#include <new>就可以取用它。这个new的用途之一是负责在vector的未使用空间上创建对象。

规则:

如果一个带有额外参数的operator new没有“带相同额外参数”的对应版operator delete,那么当new的内存分配动作需要取消并恢复旧观时候就没有任何operator delete会被调用。所以他们可能需要被成对声明:

class Widget
{
	static void* operator new(std::size_t size, std::ostream& logStream)
		throw(std::bad_alloc);

	static void operator delete(void* pMemory) throw();
	static void operator delete(void* pMemory, std::ostream& logStream) throw();
};

注:

1.当你写一个placement operator new,请确定也写出了对应的placement operator delete。如果没有这样做,你的程序可能会发生内存泄漏。

2.当你声明placement new和placement delete,请确定不要无意识地遮掩它们地正常版本。

发布了35 篇原创文章 · 获赞 5 · 访问量 407

猜你喜欢

转载自blog.csdn.net/qq_33776188/article/details/103177937