Search code examples
c++classheap-memorydynamic-memory-allocation

C++: delete object or delete members?


I would like to ask about the functional difference; maybe ask for an example scenario, where I should choose from one of the options in the main method below:

#include <iostream>

using namespace std;

class A{
    private:
        int x, y;
    public:
        A(int, int);
    };

class B{
    private:
        int *x, *y;
    public:
        B(int, int);
        ~B();
    };

A:: A(int x, int y){
    this->x = x; this->y = y;
    }

B:: B(int x, int y){
    this->x = new int(x);
    this->y = new int(y); 
    }

B:: ~B(){
    delete this->x;
    delete this->y;
    }

int main(){
    int x = 0, y = 0;
    A* objA = new A(x, y);  // line 1
    B objB1(x, y);          // line 2
    B* objB2 = new B(x, y); // line 3

    delete objA;
    delete objB2;
    return 0;
    }

I understand that the second declaration in the main method B objB1(x, y) is obviously different from the other 2, but can someone please explain the functional difference between the constructors in lines labelled 1 and 3? Is there any bad practice in either of the declarations?

Thanks

NAX

UPDATE

First of all, I appreciate all of the answers that everyone is giving, I am really getting some good insight. I have edited the code above as a few of the answers pointed out that I haven't deleted the objects that I used, which is fair and all, but that was not the purpose of my question. I just wanted to gain some insight on the functional difference between the different approaches to creating the classes. And thanks to all that targeted that point. I am reading through the answers still.


Solution

  • "The functional difference..."

    On Line 1 you allocate an object of type A on the heap through use of the new keyword. On the heap, space is allocated for the object to which objA points which means 2 ints are created on the heap, contiguously, in line with your ivar definitions.

    On line 2 you create a new object of class B on the stack. It will have its destructor called automatically when it goes out of scope. However, when B is allocated, it will be allocated with space for two int pointers (not ints) which will in turn be allocated on the heap as you have specified in B's constructor. When objB1 goes out of scope, the pointers will be successfully deleted by the destructor.

    On line 3 you create a new object of class B on the heap. Therefore, space is allocated on the heap for two int pointers (not ints), and then those ints, in turn, are allocated elsewhere on the heap through use of the new keyword. When you delete objB2, the destructor is called and therefore the two "elsewhere integers" are deallocated, and then your original object at objB2 is also deallocated from the heap.

    In line with WhozCraig's comment, class A is the most definitely the preferred class definition of the two you have shown in your example.


    EDIT (Comment response):

    WhozCraig's link basically strongly discourages use of raw pointers. In light of this actually, yes, I agree, Line 2 would be preferred purely on the basis of memory management as B technically manages its own memory (though it still uses raw pointers).

    However, I generally dislike (excessive) use of new inside classes as new is much slower than the equivalent stack (or non-new) allocation. I therefore prefer to new the entire class rather than the individual components as it only requires a single new call and all ivars are allocated in the heap anyway. (Better yet, placement new, but that is well beyond the scope of this question).

    So in summing up:

    Line 2 (class B) would be preferred on the basis of memory management, however even better than that would be:

    A objAOnStack(x, y); // Avoids heap altogether
    

    Line 1 is equal-best provided you wrap it in a smart pointer such as std::shared_ptr or std::unique_ptr or something similar.

    Line 3 should not really be considered without a smart pointer wrapper (and it's generally better for performance to shy away from nested new anyway).