11

So I was looking at this question Memory Allocation Exception in Constructor where my boss states in his beautiful answer that the destructor will not be called.

Which makes me wonder,

If I were to write

struct XBase
{
    int* a;
    char* b;
    float* c;

    XBase() : a(nullptr), b(nullptr), c(nullptr) {} 
    ~XBase()
    {
        delete[] a; delete[] b; delete[] c;
    }   
};

and

struct X : XBase
{
    X() {
        a = new int[100];
        b = new char[100];
        c = new float[100];
    }
}

Then, if the allocation of c fails (with an exception being thrown), then the destructor of XBase would be called, since the base class has been constructed.

And no memory leak?

Am I correct?

Community
  • 1
  • 1
P45 Imminent
  • 7,466
  • 2
  • 30
  • 69
  • Oh you're so nice. I'm not upvoting though ;-) – Bathsheba Feb 25 '16 at 14:40
  • @BoBTFish: Indeed. The fact remains that since you have to even ask this question means that the "solution" is complex. – Bathsheba Feb 25 '16 at 14:42
  • You're right. It's a kind of smart pointer which holds multiple raw pointers. – songyuanyao Feb 25 '16 at 14:43
  • I may be wrong but as far as I know the destructor of XBase should be declared virtual in order to be called. Please somebody corrects me if this is not true. – Robert Kock Feb 25 '16 at 14:44
  • @RobertKock Doesn't have to be; depending on the context. – Yam Marcovic Feb 25 '16 at 14:45
  • 1
    @RobertKock: no that is not correct. Indeed we don't have polymorphic types here, but we're not using polymorphism. – Bathsheba Feb 25 '16 at 14:45
  • 1
    @RobertKock If you destroy an `X` using a pointer to an `XBase`, then the destructor in `XBase` must be virtual, otherwise the behavior is undefined. – PaulMcKenzie Feb 25 '16 at 14:46
  • @MSalters: thank you for the edit. – P45 Imminent Feb 25 '16 at 14:53
  • 2
    The design issue is that the inheritance should be private, which also prevents the `X` to `XBase` conversion. – MSalters Feb 25 '16 at 14:55
  • @P45Imminent The other issue is that if you're copying `X` objects, you have to write additional code, i.e. a copy constructor and assignment operator, to ensure that there are no memory leaks, double-delete's, etc. So even if you can guarantee that your small example is safe in a toy program, you've got a lot more to code if you actually use this in a real program (unless you disable the copying). Using `std::vector` alleviates you from having to write this additional code. – PaulMcKenzie Feb 25 '16 at 14:58

3 Answers3

8

You are right; this will work, because:

  • By the time X constructor body is executed, XBase is already constructed, and its destructor will be called.
  • Doing delete or delete[] on null pointers is perfectly valid, and does nothing.

So, if the allocation of a, b or c fails, the destructor of XBase will deallocate everything.

But, obviously, this design makes you write much more code that needed, since you can simply use std::vector or std::unique_ptr<T[]>.

lisyarus
  • 13,729
  • 3
  • 40
  • 61
0

Just to have a formal version here to back up the claims,

§ 15.2/2

An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects [...].

Yam Marcovic
  • 7,467
  • 25
  • 37
-1

If you're concerned about the memory allocation failing, I would put it in a separate method, like a Build or Create. You could then let the destructor handle it if it has been initialized (but definitely check pointer before blindly delete-ing.

Another solution is smart pointers, which are not designed to fit this case necessarily, but do automatically deallocate their contents.

tyson
  • 136
  • 1
  • 8
  • The `nullptr` will work fine. I simply meant that in the case of deallocation or other pointer action, be consistent about your use of pointers and deallocation. – tyson Feb 25 '16 at 15:00