何时调用 C++ 析构函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10081429/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When is a C++ destructor called?
提问by Pat Murray
Basic Question: when does a program call a class' destructor method in C++? I have been told that it is called whenever an object goes out of scope or is subjected to a delete
基本问题:程序何时在 C++ 中调用类的析构函数方法?有人告诉我,只要对象超出范围或受到影响,就会调用它delete
More specific questions:
更具体的问题:
1) If the object is created via a pointer and that pointer is later deleted or given a new address to point to, does the object that it was pointing to call its destructor (assuming nothing else is pointing to it)?
1)如果对象是通过指针创建的,并且该指针后来被删除或被赋予一个新地址指向,它所指向的对象是否调用了它的析构函数(假设没有其他东西指向它)?
2) Following up on question 1, what defines when an object goes out of scope (not regarding to when an object leaves a given {block}). So, in other words, when is a destructor called on an object in a linked list?
2) 跟进问题 1,什么定义了对象何时超出范围(与对象何时离开给定的 {block} 无关)。那么,换句话说,什么时候对链表中的对象调用析构函数?
3) Would you ever want to call a destructor manually?
3) 你想手动调用析构函数吗?
采纳答案by David Schwartz
1) If the object is created via a pointer and that pointer is later deleted or given a new address to point to, does the object that it was pointing to call its destructor (assuming nothing else is pointing to it)?
1)如果对象是通过指针创建的,并且该指针后来被删除或被赋予一个新地址指向,它所指向的对象是否调用了它的析构函数(假设没有其他东西指向它)?
It depends on the type of pointers. For example, smart pointers often delete their objects when they are deleted. Ordinary pointers do not. The same is true when a pointer is made to point to a different object. Some smart pointers will destroy the old object, or will destroy it if it has no more references. Ordinary pointers have no such smarts. They just hold an address and allow you to perform operations on the objects they point to by specifically doing so.
这取决于指针的类型。例如,智能指针经常在删除对象时删除它们。普通指针没有。当指针指向不同的对象时也是如此。一些智能指针会销毁旧对象,或者如果它没有更多引用就会销毁它。普通指针没有这样的智能。它们只持有一个地址,并允许您通过专门这样做来对它们指向的对象执行操作。
2) Following up on question 1, what defines when an object goes out of scope (not regarding to when an object leaves a given {block}). So, in other words, when is a destructor called on an object in a linked list?
2) 跟进问题 1,什么定义了对象何时超出范围(与对象何时离开给定的 {block} 无关)。那么,换句话说,什么时候对链表中的对象调用析构函数?
That's up to the implementation of the linked list. Typical collections destroy all their contained objects when they are destroyed.
这取决于链表的实现。典型的集合在销毁时会销毁所有包含的对象。
So, a linked list of pointers would typically destroy the pointers but not the objects they point to. (Which may be correct. They may be references by other pointers.) A linked list specifically designed to contain pointers, however, might delete the objects on its own destruction.
因此,指针的链表通常会破坏指针,但不会破坏它们指向的对象。(这可能是正确的。它们可能被其他指针引用。)然而,专门设计用于包含指针的链表可能会在其自身销毁时删除对象。
A linked list of smart pointers could automatically delete the objects when the pointers are deleted, or do so if they had no more references. It's all up to you to pick the pieces that do what you want.
智能指针的链表可以在删除指针时自动删除对象,或者在它们没有更多引用时这样做。完全由您来挑选可以做您想做的事。
3) Would you ever want to call a destructor manually?
3) 你想手动调用析构函数吗?
Sure. One example would be if you want to replace an object with another object of the same type but don't want to free memory just to allocate it again. You can destroy the old object in place and construct a new one in place. (However, generally this is a bad idea.)
当然。一个例子是,如果你想用另一个相同类型的对象替换一个对象,但不想释放内存只是为了再次分配它。您可以就地销毁旧对象并就地构建新对象。(但是,通常这是一个坏主意。)
// pointer is destroyed because it goes out of scope,
// but not the object it pointed to. memory leak
if (1) {
Foo *myfoo = new Foo("foo");
}
// pointer is destroyed because it goes out of scope,
// object it points to is deleted. no memory leak
if(1) {
Foo *myfoo = new Foo("foo");
delete myfoo;
}
// no memory leak, object goes out of scope
if(1) {
Foo myfoo("foo");
}
回答by Jerry Coffin
Others have already addressed the other issues, so I'll just look at one point: do you ever want to manually delete an object.
其他人已经解决了其他问题,所以我只看一点:您是否想手动删除对象。
The answer is yes. @DavidSchwartz gave one example, but it's a fairlyunusual one. I'll give an example that's under the hood of what a lot of C++ programmers use all the time: std::vector
(and std::deque
, though it's not used quite as much).
答案是肯定的。@DavidSchwartz 举了一个例子,但这是一个相当不寻常的例子。我将举一个例子,说明很多 C++ 程序员一直使用的东西:(std::vector
而且std::deque
,虽然使用得并不多)。
As most people know, std::vector
will allocate a larger block of memory when/if you add more items than its current allocation can hold. When it does this, however, it has a block of memory that's capable of holding moreobjects than are currently in the vector.
正如大多数人所知,std::vector
当/如果您添加的项目超过其当前分配可以容纳的数量时,将分配更大的内存块。然而,当它这样做时,它有一个内存块,能够容纳比当前向量中更多的对象。
To manage that, what vector
does under the covers is allocate rawmemory via the Allocator
object (which, unless you specify otherwise, means it uses ::operator new
). Then, when you use (for example) push_back
to add an item to the vector
, internally the vector uses a placement new
to create an item in the (previously) unused part of its memory space.
为了管理这一点,vector
幕后工作是通过对象分配原始内存Allocator
(除非您另行指定,否则意味着它使用::operator new
)。然后,当您使用(例如)push_back
向 中添加项时vector
,向量在内部使用 aplacement new
在其内存空间(以前)未使用的部分中创建项。
Now, what happens when/if you erase
an item from the vector? It can't just use delete
-- that would release its entire block of memory; it needs to destroy one object in that memory without destroying any others, or releasing any of the block of memory it controls (for example, if you erase
5 items from a vector, then immediately push_back
5 more items, it's guaranteedthat the vector will notreallocate memory when you do so.
现在,当/如果你erase
是一个来自向量的项目会发生什么?它不能只是使用delete
——那样会释放它的整个内存块;它需要销毁该内存中的一个对象而不销毁任何其他对象,或释放它控制的任何内存块(例如,如果您erase
从向量中删除5 个项目,然后立即再添加push_back
5 个项目,则可以保证向量不会重新分配当你这样做时的记忆。
To do that, the vector directly destroys the objects in the memory by explicitly calling the destructor, notby using delete
.
为此,向量通过显式调用析构函数来直接销毁内存中的对象,而不是使用delete
.
If, perchance, somebody else were to write a container using contiguous storage roughly like a vector
does (or some variant of that, like std::deque
really does), you'd almost certainly want to use the same technique.
或许,如果其他人大致像 a vector
do(或它的某种变体,就像std::deque
真的那样)使用连续存储编写容器,那么您几乎肯定会想要使用相同的技术。
Just for example, let's consider how you might write code for a circular ring-buffer.
例如,让我们考虑如何为循环环形缓冲区编写代码。
#ifndef CBUFFER_H_INC
#define CBUFFER_H_INC
template <class T>
class circular_buffer {
T *data;
unsigned read_pos;
unsigned write_pos;
unsigned in_use;
const unsigned capacity;
public:
circular_buffer(unsigned size) :
data((T *)operator new(size * sizeof(T))),
read_pos(0),
write_pos(0),
in_use(0),
capacity(size)
{}
void push(T const &t) {
// ensure there's room in buffer:
if (in_use == capacity)
pop();
// construct copy of object in-place into buffer
new(&data[write_pos++]) T(t);
// keep pointer in bounds.
write_pos %= capacity;
++in_use;
}
// return oldest object in queue:
T front() {
return data[read_pos];
}
// remove oldest object from queue:
void pop() {
// destroy the object:
data[read_pos++].~T();
// keep pointer in bounds.
read_pos %= capacity;
--in_use;
}
// release the buffer:
~circular_buffer() { operator delete(data); }
};
#endif
Unlike the standard containers, this uses operator new
and operator delete
directly. For real use, you probably do want to use an allocator class, but for the moment it would do more to distract than contribute (IMO, anyway).
与标准容器不同,它直接使用operator new
和operator delete
。对于实际使用,您可能确实想使用分配器类,但目前它会更多地分散注意力而不是贡献(无论如何,IMO)。
回答by dasblinkenlight
- When you create an object with
new
, you are responsible for callingdelete
. When you create an object withmake_shared
, the resultingshared_ptr
is responsible for keeping count and callingdelete
when the use count goes to zero. - Going out of scope does mean leaving a block. This is when the destructor is called, assuming that the object was notallocated with
new
(i.e. it is a stack object). - About the only time when you need to call a destructor explicitly is when you allocate the object with a placement
new
.
- 当您创建一个对象时
new
,您负责调用delete
. 当您创建一个对象时make_shared
,结果shared_ptr
负责保持计数并delete
在使用计数变为零时调用。 - 超出范围确实意味着离开一个块。这是在调用析构函数时,假设对象没有被分配
new
(即它是一个堆栈对象)。 - 大约唯一需要显式调用析构函数的时间是为对象分配一个放置
new
。
回答by Nathaniel Ford
1) Objects are not created 'via pointers'. There is a pointer that is assigned to any object you 'new'. Assuming this is what you mean, if you call 'delete' on the pointer, it will actually delete (and call the destructor on) the object the pointer dereferences. If you assign the pointer to another object there will be a memory leak; nothing in C++ will collect your garbage for you.
1) 对象不是“通过指针”创建的。有一个指针分配给您“新建”的任何对象。假设这就是您的意思,如果您在指针上调用“删除”,它实际上会删除(并在其上调用析构函数)指针取消引用的对象。如果将指针分配给另一个对象,则会出现内存泄漏;C++ 中的任何内容都不会为您收集垃圾。
2) These are two separate questions. A variable goes out of scope when the stack frame it's declared in is popped off the stack. Usually this is when you leave a block. Objects in a heap never go out of scope, though their pointers on the stack may. Nothing in particular guarantees that a destructor of an object in a linked list will be called.
2) 这是两个不同的问题。当变量在其中声明的堆栈帧从堆栈中弹出时,它就会超出范围。通常这是你离开一个街区的时候。堆中的对象永远不会超出范围,尽管它们在堆栈上的指针可能会。没有特别保证会调用链表中对象的析构函数。
3) Not really. There may be Deep Magic that would suggest otherwise, but typically you want to match up your 'new' keywords with your 'delete' keywords, and put everything in your destructor necessary to make sure it properly cleans itself up. If you don't do this, be sure to comment the destructor with specific instructions to anyone using the class on how they should clean up that object's resources manually.
3) 不是真的。Deep Magic 可能会提出不同的建议,但通常您希望将“新”关键字与“删除”关键字相匹配,并将所有必要的内容放入析构函数中,以确保它正确地自我清理。如果您不这样做,请务必向使用该类的任何人说明如何手动清理该对象的资源,并使用特定说明对析构函数进行注释。
回答by Stuart Golodetz
To give a detailed answer to question 3: yes, there are (rare) occasions when you might call the destructor explicitly, in particular as the counterpart to a placement new, as dasblinkenlight observes.
给出问题 3 的详细答案:是的,在(极少数)情况下,您可能会显式调用析构函数,特别是作为放置 new 的对应物,如 dasblinkenlight 所观察到的。
To give a concrete example of this:
举一个具体的例子:
#include <iostream>
#include <new>
struct Foo
{
Foo(int i_) : i(i_) {}
int i;
};
int main()
{
// Allocate a chunk of memory large enough to hold 5 Foo objects.
int n = 5;
char *chunk = static_cast<char*>(::operator new(sizeof(Foo) * n));
// Use placement new to construct Foo instances at the right places in the chunk.
for(int i=0; i<n; ++i)
{
new (chunk + i*sizeof(Foo)) Foo(i);
}
// Output the contents of each Foo instance and use an explicit destructor call to destroy it.
for(int i=0; i<n; ++i)
{
Foo *foo = reinterpret_cast<Foo*>(chunk + i*sizeof(Foo));
std::cout << foo->i << '\n';
foo->~Foo();
}
// Deallocate the original chunk of memory.
::operator delete(chunk);
return 0;
}
The purpose of this kind of thing is to decouple memory allocation from object construction.
这种事情的目的是将内存分配与对象构造解耦。
回答by chrisaycock
Pointers-- Regular pointers don't support RAII. Without an explicit
delete
, there will be garbage. Fortunately C++ has auto pointersthat handle this for you!Scope-- Think of when a variable becomes invisibleto your program. Usually this is at the end of
{block}
, as you point out.Manual destruction-- Never attempt this. Just let scope and RAII do the magic for you.
指针——常规指针不支持 RAII。没有明确的
delete
,就会有垃圾。幸运的是,C++ 有自动指针为你处理这个问题!作用域——想想一个变量何时对你的程序不可见。
{block}
正如您所指出的,通常这是在 的末尾。手动销毁——切勿尝试这样做。就让 scope 和 RAII 为您施展魔法吧。
回答by cloudygoose
Whenever you use "new", that is, attach an address to a pointer, or to say, you claim space on the heap, you need to "delete" it.
1.yes, when you delete something, the destructor is called.
2.When the destructor of the linked list is called, it's objects' destructor is called. But if they are pointers, you need to delete them manually.
3.when the space is claimed by "new".
每当您使用“new”,即将地址附加到指针时,或者说,您在堆上声明空间时,您需要“删除”它。
1.是的,当您删除某些内容时,会调用析构函数。
2.当链表的析构函数被调用时,它的对象的析构函数被调用。但是如果它们是指针,则需要手动删除它们。3.当空间被“新”占用时。
回答by tnecniv
Yes, a destructor (a.k.a. dtor) is called when an object goes out of scope if it is on the stack or when you call delete
on a pointer to an object.
是的,当对象超出范围(如果它在堆栈上)或调用delete
指向对象的指针时,会调用析构函数(又名 dtor)。
If the pointer is deleted via
delete
then the dtor will be called. If you reassign the pointer without callingdelete
first, you will get a memory leak because the object still exists in memory somewhere. In the latter instance, the dtor is not called.A good linked list implementation will call the dtor of all objects in the list when the list is being destroyed (because you either called some method to destory it or it went out of scope itself). This is implementation dependent.
I doubt it, but I wouldn't be surprised if there is some odd circumstance out there.
如果通过删除指针
delete
,则将调用 dtor。如果在没有delete
先调用的情况下重新分配指针,则会出现内存泄漏,因为该对象仍然存在于内存中的某处。在后一种情况下,不会调用 dtor。一个好的链表实现会在列表被销毁时调用列表中所有对象的 dtor(因为你要么调用了一些方法来销毁它,要么它本身就超出了范围)。这是依赖于实现的。
我对此表示怀疑,但如果那里有一些奇怪的情况,我不会感到惊讶。
回答by wyx
If the object is created not via a pointer(for example,A a1 = A();),the destructor is called when the object is destructed, always when the function where the object lies is finished.for example:
如果对象不是通过指针创建的(例如,A a1 = A();),则在对象被析构时调用析构函数,总是在对象所在的函数完成时调用。例如:
void func()
{
...
A a1 = A();
...
}//finish
the destructor is called when code is execused to line "finish".
If the object is created via a pointer(for example,A * a2 = new A();),the destructor is called when the pointer is deleted(delete a2;).If the point is not deleted by user explictly or given a new address before deleting it, the memory leak is occured. That is a bug.
In a linked list, if we use std::list<>, we needn't care about the desctructor or memory leak because std::list<> has finished all of these for us. In a linked list written by ourselves, we should write the desctructor and delete the pointer explictly.Otherwise, it will cause memory leak.
We rarely call a destructor manually. It is a function providing for the system.
Sorry for my poor English!
当代码被执行以行“完成”时调用析构函数。
如果对象是通过指针创建的(例如,A * a2 = new A();),则在删除指针时调用析构函数(删除 a2;)。如果该点没有被用户明确删除或给出删除之前的新地址,就会发生内存泄漏。那是一个错误。
在链表中,如果我们使用 std::list<>,我们不需要关心析构函数或内存泄漏,因为 std::list<> 已经为我们完成了所有这些。在我们自己写的链表中,我们应该写析构函数并显式删除指针,否则会导致内存泄漏。
我们很少手动调用析构函数。它是为系统提供的功能。
对不起我糟糕的英语!
回答by Sunny Khandare
Remember that Constructor of an object is called immediately after the memory is allocated for that object and whereas the destructor is called just before deallocating the memory of that object.
请记住,在为该对象分配内存后立即调用对象的构造函数,而在释放该对象的内存之前调用析构函数。