C++ “什么是”呢;删除运算符“删除”;在C++;20?
C++20引入了“销毁”:使用标记类型参数的C++ “什么是”呢;删除运算符“删除”;在C++;20?,c++,destructor,c++20,delete-operator,destroy,C++,Destructor,C++20,Delete Operator,Destroy,C++20引入了“销毁”:使用标记类型参数的运算符delete的新重载 这到底是什么,什么时候有用?在C++20之前,对象的析构函数总是在调用其操作符delete之前被调用。使用C++20中的销毁运算符delete,运算符delete可以调用析构函数本身。下面是一个非常简单的玩具示例,介绍了非销毁与销毁运算符删除的比较: #include <iostream> #include <new> struct Foo { ~Foo() { std::c
运算符delete的新重载
这到底是什么,什么时候有用?在C++20之前,对象的析构函数总是在调用其操作符delete
之前被调用。使用C++20中的销毁运算符delete
,运算符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;
}
关于它的关键事实:
- 销毁
运算符delete
函数必须是类成员函数
- 如果有多个
运算符delete
可用,则销毁运算符始终优先于非销毁运算符
- 非销毁和销毁
运算符delete
的签名之间的区别在于前者接收一个void*
,后者接收一个指向被删除对象类型的指针和一个伪std::destroming_delete_t
参数
- 与非销毁
运算符删除
一样,销毁运算符删除
也可以采用可选的std::size\t
和/或std::align\u val\t
参数,方法相同。这意味着它们总是做同样的事情,并且它们在伪std::destroming\u delete\u t
参数后面
- 在销毁
操作符delete
运行之前,不会调用析构函数,因此它应该自己调用。这也意味着对象仍然有效,并且可以在执行此操作之前进行检查
- 使用非销毁的
运算符delete
,通过指向基类的指针而不使用虚拟析构函数对派生对象调用delete
,是未定义的行为。这可以通过赋予基类a销毁操作符delete
来实现,因为它的实现可以使用其他方法来确定要调用的正确析构函数,从而使其安全且定义良好
中详细介绍了销毁操作员删除的用例。下面是一个简短的总结:
- 销毁
operator delete
允许在末尾具有可变大小数据的类保留大小delete
的性能优势。其工作原理是将大小存储在对象中,并在调用析构函数之前在operator delete
中检索它
- 如果一个类将有子类,那么同时分配的任何可变大小的数据必须在对象开始之前,而不是在对象结束之后。在这种情况下,删除此类对象的唯一安全方法是销毁
操作员删除
,以便确定正确的分配起始地址
- 如果一个类只有几个子类,那么它可以通过这种方式为析构函数实现自己的动态分派,而不需要使用vtable。这稍微快一点,结果是班级规模更小
下面是第三个用例的示例:
#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;
}
(注意:当启用优化时,GCC 11.1及更高版本将错误地调用Triangle::~Triangle()
,而不是Square::~Square()
。请参阅。)“销毁delete可以通过指向基类的指针安全地删除派生类,即使它没有虚拟析构函数。”-它不是把确保安全的责任放在了销毁删除的实现者身上吗?该函数现在必须以某种方式调用正确的析构函数。这是否也可以用于实现入侵指针,这意味着只有在没有所有者离开的情况下才会执行实际删除?@Deduplicator:实际上可能是,但实际上不是,除非对对象生存期的措辞和delete
运算符的有效操作数做了进一步的更改。这篇文章将在相关的meta中讨论