关于placement new
在https://blog.csdn.net/qq_42604176/article/details/111997397中已经介绍了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 newvoid* 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));
}