c++ Allocator( ::operator new & placement new) & 构造函数中的参数初始化顺序 & NRVO

关于 allocator, 即拆分 new 的行为(只调用 ::operator new(size_t) 而不调用相应类的构造函数,相当于 malloc),实现性能的提升。

#include <iostream>
#include <exception>
#include <vector>
//using namespace std;

// see links: 
// http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4079/Allocators-STL.htm
// http://www.codeproject.com/Articles/4795/C-Standard-Allocator-An-Introduction-and-Implement

class Test
{
public:
	Test()
	{
		std::cout << "Constructor" << std::endl;
	}	
	~Test()
	{
		std::cout << "Destructor" << std::endl;
	}
};
 
int main()
{
	void * a = ::operator new(sizeof(Test));  // 分配内存
	new (a) Test();  // 调用构造函数初始化
	Test* b = static_cast<Test *>(a);
	std::cout << "address: " << a << std::endl;
	b->~Test();  // 调用析构函数
	::operator delete(a);  // 释放内存
	std::cout << "address: " << a << std::endl;

	return 0;
}

一般来说 new Test 的再编译的行为可以用一下代码表示:

Class Test;
Test* p;

 // don't catch exceptions thrown by the allocator itself
 void* raw = operator new(sizeof(Test));

 // catch any exceptions thrown by the ctor
 try {
   p = new(raw) Test();  // call the ctor with raw as this
 }
 catch (...) {
   // oops, ctor threw an exception
   operator delete(raw);
   throw;  // rethrow the ctor's exception
 }

因此,在 Test 的构造函数中抛异常不会导致 Test* p 的内存泄露,但是 Test 的析构函数不会被调用,由此可能在 Test 内部导致内存泄漏。

如果看到一个 placement new 带了参数,那么他会将这些参数传递给 operator new,
For example:

MyPtr ptr;
new(ptr, len, cont, scope) MyClass(len, name);  # placement new with parameter...
这个操作相当于:
1. calls operator new(sizeof(MyClass), ptr, len, cont, scope)   
	# 前提是必须定义了 operator new(const size_t size,   ---> 初始化为 sizeof(MyClass)
									MyPtr& ptr, 
									int len,
                                    MyCont& cont,
                                    MyScope& scope)
2.operator new 返回的内存块上(指针上),调用 MyClass 构造函数,

其效果就是,自定义分配一块内存,并且在该内存上调用构造函数,并且同时将一些参数传递到了底层用于决定如何分配内存。

更多关于 ::operator new & placement new 见下面链接:
https://eli.thegreenplace.net/2011/02/17/the-many-faces-of-operator-new-in-c

在 Inside the C++ Object Model 一书中强调, Menber Initialization List 是根据成员 在类中被声明的顺序 初始化的,而不是按照在 初始化列表中的顺序 初始化的!

如下对象:

#include <iostream>

class X
{
public:
	X(int val):j(val),i(j)  // i 先被声明,先初始化 i
	{}
public:
	int i;  // 先被声明
	int j; 
};

int main()
{
	X a(6);
	std::cerr << "a.i: " << a.i << std::endl;
	std::cerr << "a.j: " << a.j << std::endl;
}
// 编译的时候会有warning: [-Wreorder]

可能的输出:

a.i: -1218531328
a.j: 6

关于 NRVO 和 RVO 返回值优化,有人解释的很好,see link:

  1. http://stackoverflow.com/questions/5918312/what-rules-to-follow-for-a-function-to-use-nrv-optimization
  2. 甚至 Pass By Vaue 的参数也可以被优化:https://web.archive.org/web/20140113221447/http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值