6

I have a stateless, abstract base class from which various concrete classes inherit. Some of these derived classes are stateless as well. Because many of them are created during a run, I'd like to save memory and overhead by having all stateless derived classes emulate a singleton, by overriding operator new()/delete(). A simplified example would look something like this:

#include <memory>

struct Base {
  virtual ~Base() {}
 protected:
  Base() {}   // prevent concrete Base objects
};

struct D1 : public Base {  // stateful object--default behavior
  int dummy;
};

struct D2 : public Base {  // stateless object--don't allocate memory
  void* operator new(size_t size)
  {
    static D2 d2;
    return &d2;
  }
  void operator delete(void *p) {}
};

int main() {
  Base* p1 = new D1();
  Base* p2 = new D1();
  Base* s1 = new D2();
  Base* s2 = new D2();
  delete p1;
  delete p2;
  delete s1;
  delete s2;
  return 0;
}

This example doesn't work: delete s2; fails because delete s1; called ~Base(), which deallocated the shared Base in d2. This can be addressed by adding the same trick with new/delete overloading to Base. But I'm not sure this is the cleanest solution, or even a correct one (valgrind doesn't complain, FWIW). I'd appreciate advice or critique.

edit: actually, the situation is worse. The Base class in this example isn't abstract, as I claimed. If it's made abstract, through the addition of a pure virtual method, then I can no longer apply the new/delete overriding trick, because I cannot have a static variable of type Base. So I don't have any solution for this problem!

Eitan
  • 862
  • 1
  • 7
  • 17
  • 1
    Have you actually measured how much memory this "trick" is saving you? – Nemo Jun 22 '11 at 05:03
  • 1
    Why use `new` at all for a stateless object? – Bo Persson Jun 22 '11 at 05:04
  • @Bo Persson: Even when the object is stateless it can have a specific type and different objects having different types can be important. – sharptooth Jun 22 '11 at 05:07
  • @Martinho Fernandes: Could you please elaborate why an object without data members isn't stateless? – sharptooth Jun 22 '11 at 05:07
  • I need new because I'm creating this derived classes in a generic factory, that returns Base* type, with the actual derived type determined at run time. I expect to create many of this objects, so saving memory would help. More importantly, this create/delete operation is very frequent and the object constructors are quite lightweight (many of them are pure functors), so avoiding memory allocation could potentially reduce a lot of overhead. – Eitan Jun 22 '11 at 05:15
  • @Nemo: I just did measure the difference in my application, between using this trick (through a create() method as described below) and using regular new/delete with new tricks. The difference is about 12% in top memory consumption, and around 25% in run time performance, so I would say definitely worth it. – Eitan Jun 22 '11 at 05:57
  • @sharptooth - I'm asking why he wants to create lots of stateless objects dynamically. For one thing, you could create them as local variables or share a single copy. If they are statless (and equivalent?), why do you need lots of them, and on the heap? – Bo Persson Jun 22 '11 at 06:44
  • I dont get the problem of your Edit, why would you create a static variable of type Base?? – smerlin Jun 22 '11 at 10:24
  • 1
    @Eitan: Overloaded `new`/`delete` handles only memory allocation/deallocation. The corresponding constructor/destructor call is generated by the compiler automatically. So if there are statements like `Base* s1 = new D2(); Base* s2 = new D2();`, the memory allocated for D2 will be constructed twice without the destruction for the D2 object constructed previously. This will cause problems unless D2 is plain-old-data. – Ise Wisteria Jun 22 '11 at 13:33
  • @Bo: I don't want to create them dynamically, but I don't want a separate procedure for the stateful and stateless objects. They all derive from the same base class, so the factory can call a virtual method to create/get them based on run time information; but it just so happens some of them are stateless and can benefit from an optimization that avoids memory allocation. – Eitan Jun 22 '11 at 17:47
  • @smerlin: I would add the static Base object (with a corresponding new/delete override) so that deallocating s1 won't deallocate the shared Base, as explained in the text before the edit. – Eitan Jun 22 '11 at 17:48
  • @Ise: You're right. I didn't think about the (additional) double construction problem, since the constructors for the stateless objects are trivial (they're really just functors). But your argument also supports custom methods for object/instance management without abusing new/delete. In fact, I'm thinking of forgoing create() altogether and use the Prototype design patter instead, so that I can have different constructors but still a uniform virtual interface to obtain objects in the factory. – Eitan Jun 22 '11 at 17:53

2 Answers2

2

You just can't do that - that would violate "object identity" requirement that states that each object must have its own address. You have to allocate distinct memory block to each object - this can be done rather fast if you override operator new to use a fast block allocator specifically tailored for objects of fixed size.

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • Thanks for the link--it's a good thread. I think the subtle difference between that question and mine is that I do want all the objects returned from D2::operator new() to be the same object. As I mentioned above, implementing this as a singleton or a separate create/deallocate method works fine, but I was hoping to do it in a less kludge-y way (using new()/delete() is arguably that) – Eitan Jun 22 '11 at 05:44
1

I would say the best solution here is to make your derived class an actual singleton. Make your derived constructor private and just provide a static Base* getInstance() method that either creates the required object or returns the static instance. This way the only way to get a D1 object would be via this method since calling new D1 would be illegal.

zienkikk
  • 2,404
  • 1
  • 21
  • 28
  • I actually started with something similar. Instead of getInstance(), however, I used a create() method (and deallocate()) in each derived class that returned a pointer to an object of the respective type, which would be new'ed in the stateful objects and static in the stateless ones. This worked fine, but I am wondering if I can make this cleaner using the language mechanisms (i.e., new() and delete()). – Eitan Jun 22 '11 at 05:42
  • 1
    its not cleaner using the language mechanisms, since many people will make certain assertions when using those, and those assertions (e.g. new indeed allocates a new object, and addresses of 2 objects created with new compare unequal). Even the method name `create` could create these misinterpretations, so i would go with `get` and `release`, or simply allocate a new stateless object everytime a user needs one, this shouldnt be a problem unless those are used in very performance critical code sections. – smerlin Jun 22 '11 at 10:20
  • @smerlin: thanks for the insight into user expectations. I think your naming scheme is better. As for performance, I measured it and posted in an earlier comment, that the effect of this is trick is a reduction of ~25% of overall application run time, so significant enough. – Eitan Jun 22 '11 at 17:45