C++ new、operator new、placement new

new和delete操作符我们都用过,它们是对堆中的内存进行申请和释放,要实现不同的内存分配行为,需要重载operator new,operator delete。
 

operator new 就像operator+一样,是可以重载的,但是不能在全局范围对原型为 void operator new(size_t size) 这个函数进行重载,一般只在类中进行重载(也可以重载全局的new)。
如果类中没有重载operator new,那么调用的就是全局的::operator new来完成堆的分配。同理,operator new[]、operator delete、operator delete[]也是可以重载的,一般你重载了其中一个,那么最好把其余三个都重载一遍。

placement new是operator new的一个重载版本,只是我们很少用到它。如果你想在已经分配的内存中创建一个对象,使用new是不行的。也就是说placement new允许你在一个已经分配好的内存中(栈或堆中)构造一个新的对象。原型中void*p实际上就是指向一个已经分配好的内存缓冲区的的首地址。

我们知道使用new操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常(空间不够)。placement new就可以解决这个问题。我们构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数;而且不会出现在程序运行中途出现内存不足的异常。所以,placement new非常适合那些对时间要求比较高,长时间运行不希望被打断的应用程序。

缺省情况下,C++在global作用域内提供以下形式的operator new:

void* operator new(std::size_t) throw(std::bad_alloc);    // nornal new
void* operator new(std::size_t, void*) throw();               // placement new
void* operator new(std::size_t, const std::nothrow_t&) throw();  // nothrow new

 Example:

如下,在class base中定义重载了operator new, 在main函数中需要使用::new才能触发global的new, 如果Redefine golbal new,则触发的是你自定义的global下的new,直接返回null。

// Redefine global new
void* operator new(std::size_t) throw(std::bad_alloc) {
	return nullptr;
}

class base {
public:
	static void* operator new(std::size_t size, std::ostream& logStream)   
    throw(std::bad_alloc) {
		return nullptr;
	}
};

int main() {

	//auto pBase1 = new base;// error, 不符合base::new的签名;
	auto pBase3 = new(std::cerr) base;// right, 符合base::new的签名;
	auto pBase2 = ::new base; // right, 调用的是global 下的new;
	vector<int>* p = new vector<int>;
	if (pBase2) {
		std::cout << "allocated\n";
	} else {
		std::cout << "not allocated\n";
	}
	return 0;
}


<Effective C++> chaper 8. Item50 : 理解operator new 和operator delete的合理替换时机

观念上,写一个定制型的operator new十分简单。举个例子,下面是个快速开发出的初阶段golbal operator new, 协助检测over run(写入点在分配区块之后) 或 under run(写入点在分配区块起点之前)。 其中存在一些错误,后续会完善。

static const int signature = 0xDEADBEEF;
typedef unsigned char Byte;
void* operator new(std::size_t size) throw(std::bad_alloc)
{
    	using namespace std;
	size_t realSize = size + 2 * sizeof(int);//增加大小使得能够塞入两个signature.

	void* pMem = malloc(realSize);
	if (!pMem)
		throw bad_alloc();

	//将signature写入内存的最前段落和最后段落
	*(static_cast<int*>(pMem)) = signature;
	*(reinterpret_cast<int*>(static_cast<Byte*>(pMem+realSize-sizeof(int)) )) = signature;

	//返回指针,指向位于第一个signature之后的内存位置:
	return static_cast<Byte*>(pMem) + sizeof(int);

}

为什么会有人想要替换编译器提供的operator new或operator delete呢? 以下是三个最常见的理由:

1)用来检测运用上的错误。
如果将“new所得内存”delete掉却不幸失败,会导致内存泄漏。如果在“new所得内存”身上多次delete,则会导致不确定行为。 如果operator new持有一串动态分配所得的地址,而operator delete将地址从中移走,倒是很容易检测出上述的错误用法。   此外,各式各样的编程错误可能导致数据“over runs”(写入点在分配区块尾端之后),或“under runs”(写入点在分配区块起点之前)。

如果我们自行定义一个operator new, 便可以超额分配内存,以额外空间(位于客户所得区块之前或后)放置特定的byte pattern(即签名,signatures)。operator delete释放内存的时候便得以检查上述签名是否原封不动地存在内存里面,若否就表示在分配区的某个声明时间点发生了over run或under run,这时候operator delete可以log那个始时以及那个惹是生非的指针。

2)为了强化效能。

编译器所带的operator new和operator主要用于一般目的,它们不但可被长时间执行的程序(例如web服务器) 接受,也可被执行时间少于一秒的程序接受。  它们必须处理一系列需求,包括大块内存,小块内存,大小混合型内存。  它们必须接纳各种分配形态,范围从程序存活期的少量区块动态分配,到大数量短命对象的持续分配和归还。它们必须考虑破碎问题(fragmentation),这最终会导致程序无法满足大区块内存需求,即使彼时有总量足够但分散为多个小区块的自由内存

现实存在这么多各种各样的对内存管理的要求,因此,编译器所带的operator new和operator delete采取中庸之道也就不令人惊讶了。 它们的工作对每个人都适度地好,但不对任何特定的人有最佳表现。

如果你对自己的程序的动态内存运用形态有深刻的了解,通常可以发现,定制版的operator new 和operator delete性能超过缺省版本。 说到胜过,我的意思是它们比较快,有时甚至快很多,而且它们需要

的内存比较少,最高可省50%。对某些应用程序而言,将编译器自带的operator new和operator delete替换为定制版本,是获得重大效能提升的办法之一。

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

在一头栽进定制行operator new和operator delete之前,理当先收集你的软件如何使用其动态内存。 分配区块的大小分布如何?寿命分布如何?它们倾向于以FIFO(先进先出)次序 或 LIFO(后进先出)次序 或 随机次序来分配和归还?   它们的运用形态是否随时间改变,也就是说你的软件在不同的执行阶段有不同的分配/归还形态吗? 任何时刻所使用的最大动态分配量(高水位)是多少?  

自行定义operator new和operator delete使得我们得以轻松收集这些信息。

4)为了增加分配和归还的速度


Ref:

Placement new operator in C++ - GeeksforGeeks      -- good (https://blog.csdn.net/qq_35865125/article/details/123313975)

https://blog.csdn.net/zhangxinrun/article/details/5940019    --good!

<Effective C++> chaper 8. Item50 51 52

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

First Snowflakes

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

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

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

打赏作者

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

抵扣说明:

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

余额充值