简体   繁体   English

C ++ 20中的“破坏运算符删除”是什么?

[英]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 deleteoperator 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:关于它的关键事实:

  • A destroying operator delete function must be a class member function.销毁operator delete function 必须是 class 成员 function。
  • If more than one operator delete is available, a destroying one will always take precedence over a non-destroying one.如果有多个operator delete可用,销毁的总是优先于非销毁的。
  • The difference between the signatures of non-destroying and destroying 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 参数。
  • Like non-destroying 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 。
  • The destructor is not called prior to the destroying 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 仍然有效,可以在这样做之前进行检查。
  • With non-destroying 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:这是一个快速的总结:

  • Destroying 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中检索它来实现的。
  • If a class will have subclasses, any variable-sized data allocated at the same time must go before the start of the object, rather than after the end.如果 class 将具有子类,则同时分配的任何可变大小数据必须在 object 的开始之前 go 之前,而不是在结束之后。 In this case, the only safe way to delete such an object is destroying operator delete , so that the correct starting address of the allocation can be determined.在这种情况下, delete此类 object 的唯一安全方法是破坏operator delete ,以便确定分配的正确起始地址。
  • If a class only has a few subclasses, it can implement its own dynamic dispatch for the destructor this way, instead of needing to use a vtable.如果 class 只有几个子类,它可以通过这种方式为析构函数实现自己的动态调度,而不需要使用 vtable。 This is slightly faster and results in a smaller class size.这会稍微快一些,并导致更小的 class 大小。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM