views:

293

answers:

6

I recently read a lot about "preventing heap allocation for a class" (see this question).

I was able to understand "how", but now I can't figure out "why" someone would like to do that.

I guess there must be legitimate reasons for this, but I just can't figure them out.

In short: "Why may I want to forbid users from creating objects of my class in the heap ?"

+5  A: 

Mainly because stack-allocated objects are automatically cleaned up when they go out of scope, thus removing a large class of bugs - namely memory allocation bugs.

Visage
Isn't it something that can be more easily solved using smart pointers ?
ereOn
Absolutely. Note that I didnt say that enforcing stack allocation was advisable ;)
Visage
@Visage: Thanks, Noted ;)
ereOn
Note that the methods for preventing heap allocation do not prevent embedding inside another object which is in turn heap allocated -- they only prevent direct allocation. Oftentimes these tricks will be combined with a factory function which returns a smart pointer of some sort.
Ben Voigt
This is not a real answer to why would you want to forbid using the heap. To the question of why would you prefer to allocate in the stack, the main reason is that it is much more efficient. Allocation in the stack is basically free (in the worst case a couple of assembly instructions per variable, in the general case only a couple of assembly instructions per function), while each heap allocation may require over one hundred instructions.
David Rodríguez - dribeas
Introducing a semantic nightmare in order to save a hundred or so CPU instructions sounds like a case of premature optimisation to me.
Visage
@Visage: The overhead of heap allocation is far more than a hundred CPU instructions- the cost of indirecting to the heap can be extensive if overused, and in addition, managing the memory uses additional cycles and memory. The fundamental fact is that stack allocation is better from a performance perspective AND semantically much cleaner too.
DeadMG
Well if its a fact then you must have evidence, right?
Visage
@Visage: Maybe you can provide even a single example where stack and heap can both be used and heap allocation is cleaner than stack allocation... I don't think there is a single example. Now, consider: `void foo() { for ( int * i = new int(0); *i < 10< ++*i ) { ... } }`--ouch, how to free the pointer there? Use a smart pointer --in the *stack* --: `void foo() { for ( auto_ptr<int> i(new int(0)); *i < 10; ++*i ) { ... }`... We are *still* using the stack, and the code is more complex, or is it not?
David Rodríguez - dribeas
Im not sure contrived examples that would never be used in real life situations really bolsters your argument.
Visage
@Visage: The thing is that stack allocation is always simpler than heap allocation in code. I have provided the first example: the control variable of a loop is impossible to write with pure heap allocation. Now the ball is in your grounds: can you provide evidence of any case where heap allocation is cleaner than stack allocation? (limit the examples to situations where you can actually use stack allocation). Stack **is** the default, heap is for when stack does not fit the problem.
David Rodríguez - dribeas
But thats not what I was claiming. To recap - my claim was that one should not write stack-only classes simply because of performance reasons, as the downside in terms of complexity is rarely justified in terms of performance gains related to memory allocation.
Visage
+12  A: 

Some classes make sense only if the objects are instantiated on the stack. For example, Boost scoped_ptr, or lock_guard.

Nemanja Trifunovic
Pretty much this- some classes only make sense when allocated on the stack.
DeadMG
Looking at the boost documentation, it doesn't seem that they redefined the several `new` operators to prevent heap allocation. Is it hidden or didn't they do it. If so, why ?
ereOn
boost::scoped_ptr also makes sense for a pimpl, as noted in the link. That's not (necessarily) on the stack.
MSalters
I can imagine situations where you'd want to extend the lifetime of both those classes beyond the current scope - unusual behaviour perhaps, but it certainly makes sense. Why would you want to jump through hoops to prevent this?
Mike Seymour
Especially considering that simply redefining `new` doesn't prevent the object to be embedded in a super class which is itself heap-allocatable...
Matthieu M.
+2  A: 

Usually it is a good idea to prevent the unexpected usage for the class. For example, consider Guard classes, that rely on RAII technique. They must be allocated on the stack and they do their job when they are going out of the scope. No one expect users to allocate guard objects in the heap, so it is explicitly forbidden.

Better explicit than implicit. Herb Shutter says that it must be hard to use your class incorrectly (allocating in the heap) and very easy to use it correctly (on the stack).

SadSido
You're right about RAII, but one could heap-allocate a lock guard instance and bind it to say `auto_ptr` to achieve the same result.
sharptooth
But auto_ptr must be definitely allocated on the stack. So, it is basically the same thing, so why do we need an extra level of abstraction?
SadSido
@SadSido: the auto_ptr can be embedded in an object, or returned from the function, to extend the lifetime of the object it controls beyond the current scope. I can imagine scenarios where you'd want to do that, and would be thwarted if there were an arbitrary ban on heap allocation.
Mike Seymour
Good point. Nevertheless, it must be a very sophisticated scenario that puts you in need of extending RAII Guard's lifetime. I guess, such scenarios must be avoided =)
SadSido
@SadSido: create a helper function which checks for deadlock before returning the guard. That seems useful and without move semantics requires heap allocation. I seriously don't think Sutter ever presumed to say anything about memory allocation schemes when he talked about preventing incorrect use.
Matthieu M.
Shutter didn't say a word about allocation, he stated a general principle. I just thought this principle can be a reason for disabling heap allocation...
SadSido
+1  A: 

Stack allocation is faster(no searching for space needed).

Dani
Yes, but this is not the reason for forbidding to allocate objects of specific class on stack.
sharptooth
A: 

For certain classes of very small objects - consider 3D points in a geometry engine - allocating these individually on the heap is a recipe for creating dangling pointers and introducing a vast amount of overhead - however, they are vitally necessary in a lot of calculations

So, a common mechanism is to use a flyweight pattern in objects that actually contain a collection of 3D points, like the description of some geometrical entity, and to allow their use on the heap as intermediate results of calculations.

Now, that said, extra special care needs to be taken to avoid needless copying of data between the flyweight implementation, e.g. ListOf3DPoints and the heap allocated 3D points that are being used for intermediate results.

In general, I've found a number of cases where I have coupled a flyweight pattern with heap allocation capabilities to achieve optimimum results - the flyweight provides persistent storage, the heap allocations let me perform item level manipulations without having to gen up another flyweight

Mark Mullin
+2  A: 

I will go against the tide it seems (so I do expect downvotes, but please leave a comment to indicate the why).

I don't see any reason to forbid heap allocation, mainly because I don't like to second guess the potential uses of the classes I create.

As a design rule, I tend to put as few restrictions on the uses of my classes as possible. This means as few assumptions as possible. There is nothing as maddening as being unable to do what you wish simply because it was forbidden... for reasons either unknown or just plain wrong (denoting the superstitious/erroneous beliefs of the library writer).

Also, pragmatism teach that it's about impossible to actually prevent anything in C++. For example, some people have talked about guards --> what if I'd like to create a super class (which conveniently adds logging) ? Then I would put the guard class as an attribute, and even if its (the original class) new operator is private, my super class can be instantiated on the heap unless it somehow replicates the mechanism.

So, as for me, it's not a matter of why or how. I just don't fiddle with memory allocation schemes in library code, it's up to the user to use what's most convenient for her.

Matthieu M.
Actually, I tend to agree.
ereOn