关于placement new
在blog.csdn.net/qq_42604176…中已经介绍了placement new的形式。
它的形式为new()/delete().我们将分配好内存的指针送入括号中,就完成了初步的调用了。
其实我们可以定义放任何的东西到()内部。只放一个指针的版本是的new()是标准库先写好给我们的。
我们可以重载operator new,并写出多个版本,如:
Foo* pf = new(300,'c')Foo; //注意,这里没有传入指针
前提是每一个版本的声明都必须由独特的参数列,其中第一个参数必须是size_t,这是因为当没有()时,进行的是new Foo操作,Foo的大小会被传进operator new中作为第一参数,Foo的大小是个size_t类型。所以我们写的各种各样的版本也必须遵循这个规则。第二第三参数等等可由自己设计。new()括号中的就是第二第三参数,他们可以指定placement arguments 为初值。
下面是实例:
class Foo {
public:
Foo() {cout << "Foo::Foo()" << endl; };
Foo(int) {cout << "Foo::Foo()" << endl; throw Bad();} //这里故意抛出异常,用来测试 placement operator delete
//【1】一般的operator new()的重载
void* operator new(size_t size) {
return malloc(size);
}
//【2】这个是标准库已提供的placement new()的重载形式
void* operator new(size_t size, void* start) {
return start;
}
//【3】这个是我们重载的 placement new
void* operator new(size_t size, long extra) {
return malloc(size + extra);
}
//【4】这个也是我们重载的 placement new
void* operator new(size_t size, long extra, char init) {
return malloc(size + extra);
}
//【5】这个也是我们重载的,不过我们故意写错定义参数的类型
void* operator new(long extra, char init) {
return malloc(extra);
} //很显然这个版本会报错
};
关于placement delete
我们也可以重载placement operator delete,并对应着placement operator new写出多个对应版本,但他们绝对不会被delete调用。
只有当new所调用的ctor抛出异常,才会调用这些重载版本的operator delete。
也就是说重载的placement operator delete是用来释放未能成功创建的对象所占的内存。(正如我们所知,创建一个对象实际上是先申请空间,再调用构造函数。空间申请到了,但是对象却没构造出来,那么理所当然需要将空间释放)
对应上面的四种版本的delete:
//【1】一般的 operator delete()的重载
void operator delete(void*,size_t)
{
cout << "operator delete(void*,size_t)" << endl;
}
//【2】对应第二种
void operator delete(void*,void*)
{
cout << "operator delete(void*,void*)" << endl;
}
//【3】对应第三种
void operator delete(void*,long)
{
cout << "operator delete(void*,long)" << endl;
}
//【4】对应第四种
void operator delete(void*,long,char)
{
cout << "operator delete(void*,long,char)" << endl;
}
侯捷老师给出了下面的示例,运行到第五种。我们可以发现,此时的构造函数调用的是第二种构造函数。在之前的定义中,我们在这里抛出了异常。
接下俩便是这几条语句的执行结果:
如上所示,这些new都被重载了。所以才会打印信息。
按照道理,在构造函数抛出异常后,会调用自己重载的placement delete,打印信息。但在这里并没有,这是编译器的原因。
关于basic_string重载new()来扩充申请量
basic_string是标准库里面的一个class,就是我们使用的字符串。
如下:
template<...>
class basic_string
{
private:
struct Rep {...};
...
void release() {if(--ref == 0) delete this;}
inline static void* operator new(size_t,size_t);
inline static void operator delete(void*);
inline static Rep* create(size_t);
...
};
operator new的具体代码如下:
template<class charT,class traits, class Allocator>
inline void* basic_string<charT,traits,Allocator>::Rep::
operator new(size_t s,size_t extra)
{
return Allocator::allocate(s + extra * sizeof(charT));
}
如何使用看这儿:
这里我们把第二参数叫做extra。它的作用是,当使用者去创建一个字符串,如"hello",加上结束符一共6个字符。但是它在分配的时候还会分配extra个字符大小的空间。具体原因不做细究。
template<class charT,class traits,class Allocator>
inline basic_string<charT,traits,Allocator>::Rep*
basic_string<charT,traits,Allocator>::Rep::
create(size_t extra)
{
extra = frob_size(extra + 1);
Rep *p = new(extra)Rep;
...
return p;
}
它的placement delete重载之后则长这样:
template<class charT,class traits,class Allocator>
inline void basic_string<charT,traits,Allocator>::Rep::
operator delete(void* ptr)
{
Allocator::deallocate(ptr,sizeof(Rep) + reinterpret_cast<Rep*>(ptr)->res * sizeof(charT));
}