Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uses of destructor = delete;

Consider the following class:

struct S { ~S() = delete; }; 

Shortly and for the purpose of the question: I cannot create instances of S like S s{}; for I could not destroy them.
As mentioned in the comments, I can still create an instance by doing S *s = new S;, but I cannot delete it as well.
Therefore, the only use I can see for a deleted destructor is something like this:

struct S {     ~S() = delete;     static void f() { } };  int main() {     S::f(); } 

That is, define a class that exposes only a bunch of static functions and forbid any attempt to create an instance of that class.

What are the other uses (if any) of a deleted destructor?

like image 222
skypjack Avatar asked Nov 22 '16 13:11

skypjack


People also ask

What is the function of a destructor?

A destructor is a member function that is invoked automatically when the object goes out of scope or is explicitly destroyed by a call to delete . A destructor has the same name as the class, preceded by a tilde ( ~ ).

What is the difference between delete and destructor?

Whenever a call to destructor is made , the allocated memory to the object is not released but the object is no longer accessible in the program. But delete completely removes the object from memory.

Does delete run destructor?

Yes, the destructor will be called for all objects in the array when using delete[] .

Can you delete destructor C++?

Since C++ 11, we can use the keyword delete to declare functions as deleted and thus prohibit the use of these functions in the code. See this article for a brief overview. In particular, the destructor of a class/struct/union can be declared as deleted.


2 Answers

If you have an object which should never, ever be deleted or stored on the stack (automatic storage), or stored as part of another object, =delete will prevent all of these.

struct Handle {   ~Handle()=delete; };  struct Data {   std::array<char,1024> buffer; };  struct Bundle: Handle {   Data data; };  using bundle_storage = std::aligned_storage_t<sizeof(Bundle), alignof(Bundle)>;  std::size_t bundle_count = 0; std::array< bundle_storage, 1000 > global_bundles;  Handle* get_bundle() {   return new ((void*)global_bundles[bundle_count++]) Bundle(); } void return_bundle( Handle* h ) {   Assert( h == (void*)global_bundles[bundle_count-1] );   --bundle_count; } char get_char( Handle const* h, std::size_t i ) {   return static_cast<Bundle*>(h).data[i]; } void set_char( Handle const* h, std::size_t i, char c ) {   static_cast<Bundle*>(h).data[i] = c; } 

Here we have opaque Handles which may not be declared on the stack nor dynamically allocated. We have a system to get them from a known array.

I believe nothing above is undefined behavior; failing to destroy a Bundle is acceptable, as is creating a new one in its place.

And the interface doesn't have to expose how Bundle works. Just an opaque Handle.

Now this technique can be useful if other parts of the code need to know that all Handles are in that specific buffer, or their lifetime is tracked in specific ways. Possibly this could also be handled with private constructors and friend factory functions.

like image 147
Yakk - Adam Nevraumont Avatar answered Oct 12 '22 10:10

Yakk - Adam Nevraumont


one scenario could be the prevention of wrong deallocation:

#include <stdlib.h>  struct S {     ~S() = delete; };   int main() {      S* obj= (S*) malloc(sizeof(S));      // correct     free(obj);      // error     delete obj;      return 0;  } 

this is very rudimentary, but applies to any special allocation/deallocation-process (e.g. a factory)

a more 'c++'-style example

struct data {     //... };  struct data_protected {     ~data_protected() = delete;     data d; };  struct data_factory {       ~data_factory() {         for (data* d : data_container) {             // this is safe, because no one can call 'delete' on d             delete d;         }     }      data_protected* createData() {         data* d = new data();         data_container.push_back(d);         return (data_protected*)d;     }        std::vector<data*> data_container; }; 
like image 38
Domso Avatar answered Oct 12 '22 10:10

Domso