views:

169

answers:

4

I just came across a problem where the constructor of a class needs to allocate memory. So I happily wrote char *mem = static_cast<char*>(malloc(100*sizeof(*mem)));. But then I suddenly realized that in case of error I can't return error code (I am not using exceptions in my code). How can I solve this problem?

Should I add an bool initialized member and then after making my class and then check it right after, as in:

myClass mc;
if (!mc.initialized) {
    printf("Memory allocation failed in mc's constructor\n");
    exit(1);
}

Thanks, Boda Cydo.

+5  A: 

That's what exceptions were invented for. Also, use new instead of malloc(3).

Nikolai N Fetissov
I learned that exceptions are not good coding style. So I am not using them...
bodacydo
I strongly suggest you unlearn that, because exceptions are in fact the only way to retain RAII safety in C++.
Shirik
Shirik, thanks for the comment. I am going to follow your and Nikolai's advice!
bodacydo
A: 

If you're using C++ and not C you should probaly be using new instead of malloc. Throwing an exception is probably what you want to do from within your constructor.

If you're using new (nothrow) though because you don't want an exception,or you are using malloc then you can simply test the pointer that is returned for NULL. You would then set a member variable which indicates that the object is in a failed state. This is seen in streams classes of the standard C++ library.

Brian R. Bondy
That's the problem - there is no return value from the constructor...
bodacydo
That's the problem - there is no return value from the constructor... So even if I test return from `malloc` for `NULL` I can't indicate to the caller that `malloc` failed, unless I use `initialized` flag. That is what this question was all about...
bodacydo
@bodacydo: Sorry I was in the middle of answering and had to leave for a bit. You can re-read now that I am finished.
Brian R. Bondy
+8  A: 

You should use new, not malloc. new throws std::bad_alloc when you are out of memory. An exception should be propagated from the constructor if you fail to allocate (or for any other reason have a problem with initialization), as this is the only way you prevent the destructor from being called. If the constructor successfully completes, the destructor must be called (unless, of course, it was heap allocated and never freed).

Shirik
+1  A: 

If you're not using exceptions, you shouldn't use constructors (or shouldn't write constructors that can fail), because as you've noticed, there is no way to report an error from a constructor except via an exception. You can alternately use a factory function and use that to decouple the bits that can fail from the bits that can't

class myClass {
  private:
  char *m_malloced_buffer;

  // disallow calling ctors/dtors directly
  myClass(char *malloced_buffer) : m_malloced_buffer(malloced_buffer) {}
  ~myClass() { free(m_malloced_buffer); }

  public:
  static myClass *create()
  {
     char *buf = static_cast<char*>(malloc(100*sizeof(*mem)));
     if(!buf) return NULL;

     void *myClassRes = malloc(sizeof(myClass));
     if(!myClassRes) return NULL;
     new (myClassRes) myClass(buf); // placement new, uses existing memory to invoke the ctor
     return static_cast<myClass*>(myClassRes);
  }
  static void destroy(myClass* val)
  {
     if(!val) return;
     val->~myClass(); // explicitly invoke dtor
     free(val); // deallocate memory
  }
};
...
myClass *val = myClass::create();
if(val) { ... }
myClass::destroy(val);

In this example I've used malloc and free to do all the allocating, you could use new (std::nothrow) and delete just as easily. You'll have to extract every operation that can fail out of your ctor and into the factory function so that you can check for errors without using exceptions. Even in this simple example, you can see this is a huge pain in the neck. You say you learned that "exceptions are not good coding style" in a comment, do you mean you learned that (whether by instruction with reasoned arguments and explanations, or from experience), or someone said "exceptions are not good coding style" and you've accepted the statement as dogma?

Using a bool initialized member leads to the zombie object problem (see http://www.parashift.com/c++-faq-lite/exceptions.html).

Logan Capaldo
Why force heap allocation when you can use a initialization method just as well (see e.g. [googles guidelines](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Doing_Work_in_Constructors#Doing_Work_in_Constructors))? Why use placement-new here when you can just (possibly nothrow) `new`?
Georg Fritzsche
Initializer methods are prone to the same problems as having an "is initialized" method, along with having the additional problem of being able to forget to call them. You very well could use nothrow new here. The point of forcing "heap" allocation (it doesn't actually have to be heap allocation in practice, create could take an appropriately sized hunk of memory) is if you get back a non-NULL pointer you're guaranteed it's a fully functional object. Finally this example was meant to be almost, but not quite, a "worse cast scenario" of coding without any exceptions.
Logan Capaldo