当被称为C ++的析构函数?当被称为C ++的析构函数?(When is a C++ destruc

2019-05-13 11:16发布

基本问题:什么时候一个程序中调用C ++中的类的析构函数的方法? 有人告诉我,这是当一个对象超出范围或叫经受delete

更具体的问题:

1)如果通过指针创建的对象和指针后删除或赋予新的地址指向,的确,它是指向调用析构函数(假设没有其他指向它)的对象呢?

2)对问题1的后续行动,当一个对象超出范围什么定义(没有关于何时对象离开给定的{}块来)。 所以,换句话说,如果是在一个链表在对象上调用析构函数?

3)你是否曾经想手动调用析构函数?

Answer 1:

1)如果通过指针创建的对象和指针后删除或赋予新的地址指向,的确,它是指向调用析构函数(假设没有其他指向它)的对象呢?

这取决于指针的类型。 例如,当它们被删除智能指针经常删除自己的对象。 普通指针不会。 当指针被制成指向一个不同的对象也是如此。 一些智能指针会破坏旧的对象,还是会破坏它,如果它没有更多的参考。 普通指针有没有这样的智慧。 他们只是抱着一个地址,并允许您通过专门为此进行对它们指向的对象的操作。

2)对问题1的后续行动,当一个对象超出范围什么定义(没有关于何时对象离开给定的{}块来)。 所以,换句话说,如果是在一个链表在对象上调用析构函数?

这是到链表的实现。 典型的集合摧毁所有及其包含的对象,他们被摧毁时。

因此,指针链表通常会破坏指针,但是没有它们指向的对象。 (这可能是正确的。他们可能是通过其他指针引用。)专为包含指针,不过,可能会删除自身毁灭的对象链表。

智能指针链表可以自动删除的对象时,指针被删除,或者这样做,如果他们没有更多的参考。 这一切都取决于你选择你想要什么做的作品。

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");
}


Answer 2:

别人已经解决的其他问题,所以我就看一点:你曾经想要手动删除的对象。

答案是肯定的。 @DavidSchwartz给了一个例子,但它是一个相当不寻常的。 我给的是什么了很多C ++程序员使用所有的时间引擎盖下是一个例子: std::vector (和std::deque ,虽然它不使用相当多)。

由于大多数人都知道, std::vector将分配的,当/如果你添加更多的项目比它当前的分配可以容纳内存较大的块。 当它这样做,但是,它具有的内存比目前在向量是能够容纳更多的对象块。

要管理,什么vector在幕后所做的是通过分配原始内存Allocator对象(其中,除非另行指定,意味着它使用::operator new )。 然后,当你使用(例如) push_back将项目添加到vector ,内部的载体采用了placement new在它的内存空间(先前)未使用部分,以创建项目。

现在,当/如果你发生了什么erase从向量中的项目? 它不能只用delete -这将释放内存的整个块; 它需要破坏在该存储器中的一个对象,而不破坏任何其它,或释放任何的存储器其控制块(例如,如果erase从载体5级的物品,然后立即push_back 5个更多的项目,它保证该载体将当你这样做不是重新分配内存。

要做到这一点,矢量直接通过显式调用析构函数,而不是使用会破坏内存中的对象delete

如果,或许,别人都写在使用相邻的存储大致是这样的收纳vector做(或者是其它的变种,如std::deque确实),你几乎可以肯定要使用相同的技术。

只是举例,让我们考虑如何编写代码的圆环缓冲区。

#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直接。 对于真正的使用,你可能想使用一个allocator类,但目前会做更多的注意力比贡献(IMO,反正)。



Answer 3:

  1. 当你创建一个对象new ,你是负责调用delete 。 当你创建一个对象make_shared ,所产生shared_ptr负责保持计数,并要求delete的时候使用计数为零。
  2. 走出去的范围并不意味着离开块。 这是当调用析构函数,假设对象不是使用分配new (即它是一个栈对象)。
  3. 关于当你需要调用析构函数的唯一一次明确是当你与一个分配对象放置new


Answer 4:

1)对象是不是“通过指针”创建的。 有一个分配给你的“新”任何对象的指针。 假设这是你的意思,如果你所说的“删除”上的指针,它实际上将删除(并调用析构函数)的对象的指针引用。 如果分配指针到另一个对象会出现内存泄漏; 没有在C ++会收集你的垃圾给你。

2)这些是两个单独的问题。 一种可变超出范围时的堆栈帧它的中弹出栈声明。 通常这是当你离开一个块。 在堆中的对象从来没有走出去的范围,虽然他们在堆栈指针的可能。 没有什么特别保证在一个链表对象的析构函数将被调用。

3)不是真的。 可能有冥神功,否则建议,但通常你想你的“新”的关键字与“删除”的关键字匹配,将所有在必要时将您的析构函数,以确保它正确地清理自己的身体。 如果你不这样做,一定要与特定的指令析构函数发表评论我要使用类的人他们应该怎样手动清理对象的资源。



Answer 5:

为了给出一个详细的解答问题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;
}

这种事情的目的是从对象构造解耦内存分配。



Answer 6:

  1. 指针 -普通指针不支持RAII。 如果没有一个明确的delete ,就会有垃圾。 幸运的是C ++有自动指针是处理您的问题!

  2. 范围 -认为当一个变量变为不可见的程序的。 这通常是在月底{block} ,正如你指出。

  3. 手册破坏 -不要尝试这个。 就让范围和RAII做魔术给你。



Answer 7:

当你使用“新”,就是附加一个地址的指针,或者说,你要求的堆空间,你需要“删除”了。
1.是,当你删除的东西,析构函数被调用。
2.当链表的析构函数被调用时,它的对象的析构函数被调用。 但是,如果他们是指针,你需要手动删除它们。 3.当空间被“新”的要求。



Answer 8:

是的,析构函数(又名析构函数)是当一个对象超出范围,如果它是在栈上调用时调用delete一个指向对象的指针。

  1. 如果指针是通过删除delete ,则析构函数将被调用。 如果重新分配的指针,而不调用delete第一个,你会得到一个内存泄漏,因为这个对象依然存在于内存中的某个地方。 在后一种情况下,析构函数不叫。

  2. 一个良好的链表实现将调用时被销毁清单列表中的所有对象的析构函数(因为你要么叫一些方法destory它或它出去的范围本身)。 这是实现相关。

  3. 我怀疑这一点,但如果有一些奇怪的情况在那里我不会感到惊讶。



Answer 9:

如果通过指针不创建的对象(例如,A A1 = A();),析构函数被调用时,目的是破坏,总是当其中对象位于功能是finished.for例如:

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


当代码execused排队“完成”析构函数被调用。

如果对象是通过指针创建(例如,A * A2 =新的A();),当指针被删除析构函数被调用(删除A2)。如果点不被用户删除显式地或给予删除前的新地址,内存泄漏发生。 这是一个错误。

在一个链表,如果我们使用std ::列表<>,我们没有必要因为的std ::列表<>已完成所有的这些对我们关心的desctructor或内存泄漏。 在我们自己写的链表,我们应该写的desctructor并删除指针explictly.Otherwise,就会造成内存泄漏。

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

对不起,我的英语不好!



Answer 10:

请记住一个对象,它调用构造函数只是重新分配该对象的内存之前内存分配给该对象后立即,而析构函数被调用。



文章来源: When is a C++ destructor called?