如果使用 Visual Studio 编译,上面的代码可以工作。如果析构函数抛出异常,则不会调用
operator delete
。如果析构函数没有抛出异常,则调用 operator delete
。但如果在 Linux 上使用 gcc 编译,相同的代码会导致分段错误。即使班级中没有 std::string
成员,它也不起作用。它以“free():在 tcache 2 中检测到双重释放”结束。这意味着即使析构函数抛出异常,也会调用 operator delete
。根据 C++ 标准,该行为是否未定义?如果是这样,是否意味着如果第一次调用抛出异常就无法再次调用delete
?
#include <string>
#include <iostream>
class MyClass
{
public:
void *operator new(size_t size)
{
void *p = std::malloc(size);
if (!p) throw std::bad_alloc();
return p;
}
void operator delete(void *p)
{
std::free(p);
}
~MyClass() noexcept (false)
{
if (s == "123") throw int{};
}
std::string s{"123"};
};
int main()
{
MyClass* myobject = new MyClass{};
try
{
delete myobject;
}
catch (...) {}
std::cout << "<" << myobject->s << ">";
myobject->s = "abc";
delete myobject;
return 0;
}
我在 Windows 上使用 Visual Studio 和 Linux 上使用 GCC 编译并运行了代码。我希望它可以在两个平台上运行,但它不能在带有 GCC 的 Linux 上运行
GCC 是正确的,MSVC 是错误的(像往常一样,我敢说)。
[expr.delete]
中有这样的:
[注3: 无论对象或数组的某些元素的析构函数是否抛出异常,都会调用释放函数。 — 尾注]
注释并不规范,但如果您愿意,您可以阅读上面的规范性措辞。