是否总是会为删除操作符调用析构函数,即使它已被重载?

时间:2009-11-16 15:01:17

标签: c++ operator-overloading destructor

我正在将一些旧代码移植到C到C ++。旧代码使用类似对象的语义,并且在某一时刻将对象破坏与释放现在未使用的内存分开,并在其间发生 stuff

Object_Destructor(Object *me) { free(me->member1), free(me->member2) }

ObjectManager_FreeObject(ObjectManager *me, Object *obj) { free(obj) }

使用标准析构函数(~Object)和随后调用delete obj在C ++中是否可以实现上述功能?或者,我担心,这样做会两次调用析构函数?

在特定情况下,operator delete的{​​{1}}也会被覆盖。我在其他地方读过的定义(“当使用operator delete时,对象有一个析构函数,总是调用析构函数)在重写的运算符中是正确的吗?

9 个答案:

答案 0 :(得分:6)

delete operator用于释放内存,它不会改变是否调用析构函数。首先调用析构函数,然后只有delete operator用于释放内存。

换句话说,用C ++的析构函数和删除操作符来实现你的目标是不可能的。

<强> Sample

#include <iostream>
#include <new>
using namespace std;

struct foo {
    ~foo() { cout << "destructor\n"; }
    void operator delete(void* p) { 
        cout << "operator delete (not explicitly calling destructor)\n"; 
        free(p);
        cout << "After delete\n"; 
    }
};

int main()
{
    void *pv = malloc(sizeof(foo));
    foo* pf = new (pv) foo; // use placement new
    delete pf;
}

<强>输出:

  

     

operator delete(不显式调用析构函数)

     

删除后

答案 1 :(得分:3)

重载delete在开始执行之前仍然隐式调用析构函数,而不是放置删除(但不应该直接调用placement delete)。 因此,如果您要“删除”对象,请不要提前销毁它,您将有两次调用析构函数。但是,如果使用placement new创建对象,则显式破坏是因为(但在这种情况下,您不使用delete来销毁对象)

答案 2 :(得分:2)

在对象的破坏和对象内存的释放之间发生了什么样的事情?如果它与对象无关,那么您应该能够删除析构函数出现的对象。如果确实如此,我会仔细检查,因为这听起来不错。

如果必须重现语义,请使用释放所有资源的成员函数,并使用该函数代替destruct函数。确保可以安全地调用该函数,并将其包含在C ++析构函数中以确保。

答案 3 :(得分:2)

我绝对不明白为什么人们说这是不可能的。

将构造和零化(tm)与破坏的初始化解耦实际上非常简单。

class Clike
{
public:
  Clike() : m_usable(true) {}

  void clear(); // performs clean up then sets m_usable to false
  ~Clike() { if (m_usable) this->clear(); }

private:
  bool m_usable;
  // real variables
};

然后你可以像这样使用它:

Clike* c = new Clike();

c->clear(); // performs cleanup

// stuff

delete c;

实际上,由于析构函数不应该抛出并且不返回任何内容,因此将cleanupdestruction分开以使cleanup操作可能报告错误并不罕见。特别是像DB Connections等复杂的野兽......

虽然这不是'析构函数',但它确实有效,因此所提供的C代码实际上是完全可重现的,没有那些花哨的位置新等...

答案 4 :(得分:1)

您可以将销毁与删除区分开来,但您可能并不想这样做。

如果使用new char[]malloc分配内存,然后调用placement new,则可以将删除(通过直接调用析构函数执行)与删除(或{{1})分开})。但是你不再调用类的重载free,而是在char数组(或operator delete)上调用delete[]

如果你通过指向你的类的指针(你重载的操作符删除的那个)调用delete,那么将调用该类的析构函数。因此,没有办法在你要求的意义上将它们分开,在没有析构函数的情况下调用delete。

答案 5 :(得分:1)

不,这是不可能的。

delete调用析构函数。

您需要制定某种逻辑以确保Stuff以正确的顺序发生。

答案 6 :(得分:0)

查看实现的std :: allocators。答案是“是的,他们可能会脱钩”。这很容易做到,只是经常看不到。

答案 7 :(得分:-1)

听起来你可能需要placement new。另一方面,它听起来像你的代码变得非常毛茸茸。可能是进行一些重型重构的时候了。

答案 8 :(得分:-1)

析构函数被绑定到删除,你不能再调用它两次,因为你不能明确地调用析构函数(或者至少它是非常不寻常和罕见的,我从来没有见过它。)

然而,只需使object_destructor()成为一个成员函数并明确地调用它(通常它是一个很好的样式,可以安全地被调用两次。但是,在你的情况下,无论如何,调用两次是好的,因为调用free() NULL指针是合法的,因此object_destructor的备用版本只是为了突出显示它是如何完成的。

CLASS A {
   object_destructor() { free(this->member1); free(this->member2); }

   alternate_version_of_object_destructor() {  
           if (this->member1) { free(this->member1); this->member1= NULL; } 
           if (this->member2) { free(this->member2); this->member2= NULL; } }

    ~A() { /* do nothing or just call this->object_destructor() for safety */ }
}


foo() {
    A *pa= new A;


    pa->object_destructor();  /* member cleanup */
    delete pa;                /* free object memory */
}