I've been reading somewere that when you use placement new then you have to call the destructor manually.
我一直在阅读,当你使用placement new时,你必须手动调用析构函数。
Consider the folowing code:
考虑下面的代码:
// Allocate memory ourself
char* pMemory = new char[ sizeof(MyClass)];
// Construct the object ourself
MyClass* pMyClass = new( pMemory ) MyClass();
// The destruction of object is our duty.
pMyClass->~MyClass();
As far as I know operator delete
normally calls the destructor and then deallocates the memory, right? So why don't we use delete
instead?
据我所知,operator delete通常会调用析构函数然后释放内存,对吧?那么为什么我们不使用删除呢?
delete pMyClass; //what's wrong with that?
in the first case we are forced to set pMyClass to nullptr
after we call destructor like this:
在第一种情况下,在我们调用这样的析构函数之后,我们*将pMyClass设置为nullptr:
pMyClass->~MyClass();
pMyClass = nullptr; // is that correct?
BUT the destructor did NOT deallocate memory, right? So would that be a memory leak?
但是析构函数没有释放内存,对吧?那会是内存泄漏吗?
I'm confused, can you explain that?
我很困惑,你能解释一下吗?
4 个解决方案
#1
34
Using the new
operator does two things, it calls the function operator new
which allocates memory, and then it uses placement new, to create the object in that memory. The delete
operator calls the object's destructor, and then calls operator delete
. Yeah, the names are confusing.
使用new运算符做两件事,它调用函数operator new分配内存,然后使用placement new,在该内存中创建对象。 delete运算符调用对象的析构函数,然后调用operator delete。是的,这些名字令人困惑。
//normal version calls these two functions
MyClass* pMemory = new MyClass; void* pMemory = operator new(sizeof(MyClass));
MyClass* pMyClass = new( pMemory ) MyClass();
//normal version calls these two functions
delete pMemory; pMyClass->~MyClass();
operator delete(pMemory);
Since in your case, you used placement new manually, you also need to call the destructor manually. Since you allocated the memory manually, you need to release it manually. Remember, if you allocate with new
, there must be a coressponding delete
. Always.
因为在您的情况下,您手动使用了新的放置,您还需要手动调用析构函数。由于您手动分配了内存,因此需要手动释放它。请记住,如果您使用new分配,则必须有一个同步删除。总是。
However, placement new is designed to work with internal buffers as well (and other scenarios), where the buffers were not allocated with operator new
, which is why you shouldn't call operator delete
on them.
但是,placement new设计用于内部缓冲区(以及其他方案),其中缓冲区未使用operator new分配,这就是为什么不应该对它们调用operator delete。
#include <type_traits>
struct buffer_struct {
std::aligned_storage<sizeof(MyClass)>::type buffer;
};
int main() {
buffer_struct a;
MyClass* pMyClass = new (&a.buffer) MyClass(); //created inside buffer_struct a
//stuff
pMyClass->~MyClass(); //can't use delete, because there's no `new`.
return 0;
}
The purpose of the buffer_struct
class is to create and destroy the storage in whatever way, while main
takes care of the construction/destruction of MyClass
, note how the two are (almost*) completely separate from each other.
buffer_struct类的目的是以任何方式创建和销毁存储,而main负责MyClass的构造/销毁,注意两者是如何(几乎*)完全相互分离的。
*we have to be sure the storage has to be big enough
*我们必须确保存储空间足够大
#2
9
One reason this is wrong:
这是错误的一个原因:
delete pMyClass;
is that you must delete pMemory
with delete[]
since it is an array:
是你必须用delete []删除pMemory,因为它是一个数组:
delete[] pMemory;
You can't do both of the above.
你无法做到以上两点。
Similarly, you might ask why you can't use malloc()
to allocate memory, placement new to construct an object, and then delete
to delete and free the memory. The reason is that you must match malloc()
and free()
, not malloc()
and delete
.
类似地,您可能会问为什么不能使用malloc()来分配内存,使用placement来构造对象,然后删除以删除和释放内存。原因是你必须匹配malloc()和free(),而不是malloc()和delete。
In the real world, placement new and explicit destructor calls are almost never used. They might be used internally by the Standard Library implementation (or for other systems-level programming as noted in the comments), but normal programmers don't use them. I have never used such tricks for production code in many years of doing C++.
在现实世界中,几乎从不使用放置新的和显式的析构函数调用。它们可能由标准库实现在内部使用(或者在注释中指出的其他系统级编程),但普通程序员不使用它们。在使用C ++的许多年里,我从未在生产代码中使用过这些技巧。
#3
4
You need to distinguish between the delete
operator and operator delete
. In particular, if you're using placement new, you explicitly invoke the destructor and then call operator delete
(and not the delete
operator) to release the memory, i.e.
您需要区分删除操作符和操作符删除。特别是,如果您使用的是placement new,则显式调用析构函数,然后调用operator delete(而不是delete运算符)来释放内存,即
X *x = static_cast<X*>(::operator new(sizeof(X)));
new(x) X;
x->~X();
::operator delete(x);
Note that this uses operator delete
, which is lower-level than the delete
operator and doesn't worry about destructors (it's essentially a bit like free
). Compare this to the delete
operator, which internally does the equivalent of invoking the destructor and calling operator delete
.
请注意,这使用了operator delete,它比delete运算符更低级,并且不担心析构函数(它实际上有点像免费)。将此与delete运算符进行比较,该运算符在内部执行相当于调用析构函数和调用operator delete的操作。
It's worth noting that you don't have to use ::operator new
and ::operator delete
to allocate and deallocate your buffer - as far as placement new is concerned, it doesn't matter how the buffer comes into being / gets destroyed. The main point is to separate the concerns of memory allocation and object lifetime.
值得注意的是,您不必使用:: operator new和:: operator delete来分配和释放缓冲区 - 就放置new而言,缓冲区如何生成/被破坏无关紧要。重点是分离内存分配和对象生存期的问题。
Incidentally, a possible application of this would be in something like a game, where you might want to allocate a large block of memory up-front in order to carefully manage your memory usage. You'd then construct objects in the memory you've already acquired.
顺便提一下,这可能是一个类似于游戏的应用,你可能需要预先分配一大块内存,以便仔细管理你的内存使用情况。然后,您将在已经获得的内存中构造对象。
Another possible use would be in an optimized small, fixed-size object allocator.
另一种可能的用途是在优化的小型固定大小的对象分配器中。
#4
3
It's probably easier to understand if you imagine constructing several MyClass objects within one block of memory.
如果你想象在一个内存块中构造几个MyClass对象,可能更容易理解。
In that case, it would go something like:
在这种情况下,它会像:
- Allocate a giant block of memory using new char[10*sizeof(MyClass)] or malloc(10*sizeof(MyClass))
- Use placement new to construct ten MyClass objects within that memory.
- Do something.
- Call the destructor of each of your objects
- Deallocate the big block of memory using delete[] or free().
使用新的char [10 * sizeof(MyClass)]或malloc(10 * sizeof(MyClass))分配一个巨大的内存块
使用placement new在该内存中构造十个MyClass对象。
调用每个对象的析构函数
使用delete []或free()释放大块内存。
This is the sort of thing you might do if you're writing a compiler, or an OS, etc.
如果您正在编写编译器或操作系统等,那么您可能会这样做。
In this case, I hope it's clear why you need separate "destructor" and "delete" steps, because there's no reason you will call delete. However, you should deallocate the memory however you would normally do it (free, delete, do nothing for a giant static array, exit normally if the array is part of another object, etc, etc), and if you don't it'll be leaked.
在这种情况下,我希望很明显为什么你需要单独的“析构函数”和“删除”步骤,因为没有理由你会调用delete。但是,您应该释放内存但是通常会这样做(免费,删除,对巨型静态数组不执行任何操作,如果数组是另一个对象的一部分则正常退出,等等),如果不这样做,会被泄露。
Also note as Greg said, in this case, you can't use delete, because you allocated the array with new[] so you'd need to use delete[].
另请注意,正如Greg所说,在这种情况下,你不能使用delete,因为你用new []分配了数组,所以你需要使用delete []。
Also note that you need to assume you haven't overridden delete for MyClass, else it will do something totally different which is almost certainly incompatible with "new".
另请注意,您需要假设您没有覆盖MyClass的删除,否则它将执行完全不同的操作,这几乎肯定与“new”不兼容。
So I think you're unlikley to want to call "delete" as you describe, but could it ever work? I think this is basically the same question as "I have two unrelated types that don't have destructors. Can I new a pointer to one type, then delete that memory through a pointer to another type?" In other words, "does my compiler have one big list of all allocated stuff, or can it do different things for different types".
所以我认为你想要称之为“删除”是不可思议的,但它能不能起作用?我认为这基本上是同一个问题,“我有两个不相关的类型,没有析构函数。我可以新建一个指向一个类型的指针,然后通过指向另一个类型的指针删除该内存吗?”换句话说,“我的编译器是否有一个包含所有已分配内容的大列表,或者它可以针对不同类型执行不同的操作”。
I'm afraid I'm not sure. Reading the spec it says:
我担心我不确定。阅读规范说:
5.3.5 ... If the static type of the operand [of the delete operator] is different from its dynamic type, the static type shall be a base class of the operand's dynamic type and the static type shall have a virtual destructor or the behaviour is undefined.
5.3.5 ...如果[删除运算符]的操作数的静态类型与其动态类型不同,则静态类型应为操作数的动态类型的基类,静态类型应具有虚拟析构函数或者行为未定义。
I think that means "If you use two unrelated types, it doesn't work (it's ok to delete a class object polymorphically through a virtual destructor)."
我认为这意味着“如果你使用两个不相关的类型,它就不起作用(可以通过虚拟析构函数以多态方式删除一个类对象)。”
So no, don't do that. I suspect it may often work in practice, if the compiler does look solely at the address and not the type (and neither type is a multiple-inheritance class, which would mangle the address), but don't try it.
所以不,不要那样做。我怀疑它可能经常在实践中工作,如果编译器确实只查看地址而不是类型(并且这两个类型都不是多继承类,这会破坏地址),但不要尝试它。
#1
34
Using the new
operator does two things, it calls the function operator new
which allocates memory, and then it uses placement new, to create the object in that memory. The delete
operator calls the object's destructor, and then calls operator delete
. Yeah, the names are confusing.
使用new运算符做两件事,它调用函数operator new分配内存,然后使用placement new,在该内存中创建对象。 delete运算符调用对象的析构函数,然后调用operator delete。是的,这些名字令人困惑。
//normal version calls these two functions
MyClass* pMemory = new MyClass; void* pMemory = operator new(sizeof(MyClass));
MyClass* pMyClass = new( pMemory ) MyClass();
//normal version calls these two functions
delete pMemory; pMyClass->~MyClass();
operator delete(pMemory);
Since in your case, you used placement new manually, you also need to call the destructor manually. Since you allocated the memory manually, you need to release it manually. Remember, if you allocate with new
, there must be a coressponding delete
. Always.
因为在您的情况下,您手动使用了新的放置,您还需要手动调用析构函数。由于您手动分配了内存,因此需要手动释放它。请记住,如果您使用new分配,则必须有一个同步删除。总是。
However, placement new is designed to work with internal buffers as well (and other scenarios), where the buffers were not allocated with operator new
, which is why you shouldn't call operator delete
on them.
但是,placement new设计用于内部缓冲区(以及其他方案),其中缓冲区未使用operator new分配,这就是为什么不应该对它们调用operator delete。
#include <type_traits>
struct buffer_struct {
std::aligned_storage<sizeof(MyClass)>::type buffer;
};
int main() {
buffer_struct a;
MyClass* pMyClass = new (&a.buffer) MyClass(); //created inside buffer_struct a
//stuff
pMyClass->~MyClass(); //can't use delete, because there's no `new`.
return 0;
}
The purpose of the buffer_struct
class is to create and destroy the storage in whatever way, while main
takes care of the construction/destruction of MyClass
, note how the two are (almost*) completely separate from each other.
buffer_struct类的目的是以任何方式创建和销毁存储,而main负责MyClass的构造/销毁,注意两者是如何(几乎*)完全相互分离的。
*we have to be sure the storage has to be big enough
*我们必须确保存储空间足够大
#2
9
One reason this is wrong:
这是错误的一个原因:
delete pMyClass;
is that you must delete pMemory
with delete[]
since it is an array:
是你必须用delete []删除pMemory,因为它是一个数组:
delete[] pMemory;
You can't do both of the above.
你无法做到以上两点。
Similarly, you might ask why you can't use malloc()
to allocate memory, placement new to construct an object, and then delete
to delete and free the memory. The reason is that you must match malloc()
and free()
, not malloc()
and delete
.
类似地,您可能会问为什么不能使用malloc()来分配内存,使用placement来构造对象,然后删除以删除和释放内存。原因是你必须匹配malloc()和free(),而不是malloc()和delete。
In the real world, placement new and explicit destructor calls are almost never used. They might be used internally by the Standard Library implementation (or for other systems-level programming as noted in the comments), but normal programmers don't use them. I have never used such tricks for production code in many years of doing C++.
在现实世界中,几乎从不使用放置新的和显式的析构函数调用。它们可能由标准库实现在内部使用(或者在注释中指出的其他系统级编程),但普通程序员不使用它们。在使用C ++的许多年里,我从未在生产代码中使用过这些技巧。
#3
4
You need to distinguish between the delete
operator and operator delete
. In particular, if you're using placement new, you explicitly invoke the destructor and then call operator delete
(and not the delete
operator) to release the memory, i.e.
您需要区分删除操作符和操作符删除。特别是,如果您使用的是placement new,则显式调用析构函数,然后调用operator delete(而不是delete运算符)来释放内存,即
X *x = static_cast<X*>(::operator new(sizeof(X)));
new(x) X;
x->~X();
::operator delete(x);
Note that this uses operator delete
, which is lower-level than the delete
operator and doesn't worry about destructors (it's essentially a bit like free
). Compare this to the delete
operator, which internally does the equivalent of invoking the destructor and calling operator delete
.
请注意,这使用了operator delete,它比delete运算符更低级,并且不担心析构函数(它实际上有点像免费)。将此与delete运算符进行比较,该运算符在内部执行相当于调用析构函数和调用operator delete的操作。
It's worth noting that you don't have to use ::operator new
and ::operator delete
to allocate and deallocate your buffer - as far as placement new is concerned, it doesn't matter how the buffer comes into being / gets destroyed. The main point is to separate the concerns of memory allocation and object lifetime.
值得注意的是,您不必使用:: operator new和:: operator delete来分配和释放缓冲区 - 就放置new而言,缓冲区如何生成/被破坏无关紧要。重点是分离内存分配和对象生存期的问题。
Incidentally, a possible application of this would be in something like a game, where you might want to allocate a large block of memory up-front in order to carefully manage your memory usage. You'd then construct objects in the memory you've already acquired.
顺便提一下,这可能是一个类似于游戏的应用,你可能需要预先分配一大块内存,以便仔细管理你的内存使用情况。然后,您将在已经获得的内存中构造对象。
Another possible use would be in an optimized small, fixed-size object allocator.
另一种可能的用途是在优化的小型固定大小的对象分配器中。
#4
3
It's probably easier to understand if you imagine constructing several MyClass objects within one block of memory.
如果你想象在一个内存块中构造几个MyClass对象,可能更容易理解。
In that case, it would go something like:
在这种情况下,它会像:
- Allocate a giant block of memory using new char[10*sizeof(MyClass)] or malloc(10*sizeof(MyClass))
- Use placement new to construct ten MyClass objects within that memory.
- Do something.
- Call the destructor of each of your objects
- Deallocate the big block of memory using delete[] or free().
使用新的char [10 * sizeof(MyClass)]或malloc(10 * sizeof(MyClass))分配一个巨大的内存块
使用placement new在该内存中构造十个MyClass对象。
调用每个对象的析构函数
使用delete []或free()释放大块内存。
This is the sort of thing you might do if you're writing a compiler, or an OS, etc.
如果您正在编写编译器或操作系统等,那么您可能会这样做。
In this case, I hope it's clear why you need separate "destructor" and "delete" steps, because there's no reason you will call delete. However, you should deallocate the memory however you would normally do it (free, delete, do nothing for a giant static array, exit normally if the array is part of another object, etc, etc), and if you don't it'll be leaked.
在这种情况下,我希望很明显为什么你需要单独的“析构函数”和“删除”步骤,因为没有理由你会调用delete。但是,您应该释放内存但是通常会这样做(免费,删除,对巨型静态数组不执行任何操作,如果数组是另一个对象的一部分则正常退出,等等),如果不这样做,会被泄露。
Also note as Greg said, in this case, you can't use delete, because you allocated the array with new[] so you'd need to use delete[].
另请注意,正如Greg所说,在这种情况下,你不能使用delete,因为你用new []分配了数组,所以你需要使用delete []。
Also note that you need to assume you haven't overridden delete for MyClass, else it will do something totally different which is almost certainly incompatible with "new".
另请注意,您需要假设您没有覆盖MyClass的删除,否则它将执行完全不同的操作,这几乎肯定与“new”不兼容。
So I think you're unlikley to want to call "delete" as you describe, but could it ever work? I think this is basically the same question as "I have two unrelated types that don't have destructors. Can I new a pointer to one type, then delete that memory through a pointer to another type?" In other words, "does my compiler have one big list of all allocated stuff, or can it do different things for different types".
所以我认为你想要称之为“删除”是不可思议的,但它能不能起作用?我认为这基本上是同一个问题,“我有两个不相关的类型,没有析构函数。我可以新建一个指向一个类型的指针,然后通过指向另一个类型的指针删除该内存吗?”换句话说,“我的编译器是否有一个包含所有已分配内容的大列表,或者它可以针对不同类型执行不同的操作”。
I'm afraid I'm not sure. Reading the spec it says:
我担心我不确定。阅读规范说:
5.3.5 ... If the static type of the operand [of the delete operator] is different from its dynamic type, the static type shall be a base class of the operand's dynamic type and the static type shall have a virtual destructor or the behaviour is undefined.
5.3.5 ...如果[删除运算符]的操作数的静态类型与其动态类型不同,则静态类型应为操作数的动态类型的基类,静态类型应具有虚拟析构函数或者行为未定义。
I think that means "If you use two unrelated types, it doesn't work (it's ok to delete a class object polymorphically through a virtual destructor)."
我认为这意味着“如果你使用两个不相关的类型,它就不起作用(可以通过虚拟析构函数以多态方式删除一个类对象)。”
So no, don't do that. I suspect it may often work in practice, if the compiler does look solely at the address and not the type (and neither type is a multiple-inheritance class, which would mangle the address), but don't try it.
所以不,不要那样做。我怀疑它可能经常在实践中工作,如果编译器确实只查看地址而不是类型(并且这两个类型都不是多继承类,这会破坏地址),但不要尝试它。