It seems easy to make sure that an object can only be created on the stack and not on the heap (by simply marking private operator new), but is it possible to do the opposite: only permitting the creation of an object on the heap (with operator new) and *not* on the stack, with compiler warnings for the forgetful programmer?

Dani AI

Generated

Short summary: and are right to point at a factory + controlled construction approach if the goal is strict centralisation of allocation. A different, compact idiom prevents automatic (stack) objects while still allowing normal new usage: make destruction non-public so the compiler refuses to emit the end-of-scope destructor call for an automatic object.

This pattern keeps a normal (public) constructor if desired, but makes the destructor private (or protected) and exposes a controlled destroy path. Example (C++11-friendly):

class HeapOnly {
public:
  static HeapOnly* Create() { return new HeapOnly; }
  static void Destroy(HeapOnly* p) { delete p; }

  static std::unique_ptr<HeapOnly, void(*)(HeapOnly*)> MakeUnique() {
    return std::unique_ptr<HeapOnly, void(*)(HeapOnly*)>(Create(), &HeapOnly::Destroy);
  }

  HeapOnly(const HeapOnly&) = delete;
  HeapOnly& operator=(const HeapOnly&) = delete;

private:
  HeapOnly() = default;
  ~HeapOnly() = default; // private: automatic (stack) allocation will fail to compile
};

Why this helps: attempting HeapOnly h; produces a compile-time error because the destructor is inaccessible. Heap allocation via new HeapOnly still works, and MakeUnique() automates safe deletion while keeping the destructor inaccessible to general code.

Notes and caveats:

  • Use protected destructor instead of private when subclassing is required.
  • If centralised tracking of allocations is the goal (as mentioned), overload the class operator new / operator delete to route through the allocator, or have Create() call the central allocator and have Destroy() report deallocation. Return smart pointers with class-controlled deleters so clients cannot forget to report frees.
  • This enforces a compile-time error (better than a warning). If only a warning is desired, rely on static analysis or code-review policies rather than language-level tricks.

Recommended Answers

All 4 Replies

If you have something like a factory pattern then you can enforce it, for example:

class CandyFactory{
 private:
  struct ChoclateBar(){ 
        int calories; 
        ChoclateBar(): calories(){}
   };
 public:
  shared_ptr<ChoclateBar> makeChoclateBar(){
       return shared_ptr( new ChoclateBar() )
  }
};

I don't know if thats what you were asking?

I don't see much purpose for it. Stack-based objects are so much nicer to use than heap-allocated objects. I'd be curious to know about your use-case?

The solution is to use a factory function and private constructors, as suggested by firstPerson (and, of course, smart-pointers are strongly preferred to raw-pointers).

If you have C++11 support, there is a very nice idiom to create a factory function, as so:

class MyRational {
  private:
    int num;
    int denom;
    
    MyRational(int aNum = 0, int aDenom = 1) : num(aNum), denom(aDenom) { };
  public:
    //..
    MyRational(const MyRational&) = delete;
    MyRational(MyRational&&) = delete;
    MyRational& operator=(const MyRational&) = delete;
    MyRational& operator=(MyRational&&) = delete;

    template <typename... Args>
    static std::unique_ptr<MyRational>&& Create(Args&&... args) {
      return std::unique_ptr<MyRational>(new MyRational(std::forward<Args>(args)...));
    };
};

int main() {

  std::unique_ptr<MyRational> r1 = MyRational::Create(2);
  std::unique_ptr<MyRational> r2 = MyRational::Create(1,2);

  return;
};

To firstperson: Yep, that seems to do the trick! Thanks.
To mike:
the use case was the desire to centralise all memory allocation to get more information on memory use/availability.
Regarding C++11. My problem is that this is all theoretical for me. I use VS2009 which does not support it. Do newer version of VS support it? Do you know when they expect to support it? Will it be more close to 2 year or 10 years. (Remember that template partial specialisation support tooks may years to be available and the template are still not not bug free in Vs2009 (with Msft expressly telling me that they have no plan to correct the defect discovered by Vanjiyan121).

VC10 supports such as rvalue-refs. will add a few insignificant language features, but at least, in VC11, the C++11 standard library will be pretty much complete (put aside the things that cannot be implemented with the limited C++11 language feature support). But almost all interesting features will still be lacking at least until VC12, like variadic templates, template aliases, delegating ctors, inherited ctors, default/delete functions, and initializer lists.

I share your skepticism of MSVC's ability to every reach a reasonable compliance, given their track-record on delivering support for C++98, with about a 10 year delay (since MSVC2008 is about the first decent compiler they produced in that respect). But I think that most features of C++11 should be reasonably easy to add, at least, the GCC team managed to get more of them done pretty quickly, just lacking a few things.

So, I know C++11 is still theoretical for most, but I think it's good to start showing C++11 code (and how it solves some problems pretty neatly) just to build enthusiasm and make the switch as swift as possible (with more pressure on compiler-vendors).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.