我正在将一些旧代码移植到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时,对象有一个析构函数,总是调用析构函数)在重写的运算符中是正确的吗?
答案 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;
实际上,由于析构函数不应该抛出并且不返回任何内容,因此将cleanup
和destruction
分开以使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 */
}