当前位置: 首页 > 知识库问答 >
问题:

什么时候调用C析构函数?

南宫俊喆
2023-03-14

基本问题:程序何时在C中调用类的析构函数方法?有人告诉我,每当对象超出范围或受到删除时都会调用它

更具体的问题:

1)如果对象是通过指针创建的,并且该指针后来被删除或给定一个新的地址来指向,它所指向的对象是否调用其析构函数(假设没有其他东西指向它)?

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

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

共有3个答案

狄旻
2023-03-14
  1. 使用new创建对象时,您负责调用delete。当您使用make_shared创建对象时,生成的shared_ptr负责保持计数,并在使用计数为零时调用delete
  2. 超出范围确实意味着离开一个街区。这是调用析构函数时的情况,假设对象未分配new(即它是堆栈对象)
  3. 大约唯一需要显式调用析构函数的时候,是在为对象分配位置new
谢俊悟
2023-03-14

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

答案是肯定的@戴维施瓦茨举了一个例子,但这是一个相当不寻常的例子。我将给出一个很多C程序员一直在使用的例子:std::vector(和std::deque,尽管使用得不多)。

正如大多数人所知,std::vector将在添加的项目超过当前分配的容量时分配更大的内存块。然而,当它这样做时,它有一块内存,能够容纳比当前向量中更多的对象。

为了管理这一点,vector在封面下做的是通过分配器对象分配原始内存(除非您另有指定,否则这意味着它使用::operator new)。然后,当您使用(例如)push_back将项目添加到向量时,向量在内部使用placement new在其内存空间(以前)未使用的部分创建项目。

现在,如果从向量中删除一个项目,会发生什么?它不能只使用删除——这会释放整个内存块;它需要销毁该内存中的一个对象,而不销毁任何其他对象,或者释放它控制的任何内存块(例如,如果从向量中擦除5项,然后立即推回5项,则保证向量在执行此操作时不会重新分配内存。

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

如果有人使用连续存储来编写容器,大概像向量那样(或者像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;
    }
  
~circular_buffer() {
    // first destroy any content
    while (in_use != 0)
        pop();

    // then release the buffer.
    operator delete(data); 
}

};

#endif

与标准容器不同,它直接使用操作员新建操作员删除。在实际使用中,您可能确实希望使用分配器类,但目前它所做的更多的是分散注意力,而不是贡献(无论如何,在我看来)。

纪正德
2023-03-14

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");
}
 类似资料:
  • 我有一些类,我可以不使用任何东西构建它,也可以使用。在销毁时,应该调用给定的函数(如果有)。我的问题是,该对象在创建后立即被销毁,并由我的函数返回,该函数在应该调用它之前调用。传递给构造函数的函数只能调用一次。一些示例代码: 程序输出 因此,函数在被调用之前被调用(在int main()的末尾)。 添加移动构造函数和赋值操作符后,一切都按预期工作: 以及程序输出 实际问题:第一次销毁对象以便调用t

  • 问题内容: 嗨,我想知道何时才是使用htmlspecialchars()的适当位置。是在将数据插入数据库之前还是从数据库中检索数据时? 问题答案: 仅在将数据回显为HTML时才应调用此方法。 不要将转义的HTML存储在数据库中;它只会使查询更烦人。 数据库应存储您的实际数据,而不是其HTML表示形式。

  • 问题内容: 我正在为嵌入式Linux系统编写用户应用程序,并且正在使用设备的常用功能,例如打开,关闭,读取,ioctl等。现在,我读到有关EINTR的信息,它表明该功能已被信号中断,但我不确定其中的含义。在我拥有的所有示例程序中,有时都完成了,例如ioctl(),有时没有完成,例如read()。所以,我有点困惑。 何时最好检查EINTR并重复函数调用? 问题答案: 参见sigaction:http

  • 本文向大家介绍什么时候用delegate,什么时候用Notification?相关面试题,主要包含被问及什么时候用delegate,什么时候用Notification?时的应答技巧和注意事项,需要的朋友参考一下 答:delegate针对one-to-one关系,并且reciever可以返回值 给sender,notification 可以针对one-to-one/many/none,recieve

  • 问题内容: 奇怪的是: 似乎或多或少被定义为。通过这种方式很容易产生错误: 一些fname意外地以else块结尾。修复很简单,我们应该改用它,但是从表面上看,这似乎是一种不错的pythonic方式,并且比“正确”的方式更具可读性。 由于字符串是不可变的,所以为什么字符串错误是什么技术细节?什么时候进行身份检查更好,什么时候进行平等检查更好? 问题答案: 据我所知,检查对象身份是否相等。由于没有强制

  • 问题内容: 我了解OOP语言(例如C ++)中的构造函数的概念。但是,我不确定何时在REACT中使用构造函数。我确实了解JavaScript是面向对象的,但是我不确定构造器实际上是在“构造”什么。 呈现子组件时,子组件中是否需要构造函数?例如: 为了简洁起见,我将简短示例。但是,为什么需要构造函数?您是否需要在子组件中使用一个构造函数来构造道具? 我的ES6知识很可能还没有达到标准。 问题答案: