何时调用C ++析构函数?

基本问题:一个程序什么时候打电话给一个班级' C ++中的析构函数方法? 有人告诉我,只要一个物体超出范围或受到delete的影响就会被调用

更具体的问题:

1)如果通过指针创建对象并且稍后删除该指针或给出指向的新地址,那么它所指向的对象是否会调用其析构函数(假设没有其他指向它)?

2)关注问题1,什么定义了一个对象何时超出范围(不是关于对象何时离开给定的{block})。 那么,换句话说,什么时候是一个析构函数调用链表中的对象?

3)你想要手动调用析构函数吗?

Pat Murray asked 2019-08-13T14:39:47Z
10个解决方案
60 votes

1)如果通过指针创建对象并且稍后删除该指针或给出指向的新地址,那么它所指向的对象是否会调用其析构函数(假设没有其他指向它)?

这取决于指针的类型。 例如,智能指针通常在删除对象时删除它们。 普通指针没有。 当指针指向不同的对象时也是如此。 一些智能指针会破坏旧对象,或者如果它没有更多引用就会销毁它。 普通指针没有这样的智慧。 它们只保存一个地址,并允许您通过专门执行操作对它们指向的对象执行操作。

2)关注问题1,什么定义了一个对象何时超出范围(不是关于对象何时离开给定的{block})。 那么,换句话说,什么时候是一个析构函数调用链表中的对象?

这取决于链表的实现。 典型的集合在销毁时会销毁所有包含的对象。

因此,链接的指针列表通常会破坏指针,但不会破坏它们指向的对象。 (这可能是正确的。它们可能是其他指针的引用。)但是,专门设计用于包含指针的链表可能会删除自身销毁的对象。

智能指针的链接列表可以在删除指针时自动删除对象,如果没有更多引用,则可以自动删除对象。 一切都取决于你选择你想要的东西。

3)你想要手动调用析构函数吗?

当然。 一个例子是,如果你想用另一个相同类型的对象替换一个对象,但不想释放内存只是为了再次分配它。 您可以在适当的位置销毁旧对象并构建一个新对象。 (但是,通常这是一个坏主意。)

// 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");
}
David Schwartz answered 2019-08-13T14:40:48Z
13 votes

其他人已经解决了其他问题,所以我只想看一点:你是否想要手动删除一个对象。

答案是肯定的。 @DavidSchwartz举了一个例子,但这是一个相当不寻常的例子。 我将举例说明许多C ++程序员一直使用的内容:operator new(和operator delete,虽然它没有使用得那么多)。

正如大多数人所知,operator new会在/当你添加的项目超过其当前分配时可以分配更大的内存块。 但是,当它执行此操作时,它具有一块内存,它能够容纳比当前在向量中更多的对象。

为了管理它,operator new的内容是通过operator delete对象分配原始内存(除非另有说明,否则表示它使用erase)。 然后,当您使用(例如)push_back将项目添加到vector时,向量内部使用placement new在其内存空间的(先前)未使用部分中创建项目。

现在,如果您是operator new向量中的项目会发生什么? 它不能只使用operator delete - 这将释放其整个内存块; 它需要销毁该内存中的一个对象而不破坏任何其他对象,或释放它控制的任何内存块(例如,如果你从一个向量中的5个项目,那么立即push_back另外5个项目,它会得到保证 当你这样做时,向量不会重新分配内存。

为此,向量通过显式调用析构函数直接销毁内存中的对象,而不是使用operator new

如果,其他人使用连续存储来写一个容器大致就像operator new那样(或者某些变体,如operator delete确实如此),你几乎肯定想要使用相同的技术。

例如,让我们考虑如何为循环环形缓冲区编写代码。

#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

与标准容器不同,它直接使用operator newoperator delete。 对于实际使用,您可能确实想要使用分配器类,但目前它会分散注意力而不是贡献(IMO,无论如何)。

Jerry Coffin answered 2019-08-13T14:42:22Z
5 votes
  1. 当您使用new创建对象时,您负责调用delete.当您使用make_shared创建对象时,生成的shared_ptr负责在使用计数变为零时保持计数并调用delete
  2. 超出范围确实意味着留下障碍。 这是在调用析构函数时,假设该对象未使用new分配(即它是堆栈对象)。
  3. 关于唯一需要显式调用析构函数的时间是使用展示位置new分配对象时。
dasblinkenlight answered 2019-08-13T14:43:07Z
4 votes

1)不通过指针创建对象&#39;。 有一个指针分配给您新的&#39;。 假设这就是你的意思,如果你打电话'删除&#39; 在指针上,它实际上会删除(并调用析构函数)指针取消引用的对象。 如果将指针指定给另一个对象,则会发生内存泄漏; C ++中没有任何内容可以为您收集垃圾。

