3

我正在将一些旧代码从 C 移植到 C++。旧代码使用类似对象的语义,并且在某一时刻将对象销毁与释放现在未使用的内存分开,两者之间发生了一些事情:

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

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

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

在特定情况下,operator deleteofObject也会被覆盖。我在其他地方读到的定义(“当使用运算符删除,并且对象有一个析构函数时,总是调用析构函数)在重写运算符的情况下是否正确?

4

9 回答 9

6

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

换句话说,使用 C++ 的析构函数和删除运算符是不可能实现您所针对的语义的。

样品

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

输出:

析构函数

运算符删除(不显式调用析构函数)

删除后

于 2009-11-16T15:04:52.440 回答
3

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

于 2009-11-16T15:27:06.310 回答
2

在对象的销毁和对象内存的释放之间发生了什么样的事情?如果它与对象无关,那么您应该可以删除析构函数出现的对象。如果确实如此,那么我会非常仔细地检查,因为这听起来是个坏主意。

如果您必须重现语义,请拥有一个释放所有资源的成员函数,并使用它而不是 destruct 函数。确保可以安全地多次调用该函数,并将其包含在 C++ 析构函数中以确保安全。

于 2009-11-16T15:33:35.983 回答
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;

实际上,由于析构函数永远不应该抛出并且不返回任何东西,因此将 thecleanup和 thedestruction分开以使cleanup操作可能报告错误一点也不稀奇。特别是对于 DB Connections 等复杂的野兽......

虽然这不是“析构函数”,但它确实有效,因此所呈现的 C 代码实际上是完全可重现的,没有那些花哨的放置 new 等......

于 2009-11-17T10:08:53.500 回答
1

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

如果您使用new char[]or分配内存malloc,然后调用placement new,那么您可以将销毁(通过直接调用析构函数)与删除(或free)分开。但是你不再调用类的重载operator delete,而是调用delete[]char 数组(或free)。

如果您通过指向您的类的指针(您重载运算符 delete 的那个)调用 delete,则将调用该类的析构函数。因此,没有办法按照您的要求将它们分开,即在没有析构函数的情况下调用 delete 。

于 2009-11-16T15:28:28.303 回答
1

不,这是不可能的。

delete调用析构函数。

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

于 2009-11-16T15:29:48.630 回答
0

看看 std::allocators 的实现。答案是“是的,它们可能是解耦的”。这很容易做到,只是很少见。

于 2009-11-16T15:31:34.147 回答
-1

听起来您可能想要一个展示位置 new。另一方面,听起来您的代码也变得很麻烦。可能是时候进行一些重大的重构了。

于 2009-11-16T15:17:03.320 回答
-1

析构函数与 delete 耦合,你不能调用它两次,因为你不能明确地调用析构函数(或者至少它是非常不寻常和不常见的,我从未见过它)。

但是,只需将 object_destructor() 设为成员函数并显式调用它(通常,确保被调用两次的安全性是一种很好的方式。但是,在您的情况下,调用两次是可以的,因为使用 NULL 指针调用 free() 是合法,所以 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 */
} 
于 2009-11-17T11:43:35.233 回答