tags:

views:

162

answers:

5

Is there a way to create a class Foo

so that I can do:

Foo foo;

but not

Foo* foo = new Foo(); 

?

I don't want people to be able to allocate copies of Foo on the heap.

Thanks!

EDIT: Sorry, I was wrong on the "stack only, not heap". What I want to say is "can not use new operator".

+5  A: 

In your documentation, put "do not create on the heap". Explaining why would be a good idea. Note that any attempt to enforce stack-only construction will also prevent the class from being used in standard containers and similar classes - it's not a good idea.

anon
I think this is about preventing instance being allocated with `new`. It does not seem to prevent storing instances in standard containers (since these don't allocate value-types with `new`: vector allocates raw memory, list allocates nodes etc)
UncleBens
@UncleBens std:;vector for example uses placement new to create the copies it stores in the raw memory allocated by new. All the standard containers store the things they contain in heap memory by default
anon
Yes, but none of them uses `X::operator new(size_t)` directly, so you can hide that and still be able to store objects in the containers by value. - I suppose you were answering the earlier version of the question, where it might have been unclear whether OP wanted preventing the object being on the heap altogether, or whether he just wants to disable `new X`
UncleBens
+3  A: 

You can give the class a private new operator. See http://stackoverflow.com/questions/1820069/public-operator-new-private-operator-delete-getting-c2248-can-not-access-priva for more information.

Patrick
+5  A: 

Make your operator new private.

#include <new>

struct Foo {
        int x;
private:
        void* operator new (std::size_t size) throw (std::bad_alloc);
};

On C++0x you can delete the operator new:

struct Foo {
        int x;
        void* operator new (std::size_t size) throw (std::bad_alloc) = delete;
};

Note that you need to do the same for operator new[] separately.

KennyTM
That will stop direct instantiations of Foo, but very easy to work around, see my answer.
Steve Folly
You can bypass the private operator new and go straight for the global op new trivially: `::new Foo` .
Charles Bailey
A: 

Why don't you want people to create Foo on the heap? I can think of absolutely no reason why this would be useful.

Anyway, you can give your class a private new operator to accomplish this, but do note that this will also prevent people from using your class in standard containers, as those all allocate their contained objects on the heap. It's simply a bad idea to do what you're trying to do.

Peter Alexander
The usual reason is that Foo is a RAII class for something which the designer intends to be lexically scoped. For instance if you've used Java and you like the way it does locks, then you might think your LockSession class should only ever be used as an automatic variable. Whether it's worth trying to enforce this in code is another matter entirely, but if you know that anyone creating one on the heap has misunderstood how the class is supposed to be used, then that's why you don't want them on the heap.
Steve Jessop
That may be so, but there are always scenarios where you might want to allocate on the heap. Using your LockSession case as an example, what if you wanted to create an arbitrary number of scoped locks? Ideally you'd want to create a bunch of them into a `std::vector` and then once the scope had ended go through and `delete` them all manually. With forced stack allocation you can't create arbitrary numbers of objects.
Peter Alexander
If you wanted to do that, then you wouldn't use my hypothetical LockSession interface, you'd use some other kind of lock. In Java you cannot dynamically scope the holding of a lock - databases and the like which might juggle lots of locks roll their own, and I'm not aware of any Java project which has failed as a consequence. It's a perfectly reasonable constraint, the only time I've personally handled code that really needed to return with a lock held which wasn't held when it was entered, or vice-versa, was deep in kernel code (and of course the implementations of pthread_mutex-style APIs).
Steve Jessop
+8  A: 

It's impossible to prevent an object being created on the heap. There are always ways around it. Even if you manage to hide operator new for Foo, you can always do:

#include <new>

struct Foo {
        int x;
private:
        void* operator new (std::size_t size) throw (std::bad_alloc);
};

struct Bar
{
    Foo foo;
};

int main()
{
    Bar* bar = new Bar;
    return 0;
}

And hey presto, you have a Foo on the heap.

Steve Folly
I think its "nicer" to make the new operator throw an exception than to just hide it.
Tom
@Tom - I disagree, on the "detect errors early" principle. I'd much rather have the compiler or linker complain than have the program crash on a users system. As for catching the exception, what are you going to do to resolve it? Exceptions are meant to handle exceptional *run-time* issues, *not* programmer mistakes.
Steve314
Good point Steve.
Tom
@Steve: You can (even more) trivially bypass a private operator new by going forcing the use of global operator new: `::new Foo`. No extra class required.
Charles Bailey