2)这是两个单独的问题。 当声明的堆栈帧从堆栈中弹出时,变量超出范围。 通常这是你离开一个街区。 堆中的对象永远不会超出范围,尽管它们在堆栈上的指针可能会。 没有什么特别保证将调用链表中对象的析构函数。

3)不是真的。 可能会有深魔术暗示其他情况,但通常你想要匹配你的新的&#39; 你的&#39;删除&#39;的关键字 关键字,并在您的析构函数中放置所有必要的东西,以确保它正确清理自己。 如果您不这样做,请务必向使用该类的任何人详细说明析构函数,以了解他们应如何手动清理该对象的资源。

Nathaniel Ford answered 2019-08-13T14:43:52Z
3 votes

为了给问题3提供详细的答案:是的,有一些(罕见的)你可以明确地调用析构函数,特别是作为一个新的位置对应,就像dasblinkenlight观察到的那样。

举一个具体的例子:

#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;
}

这种事情的目的是将内存分配与对象构造分离。

Stuart Golodetz answered 2019-08-13T14:44:37Z
2 votes
  1. 指针 - 常规指针不支持RAII。 没有明确的{block},就会有垃圾。 幸运的是,C ++有自动指针可以为您处理这个问题!

  2. 范围 - 考虑变量何时对程序不可见。 正如你所指出的,通常这是在{block}的末尾。

  3. 手动破坏 - 切勿尝试此操作。 让范围和RAII为您带来魔力。

chrisaycock answered 2019-08-13T14:45:27Z
1 votes

无论何时使用&#34; new&#34;,即将地址附加到指针,或者说,您在堆上声明空间,您需要&#34;删除&#34; 它。
1.yes,当你删除某些东西时,会调用析构函数。
2.当链接列表的析构函数被调用时,它的对象是&#39; 析构函数被称为。 但如果它们是指针,则需要手动删除它们。3.当空间由&#34; new&#34;声明时。

cloudygoose answered 2019-08-13T14:46:10Z
0 votes

是的,如果对象超出范围(如果它在堆栈上)或者在指向对象的指针上调用delete时,则会调用析构函数(a.k.a. dtor)。

  1. 如果通过delete删除指针,则将调用dtor。 如果首先重新分配指针而不调用delete,则会出现内存泄漏,因为该对象仍然存在于内存中。 在后一种情况下,不调用dtor。

  2. 当列表被销毁时,一个好的链表实现将调用列表中所有对象的dtor(因为你要么调用一些方法来破坏它,要么它超出了范围本身)。 这取决于实现。

  3. 我对此表示怀疑,但如果那里有一些奇怪的情况,我不会感到惊讶。

tnecniv answered 2019-08-13T14:47:06Z
0 votes

如果不是通过指针创建对象(例如,A a1 = A();),则在对象被破坏时调用析构函数,总是在对象所在的函数完成时调用。例如:

void func()
{
...
A a1 = A();
...
}//finish


当代码被执行到行&#34;完成&#34;时,将调用析构函数。

如果通过指针创建对象(例如,A * a2 = new A();),则在删除指针时调用析构函数(删除a2;)。如果该点未被用户明确删除或给定 删除之前的新地址,发生内存泄漏。 那是一个错误。

在链表中,如果我们使用std :: list&lt;&gt;,我们不需要关心析构函数或内存泄漏,因为std :: list&lt;&gt; 为我们完成了所有这些。 在我们自己编写的链表中,我们应该编写析构函数并明确地删除指针。否则会导致内存泄漏。

我们很少手动调用析构函数。 它是为系统提供的功能。

抱歉我的英语不好!

wyx answered 2019-08-13T14:48:13Z
0 votes

请记住,在为该对象分配内存后立即调用对象的构造函数,而在释放该对象的内存之前调用析构函数。

Sunny Khandare answered 2019-08-13T14:48:43Z
translate from https://stackoverflow.com:/questions/10081429/when-is-a-c-destructor-called