析构函数是否总是被删除运算符调用,即使它已重载?

发布于 2024-08-11 06:42:18 字数 486 浏览 3 评论 0原文

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

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

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

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

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

I'm porting a bit of an old code from C to C++. The old code uses object-like semantics, and at one point separates object destruction from freeing the now-unused memory, with stuff happening in between:

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

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

Is the above functionality possible in C++ using the standard destructor (~Object) and a subsequent call to delete obj? Or, as I fear, doing that would call the destructor twice?

In the particular case, the operator delete of Object is overridden as well. Is the definition I've read elsewhere ("when operator delete is used, and the object has a destructor, the destructor is always called) correct in the overridden operator case?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

吹梦到西洲 2024-08-18 06:42:18

删除运算符用于释放内存,无论析构函数是否被调用,它都不会改变。首先调用析构函数,然后才使用删除运算符来释放内存。

换句话说,使用 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;
}

输出:

析构函数

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

删除后

The delete operator is used to free memory, it doesn't change whether the destructor is called or not. First the destructor is called, and only after that is the delete operator used to deallocate the memory.

In other words it's not possible to achieve the semantics you're aiming at with C++'s destructors and delete operators.

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

Output:

destructor

operator delete (not explicitly calling destructor)

After delete

心如荒岛 2024-08-18 06:42:18

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

Overloaded delete still calls destructor implicitly before it starts executing as opposed to placement delete (but placement delete is not supposed to be called directly).
So if you are going to "delete" object, do not destroy it in advance you will have destructor called twice. However explicit destruction is due if object was created with placement new (but in that case you do not destroy object using delete)

白色秋天 2024-08-18 06:42:18

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

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

What sort of stuff happens between the destruction of the object and the freeing of the object's memory? If it has nothing to do with the object, then you should be able to delete the object where the destructor appears. If it does, well, I'd examine that very carefully, because it sounds like a bad idea.

If you have to reproduce the semantics, have a member function that releases all the resources, and use that instead of the destruct function. Make sure that function can be called more than once safely, and include it in the C++ destructor just to be sure.

緦唸λ蓇 2024-08-18 06:42:18

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

将初始化与构造以及归零(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 操作可能会报告错误。特别是对于像数据库连接等复杂的野兽...

虽然这不是一个“析构函数”的东西,但它确实有效,因此所提供的 C 代码实际上是可以完美重现的,而无需那些花哨的放置 new 等...

I absolutely don't get why people say it's impossible.

Decoupling initialization from construction and zeroization (tm) from destruction is actually extremely simple.

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

Then you can use it like so:

Clike* c = new Clike();

c->clear(); // performs cleanup

// stuff

delete c;

Actually, since destructors should never throw and do not return anything, it is not unusual at all that the cleanup and the destruction be separated so that the cleanup operation may report errors. Especially for complicated beasts like DB Connections etc...

While this is not a 'destructor' thing, it sure works, and so the C-code presented is actually perfectly reproducible without those fancy placement new etc...

百变从容 2024-08-18 06:42:18

您可以将破坏与删除分开,但您可能并不真正想要这样做。

如果您使用 new char[]malloc 分配内存,然后调用placement new,那么您可以将销毁(通过直接调用析构函数来完成)与删除分开(或免费)。但是,您不再调用该类的重载运算符delete,而是在char数组上调用delete[](或free) )。

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

You can separate destruction from deletion, but you probably don't really want to.

If you allocate the memory with new char[] or malloc, and then call placement new, then you can separate destruction (which you do by directly calling the destructor) from deletion (or free). But then you're no longer calling the class's overloaded operator delete, instead you're calling delete[] on the char array (or free).

If you call delete via a pointer to your class (the one you overloaded operator delete for), then that class's destructor will be called. So there is no way to separate them in the sense you ask for, of calling delete without the destructor.

孤星 2024-08-18 06:42:18

不,这是不可能的。

delete 调用析构函数。

您需要制定某种逻辑来确保事情按正确的顺序发生。

No, it is not possible.

delete calls the destructor.

You will need to work out some kind of logic to ensure that Stuff happens in the right order.

甩你一脸翔 2024-08-18 06:42:18

查看 std::allocators 的实现。答案是“是的,它们可能是分离的”。这很容易做到,只是不常见。

Have a look at the std::allocators for the implementation. The answer is 'yes, they may be decoupled'. It's quite easy to do, it just not seen very often.

卖梦商人 2024-08-18 06:42:18

听起来您可能想要一个新展示位置。另一方面,听起来你的代码也变得非常复杂。也许是时候进行一些重大重构了。

It sounds like you might want a placement new. On the other hand, it also sounds like your code is getting pretty hairy. It might be time for some heavy refactoring.

残花月 2024-08-18 06:42:18

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

但是,只需将 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 */
} 

The destructor is coupled to delete and you can't call it twice because you can't explicitely call the destructor (or at least it is highly unusual and uncommon, I've never seen it).

However, just make object_destructor() a member function and call it explicitely (and usually it's good style to make it safe for being called twice. In your case however, calling twice is okay anyway, because calling free() with a NULL pointer is legal, so the alternate version of object_destructor is just to highlight how it could be done.

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 */
} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文