7

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".

anon
  • 41,035
  • 53
  • 197
  • 293
  • KennyTM> That can be useful in some specific case like stack-allocated-vectors (see auto-buffer there : http://www.boost.org/community/review_schedule.html ) But yes it's an edge case. – Klaim Feb 20 '10 at 12:09

6 Answers6

18

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
  • 8,327
  • 9
  • 52
  • 63
  • I think its "nicer" to make the new operator throw an exception than to just hide it. – Tom Feb 20 '10 at 11:42
  • 9
    @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. –  Feb 20 '10 at 12:22
  • @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. – CB Bailey Mar 26 '10 at 09:07
9

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
  • 510,854
  • 105
  • 1,084
  • 1,005
5

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.

  • 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 Feb 20 '10 at 11:50
  • 1
    @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 –  Feb 20 '10 at 11:52
  • 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 Feb 20 '10 at 12:33
3

You can give the class a private new operator. See Public operator new, private operator delete: getting C2248 "can not access private member" when using new for more information.

Community
  • 1
  • 1
Patrick
  • 23,217
  • 12
  • 67
  • 130
0

Late for the party, but I think I found a way to define and use a class only instantiable on stack. I must admit its use is rather limited, as you can only:

  • take it as a const reference and
  • call its const member functions and
  • read its data members.

There is no way, however, to use it in a new expression or allocate it with dynamic storage duration as a member of another object (like Steve Folly shows in his answer).

Example implementation:

class StackOnly
{
  const char* _str{ "" };
  StackOnly() = default;
  StackOnly(const StackOnly&) = default;
  constexpr StackOnly(const char* str) : _str{ str } {};
public:
  template <typename ...Args>
  constexpr static StackOnly create(Args&&... args)
  { return StackOnly{ std::forward<Args>(args)... }; }

  void print() const { std::cout << _str; }
};

Please note that even though its limits, object still can be used quite decently, if you … well … use it in a function:

void useStackOnly(const StackOnly& obj)
{
    // function extends the lifetime of our temporary :)
    const StackOnly& stackObj = obj;
    stackObj.print();
}

int main()
{
    useStackOnly(StackOnly::create("greetings from stack"));
}

Note 1: The object itself might allocate on heap and use mutable data members - technically you can make up a vector :).

Note 2: C++11 is not required. One can achieve this in older C++ revisions as well.

neonxc
  • 802
  • 9
  • 21
-2

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
  • 53,344
  • 14
  • 119
  • 168
  • 2
    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 Feb 20 '10 at 11:32
  • 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 Feb 20 '10 at 12:26
  • 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 Feb 20 '10 at 13:06