我尝试调用::delete
中operator delete
中的一个类。但是不会调用析构函数。
我定义了一个类MyClass
,其operator delete
已被重载。全局operator delete
也超载。 operator delete
中重载的MyClass
将调用全局重载的operator delete
。
class MyClass
{
public:
MyClass() { printf("Constructing MyClass...\n"); }
virtual ~MyClass() { printf("Destroying MyClass...\n"); }
void* operator new(size_t size)
{
printf("Newing MyClass...\n");
void* p = ::new MyClass();
printf("End of newing MyClass...\n");
return p;
}
void operator delete(void* p)
{
printf("Deleting MyClass...\n");
::delete p; // Why is the destructor not called here?
printf("End of deleting MyClass...\n");
}
};
void* operator new(size_t size)
{
printf("Global newing...\n");
return malloc(size);
}
void operator delete(void* p)
{
printf("Global deleting...\n");
free(p);
}
int main(int argc, char** argv)
{
MyClass* myClass = new MyClass();
delete myClass;
return EXIT_SUCCESS;
}
输出为:
Newing MyClass...
Global newing...
Constructing MyClass...
End of newing MyClass...
Constructing MyClass...
Destroying MyClass...
Deleting MyClass...
Global deleting...
End of deleting MyClass...
实际:
在调用operator delete
的重载MyClass
之前,只有一个对析构函数的调用。
预期:
有两个对析构函数的调用。调用operator delete
的重载MyClass
之前的一个。在调用全局operator delete
之前的另一个。
答案 0 :(得分:17)
您正在滥用operator new
和operator delete
。这些运算符是分配和释放函数。他们不负责构造或破坏对象。它们仅负责提供用于放置对象的内存。
这些函数的全局版本为::operator new
和::operator delete
。
::new
和::delete
是new / delete-expressions,new
/ delete
与它们不同的是::new
和::delete
绕过特定于类的operator new
/ operator delete
重载。
new / delete-expressions构造/析构和分配/解除分配(通过在构造之前或破坏之后调用适当的operator new
或operator delete
)。
由于您的重载仅负责分配/取消分配部分,因此应调用::operator new
和::operator delete
而不是::new
和::delete
。
delete
中的delete myClass;
负责调用析构函数。
::delete p;
不会调用析构函数,因为p
的类型为void*
,因此表达式无法知道要调用的析构函数。尽管将::operator delete
用作 delete-expression 的操作数是不正确的格式,但它可能会调用您替换的void*
来释放内存(见下文编辑)。>
::new MyClass();
调用替换后的::operator new
来分配内存并在其中构造一个对象。指向该对象的指针以void*
的形式返回到MyClass* myClass = new MyClass();
中的new表达式,然后它将在此内存中构造另一个对象,从而结束了前一个对象的生命周期,而没有调用它的析构函数。
编辑:
由于@ M.M对这个问题的评论,我意识到void*
作为::delete
的操作数实际上是不正确的。 ([expr.delete]/1)但是,主要的编译器似乎决定只警告这一点,而不是错误。在使其变形之前,在::delete
上使用void*
已经存在未定义的行为,请参见this question。
因此,您的程序格式错误,并且如果仍然可以编译,则不能保证该代码确实执行了我上面所述的操作。
正如@SanderDeDycker在他的回答下面指出的那样,您还具有未定义的行为,因为通过在内存中构造一个已经包含MyClass
对象的另一个对象而不先调用该对象的析构函数,您就违反了[basic.life]/5如果程序依赖于析构函数的副作用,则禁止这样做。在这种情况下,析构函数中的printf
语句具有这种副作用。
答案 1 :(得分:14)
您的类特定的重载操作不正确。在您的输出中可以看到:构造函数被调用了两次!
在特定于类的operator new
中,直接调用全局运算符:
return ::operator new(size);
类似地,在特定于类的operator delete
中,执行以下操作:
::operator delete(p);
有关更多详细信息,请参见operator new
参考页。
答案 2 :(得分:1)
请参见CPP Reference:
import java.util.Scanner; public class Main { public static int factorial(myFunc mf, int n) { return mf.func(n); } public static int fact() { Factorial f = new Factorial(); System.out.println("Enter a number: "); Scanner scanner = new Scanner(System.in); int inINT = scanner.nextInt(); int outINT = factorial(f::Factor, inINT); System.out.println(outINT); return outINT; } public static void main(String args[]) { fact(); } }
,operator delete
取消分配先前由匹配的
operator delete[]
分配的存储。 这些释放函数由delete-expressions和 new-expressions在销毁(或无法执行以下操作)后重新分配内存 构造)具有动态存储期限的对象。他们可能也是 使用常规函数调用语法调用。
删除(和新增)仅负责“内存管理”部分。
因此很明显并且可以预期析构函数仅被调用一次-清理对象的实例。它将被调用两次,每个析构函数都必须检查它是否已经被调用。