[英]What is "destroying operator delete" in C++20?
C++20 introduced "destroying operator delete
": new overloads of operator delete
that take a tag-type std::destroying_delete_t
parameter. C++20 引入了“破坏operator delete
”:操作符operator delete
的新重载,采用标记类型std::destroying_delete_t
参数。
What exactly is this and when is it useful?这到底是什么,什么时候有用?
Prior to C++20, objects' destructors were always called prior to calling their operator delete
.在 C++20 之前,对象的析构函数总是在调用它们的operator delete
之前被调用。 With destroying operator delete
in C++20, operator delete
can instead call the destructor itself.在 C++20 中使用破坏operator delete
, operator delete
可以改为调用析构函数本身。 Here's a very simple toy example of non-destroying vs. destroying operator delete
:这是一个非常简单的非破坏与破坏operator delete
的玩具示例:
#include <iostream>
#include <new>
struct Foo {
~Foo() {
std::cout << "In Foo::~Foo()\n";
}
void operator delete(void *p) {
std::cout << "In Foo::operator delete(void *)\n";
::operator delete(p);
}
};
struct Bar {
~Bar() {
std::cout << "In Bar::~Bar()\n";
}
void operator delete(Bar *p, std::destroying_delete_t) {
std::cout << "In Bar::operator delete(Bar *, std::destroying_delete_t)\n";
p->~Bar();
::operator delete(p);
}
};
int main() {
delete new Foo;
delete new Bar;
}
And the output:和 output:
In Foo::~Foo()
In Foo::operator delete(void *)
In Bar::operator delete(Bar *, std::destroying_delete_t)
In Bar::~Bar()
Key facts about it:关于它的关键事实:
operator delete
function must be a class member function.销毁operator delete
function 必须是 class 成员 function。operator delete
is available, a destroying one will always take precedence over a non-destroying one.如果有多个operator delete
可用,销毁的总是优先于非销毁的。operator delete
is that the former receives a void *
, and the latter receives a pointer to the type of the object being deleted and a dummy std::destroying_delete_t
parameter.非破坏和破坏operator delete
的签名之间的区别在于前者接收一个void *
,后者接收一个指向被删除 object 类型的指针和一个虚拟std::destroying_delete_t
destroying_delete_t 参数。operator delete
, destroying operator delete
can also take an optional std::size_t
and/or std::align_val_t
parameter, in the same way.与非销毁operator delete
一样,销毁operator delete
也可以采用可选的std::size_t
和/或std::align_val_t
参数,以相同的方式。 These mean the same thing they always did, and they go after the dummy std::destroying_delete_t
parameter.这些意味着他们总是做同样的事情,他们在虚拟std::destroying_delete_t
destroying_delete_t 参数之后是 go 。operator delete
running, so it is expected to do so itself.在销毁operator delete
运行之前,不会调用析构函数,因此它应该自己这样做。 This also means that the object is still valid and can be examined prior to doing so.这也意味着 object 仍然有效,可以在这样做之前进行检查。operator delete
, calling delete
on a derived object through a pointer to a base class without a virtual destructor is Undefined Behavior.使用非破坏性operator delete
,通过指向没有虚拟析构函数的基本 class 的指针对派生的 object 调用delete
是未定义的行为。 This can be made safe and well-defined by giving the base class a destroying operator delete
, since its implementation can use other means to determine the correct destructor to call.这可以通过给基础 class 一个破坏性operator delete
来确保安全和明确定义,因为它的实现可以使用其他方法来确定要调用的正确析构函数。 Use-cases for destroying operator delete
were detailed in P0722R1 . P0722R1中详细介绍了销毁operator delete
的用例。 Here's a quick summary:这是一个快速的总结:
operator delete
allows classes with variable-sized data at the end of them to retain the performance advantage of sized delete
.销毁operator delete
允许在其末尾具有可变大小数据的类保留大小delete
的性能优势。 This works by storing the size within the object, and retrieving it in operator delete
before calling the destructor.这是通过将大小存储在 object 中,并在调用析构函数之前在operator delete
中检索它来实现的。delete
such an object is destroying operator delete
, so that the correct starting address of the allocation can be determined.在这种情况下, delete
此类 object 的唯一安全方法是破坏operator delete
,以便确定分配的正确起始地址。Here's an example of the third use case:这是第三个用例的示例:
#include <iostream>
#include <new>
struct Shape {
const enum Kinds {
TRIANGLE,
SQUARE
} kind;
Shape(Kinds k) : kind(k) {}
~Shape() {
std::cout << "In Shape::~Shape()\n";
}
void operator delete(Shape *, std::destroying_delete_t);
};
struct Triangle : Shape {
Triangle() : Shape(TRIANGLE) {}
~Triangle() {
std::cout << "In Triangle::~Triangle()\n";
}
};
struct Square : Shape {
Square() : Shape(SQUARE) {}
~Square() {
std::cout << "In Square::~Square()\n";
}
};
void Shape::operator delete(Shape *p, std::destroying_delete_t) {
switch(p->kind) {
case TRIANGLE:
static_cast<Triangle *>(p)->~Triangle();
break;
case SQUARE:
static_cast<Square *>(p)->~Square();
}
::operator delete(p);
}
int main() {
Shape *p = new Triangle;
delete p;
p = new Square;
delete p;
}
It prints this:它打印这个:
In Triangle::~Triangle()
In Shape::~Shape()
In Square::~Square()
In Shape::~Shape()
(Note: GCC 11.1 and older will incorrectly call Triangle::~Triangle()
instead of Square::~Square()
when optimizations are enabled. See comment 2 of bug #91859 .) (注意:启用优化时,GCC 11.1 和更早版本将错误地调用Triangle::~Triangle()
而不是Square::~Square()
。请参阅错误 #91859 的注释 2。 )
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.