Why does the standard allow me to freely store distribution classes without destructors?

If you have a class without a destructor:

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

The standard does not allow me to "locally" allocate an instance of this class:

 int main() { A a; //error } 

But it seems that this is normal if I set it aside in a free store:

 int main() { a *p = new A(); } 

Until I call delete on this pointer:

 int main() { a *p = new A(); delete p; //error } 

So my question is: why does the standard allow me to have a class without a destructor if I distribute it across free storage? I would suggest that there are some use cases for this? But what exactly?

+5
source share
3 answers

So my question is: why does the standard allow me to have a class without a destructor if I distribute it across free storage?

Because this is not how standard functions work.

The syntax = delete you are talking about was invented to solve a number of problems. One of them was very specific: creating types that were moved or fixed, for which the compiler generated a compile-time error if you tried to call a copy (or move) constructor / assignment statement.

But the syntax has other purposes when applied as a whole. Using the =delete function, you can prevent people from invoking certain function overloads, mainly to stop certain types of problematic implicit conversions. If you do not call a function with a specific type, you get a compile-time error for calling overload delete d. Therefore, =delete allowed to apply to any function.

And the class destructor qualifies as "any function."

The purpose of this function was not to create types that would be indestructible. This is simply the result of permission =delete on any function. It is not a design or an intention; it's simple.

While using =delete for the destructor is not so much, it is also not so important to use the specification to explicitly prohibit its use on the destructor. And, of course, there’s not much point in using =delete to behave differently when applied to the destructor.

+7
source

Wherein:

 A a; 

You are called by the destructor when you exit the scope (and you deleted the destructor, hence the error). Wherein:

 A *a = new A(); 

You simply do not name the destructor (because you never use delete ). The memory is cleared when the program terminates, but you essentially guarantee a memory leak.

There is no reason for c++ reject this behavior because it would create a very specific case for programming in the compiler. For example, c++ does not prohibit this:

 int *p; *p = 5; 

Although this is obviously bad, it will always be bad. The programmer must make sure that they do not.

There is no reason why you should remove the destructor because it is not useful.

+5
source

In a multi-threaded environment, you can share an indestructible class between threads.
If the thread that allocates memory ends, there is no reason why the pointer to the allocated memory cannot yet be used by another thread.

The standard implies that a constructed object with dynamic storage duration is not suitable for calling a destructor.

12.4 Destructors [class.dtor]

The destructor is called implicitly - for a constructed object with a static storage time at the end of the program,
- for a constructed object with a duration of storage of the stream at the output of the thread,
- for a constructed object with automatic storage time, when the block in which the output object is created,
- for the created temporary object, when its end of life.


We can see the benefits of this through a basic example of memory sharing between threads:

 #include <thread> #include <iostream> //shared object struct A { void say_hello(){ std::cout << ++value << '\n'; } ~A() = delete; int value; }; //two threads void creator(); void user(A* a); //the creator allocates memory, //gives it to another thread (via pointer), //and then ends gracefully. //It does not attempt to implicitly call the destructor. //Nor would we want it to for allocated memory we are sharing. void creator(){ A* a = new A(); a->value = 0; std::thread t(user, a); t.detach(); } //The user recieves a pointer to memory, //and is free to continue using it //despite the creator thread ending void user(A* a){ while(1){ a->say_hello(); } } //main->creator->user int main(){ std::thread t(creator); while(1){} } 

+1
source

All Articles