有些类的对象我们并不希望在堆区开辟。举一个例子。我们有一个文件关闭器,就是一个RAII类,负责文件的正常关闭。
template <typename FStream>
struct FileCloser
{
FStream & _file_stream;
FileCloser(FStream & file_stream) : _file_stream(file_stream)
{ }
~FileCloser()
{
_file_stream.close();
}
};
很简单也很容易实现,不是吗?领这个类的对象开辟在堆区我想应当是毫无意义的。
从性能的角度来讲,我们的成员只有一个引用而已,就是经过包装后的指针。在栈区开辟耗费不了多大的栈区内存。相反,如果使用new来产生一个对象,那么会先调用operator new然后再调用定位new来构造对象。相对于在栈区创建对象的事件,在堆区创建应当慢许多。
从资源泄露的角度来讲。我们定义了一个RAII的时候,就是为了避免资源泄露。如果读者有过使用智能指针的经验。你会发现,你似乎从没在heap中创建一个智能指针——好嘛,本来是为了管理资源泄露,这回RAII对象也有可能资源泄漏(不被delete)。
所以,对于某些特殊的对象,不让其产生在heap上是有一定道理的。
具体的做法
static void * operator new(size_t) = delete;
static void operator delete (void *) = delete;
在C++11中,你只需要在类中加上这两行代码即可。这表示这个类的new是被删除的,也就是说不能再heap中创建对象。十分的简单。如果使用的事C++11以下的版本,只需要将其声明为private即可——不过这样也是有一定的隐患,你要确保类中没有友元函数或者类中的成员函数不会调用它。
你也可以如法炮制的这样
static void * operator new[](size_t) = delete;
static void operator delete[](void * p)= delete;
就是数组的形式。
你也可以仅仅声明new的运算符是被删除的。但是这样做是一个不合理的行为。声明了new是被删除的。为什么不声明delete是被删除的?
如此这样你就可以避免任何形式的在堆区开辟对象。
或许你会说,我可以使用malloc开辟空间,使用定位new来进行构造对象。
我们且不说这样有多麻烦,一般这种工作都是由new operator(注意和operator new的区别)来完成。或者是由分配器来完成。而且这样麻烦你确定不会在中出错?好在在new声明为被删除的情况下对应的定位new也会编译错误。就像这样。Foo的new被声明为删除的。
auto p = static_cast<Foo*>(::operator new(sizeof (Foo)));
new (p) Foo;
我们会得到这样的一个编译错误。
No matching function for call to 'operator new'
希望篇博客可以帮助你,以上就是禁止对象在堆区开辟对象的这个手法的大概内容。