24

In this SO question is stated that this construct prevents stack allocation of instance.

class FS_Only {
    ~FS_Only() = delete;  // disallow stack allocation
};

My question is, how does it prevents allocation? I understand, that is not possible to delete this instance, either explicitly or implicitly. But I think, that would lead to memory leak or run time error, respectively.

Are compilers smart enough to sort this out and raise compiler error? Also why would one need this?

Solomon Ucko
  • 5,724
  • 3
  • 24
  • 45
jnovacho
  • 2,825
  • 6
  • 27
  • 44
  • 6
    Technically it prevents deallocation not allocation. :P You could use `new` to create an instance on the heap but if you never `delete` it it will compile fine. – Simple Sep 17 '13 at 10:51
  • @Simple It has absolutely nothing to do with allocation or deallocation. It means that the class does not have a callable destructor. – James Kanze Sep 17 '13 at 11:30

6 Answers6

40

The destructor of a variable with automatic storage duration (i.e. a local variable) would need to run when the variable's lifetime ends. If there is no accessible destructor the compiler refuses to compile the code that allocates such a variable.

Basically the distinction between "stack allocation" (an inaccurate choice of term by the way) and free store allocation is that with local variables constructor/destructor calls always come in pairs, whereas with free store allocation you can construct an object without ever destructing it. Therefore by preventing access to the destructor your code makes it impossible to allocate a local variable of the type (if the constructor runs the destructor must also run, but there is no destructor so the program is rejected).

Jon
  • 428,835
  • 81
  • 738
  • 806
  • 5
    *"the compiler refuses to compile the code that allocates such a variable."* - Well, to be presise, in particular it refuses to compile the code that *deallocates* such a variable (yet you are right that the same code also has its allocation somewhere else). – Christian Rau Sep 17 '13 at 10:53
  • And finally +1 for the added second paragraph. – Christian Rau Sep 17 '13 at 11:09
  • 1
    @ChristianRau To be precise, the compiler refuses to compile code which will destruct such a variable. It's actually possible to construct a local variable, by declaring properly aligned raw memory and using placement new, and the memory for that variable will be deallocated. But the destructor won't run. – James Kanze Sep 17 '13 at 11:32
  • @JamesKanze Hah, right, got me there. I forgot that with correcting imprecise statements comes the responsibility to be exactly precise yourself. ;-) – Christian Rau Sep 17 '13 at 11:35
  • The part about compilation error was what I was looking for. I didn't realize, that destructor calls are generated at compile time, hence my confusion. – jnovacho Sep 17 '13 at 11:50
  • Won't this mean that you're forced to leak the object? – paulm Sep 17 '13 at 12:36
  • @paulm: Yes. The idea is that you will arrange not to care about that. – Jon Sep 17 '13 at 12:50
22

I understand, that is not possible to delete this instance, either explicitly or implicitly.

More than that, it's not possible to destroy any instance; whether by deleting it or otherwise.

Declaring an automatic variable (or "stack allocation", if you like) doesn't just cause the instance to be created when the program reaches the point of declaration; it also causes it to be destroyed when the program leaves that block. With a deleted destructor, that can't be done, so the declaration is not allowed.

This also prevents you from declaring a static or thread-local variable, since that also generates code to destroy the variable when the program or thread ends.

So the only way to create one of these is with new, and once you've done that you can never destroy it. However, this doesn't altogether prevent stack allocation:

char memory[sizeof(FS_Only)] alignas(FS_Only);
FS_Only * not_fs = new (memory) FS_Only;

Also why would one need this?

In my view, you wouldn't. A mandatory memory leak is a horrible way to ensure that an object is never destroyed at the wrong time. Instead, use techniques such as RAII to manage any objects that need a dynamic lifetime, ensuring that they always have a well defined owner (or shared owners) responsible for deleting them after use. The smart pointers in the C++11 standard library are a good starting point.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Just to be sure, is there any reason you used placement-new in your example? Wouldn't it also work with "plain" `new`? – Christian Rau Sep 17 '13 at 11:13
  • 5
    @ChristianRau: It's an example of how to achieve "stack allocation" dispite the efforts of the class implementor to prevent it. Of course, plain `new` also works, as the implementor intended. – Mike Seymour Sep 17 '13 at 11:16
  • 2
    If you want to prevent local variables, the simplest solution is to make the destructor private, and then provide a member function `deleteMe` or some such. This also allows deletion of dynamically allocated instances. – James Kanze Sep 17 '13 at 11:34
  • @JamesKanze: That's quite a big "if"; but yes, you could do that, if you really had a reason to prevent locals. – Mike Seymour Sep 17 '13 at 11:36
  • +1, very clear and balanced answer -- much more so than mine. – Jon Sep 17 '13 at 11:36
  • @MikeSeymour I'll admit that I've never found a case for it. – James Kanze Sep 17 '13 at 14:48
7

Marking a destructor as deleted will make it impossible to destroy the object. It doesn't matter if it's on the stack or on the heap. All destruction of an object (whether automatic by it going out of scope, or by doing delete on it) calls the destructor. And as it is the compiler that handles the calling of the destructor, it will notice if it's been marked as deleted and issue an error.

It doesn't however disallow the creation of objects, but as non-pointer instances are destructed on the end of the scope where they were declared, the compiler will issue an error, in effect disallowing creation of the instance. It's still possible to dynamically allocate instances using new, with the caveat that they can't be deleted, possibly leading to a memory leak.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
5

I have two answers:


From "The C++ Programming Language":

You can’t have a local variable that can’t be destroyed (§17.2.2) ...


In my own words:

Think about it for a minute - a variable created on the stack is meant to be deleted at the end of scope in which it is declared.

By disallowing the object's implicit destructor, you are basically telling the compiler "You are not permitted to delete this object at the end of scope." The compiler doesn't want to take a risk of creating something that can't be destroyed (and it's right - what if it's large and will leak megabytes of memory?), so it refuses to create it. That leaves you only one option - to create it on the free store and manually manage it.

Now the responsibility is yours. Note, however, that this is bad code - the objects will never be able to be deleted, even manually. Remember - you deleted the destructor! As it was correctly pointed out in other answers, this is a forced memory leak and should be avoided.

Oleksiy
  • 37,477
  • 22
  • 74
  • 122
3

When you try to declare FS_Only on the stack

{ 
    FS_Only fs;
    //...
}

the destructor will automatically be called on the closing brace so you will get a compile error.
Of course, you won't be able to delete a new'ed one either:

{ 
    FS_Only * fs = new FS_Only();
    //...
    delete fs; //also won't compile
}
doctorlove
  • 18,872
  • 2
  • 46
  • 62
0

The only way to allocate such an object would be through dynamic allocation, without any de-alocation (the object will either be leaked, or accessed by pointer for the duration of the program.

Any attempt to allocate a static instance will cause a compilation error when the allocated object would go out of scope (as the destructor code cannot be called).

utnapistim
  • 26,809
  • 3
  • 46
  • 82