nnrales
nnrales

Reputation: 1519

When is C++ destructor called on an object in heap?

Suppose a class and its usage

#include <vector>
class B
{
 public:
     B() {temp = 0;}; // implemented 
    ~B() {} ; // implmented 
 private : 
      int temp; 
      // it holds a std::bitset , a std::vector.. It is quite large in size.
};

class A 
{
    private:
    std::vector<B*> * _data ; 
    public: 
    A()
    {
       _data = new std::vector<B*>(0); 
    }
    ~A()
     {
         for(unsigned int idx_ = 0; idx_ < _data->size(); ++idx_)
              delete _data->at(idx_);
         delete _data ; 
     }
     void addB()
     {
          B * temp = new B();
          _data->push_back(temp);
     }
}



int main()
{
   A temp; 
   temp.addB(); 
    // Do something 
}

My question is does this code leak memory ? Also suppose another usage

int main()
{
     A * temp = new A(); 
     temp->addB(); 

     delete temp ; // 1 
}

Is 1 here required ? If I have a pointer to the heap and the pointer goes out of scope is the destructor called on the element in heap. I just want to be sure about this point. Thank you !

Upvotes: 1

Views: 3183

Answers (3)

BR41N-FCK
BR41N-FCK

Reputation: 766

The rule of thumb is if you've used new to create something then you have to use delete to destroy it. Pointers are all about manual memory management (they are heritage from the C language) or, as Scott Meyers once called them, "running with scissors". References are a C++ thing (or you can also check out smart pointers like std::shared_ptr).

UPDATE I thought it would be useful to show an example with std::shared_ptr. It has dumb pointer semantics, but its destructor -since it actually has one- calls pointed object's destructor.

#include <memory>
#include <iostream>
#include <string>
#include <type_traits>
using namespace std;


struct Simple {
    std::string txt;
    Simple(const std::string& str) : txt(str) {}
    Simple(){}
    ~Simple() { cout << "Destroyed " << txt << endl; }
};


template<class U> using SPtr = shared_ptr<U>;
template<class V> using PtrToSimple = typename conditional<is_same<SPtr<Simple>, V>::value,SPtr<Simple>,Simple*>::type;


template<class T>
void modify(PtrToSimple<T> pt) {

    pt->txt.append("_modified");
    // equivalent to (*pt).txt.append("_modified");
}


int main() {

    auto shared = shared_ptr<Simple>(new Simple("shared_ptr_obj"));
    modify<decltype(shared)>(shared);
    cout << shared->txt << endl;

    auto dumb = new Simple("dumb_ptr_obj");
    modify<decltype(dumb)>(dumb);
    cout << dumb->txt << endl;

    // The object pointed by 'shared'
    // will be automatically deleted.
    // dumb's object won't
    return 0;
}

prints

shared_ptr_obj_modified

dumb_ptr_obj_modified

Destroyed shared_ptr_obj_modified

Upvotes: 2

Loki
Loki

Reputation: 307

To make it easier to associate when a destructor will be called and when it wont be, use this rule of thumb: When the memory for an object is being reclaimed, then the object's destructor is called (and the memory is reclaimed right after that).

Your first example does not leak any memory. Here is why... You essentially created 2 objects.

A and B.

A was on the stack. The memory for object A was created implicitly on the stack.

B was created on the heap explicitly by your code.

When main() returned every Object on the stack is destroyed. i.e the memory that was being used to hold the members of the object on the stack (in this case object A) is being reclaimed implicitly. Since the object (A) is actually being destroyed and its memory being reclaimed, the destructor for A gets called.

Within A's destructor you are explicitly telling the runtime to reclaim all the memory what was explicitly allocated by your code (i.e when you call delete). Thus the memory for object B is being reclaimed too.

Thus there was no leak.

In the 2nd example, you again create 2 objects A and B. Here, the memory for both objects resides in the heap. This was allocated explicitly by your code using the new operator. In this case, your code never reclaims the memory allocated for A. i.e delete is never called for A.

The stack for main() only contains the memory for a pointer to A. The main() stack itself does not contain the memory for A. So when main() returns, all that is being destroyed is the memory that was allocated for the pointer to A, and not A itself. Since the memory for A was never reclaimed, it was never "destroyed". Thus its destructor was never called and correspondingly "B" was never destroyed either.

Upvotes: 5

DBug
DBug

Reputation: 2566

No, destructor is not implicitly called. you have to delete temp, at which time it's destructor is called. It could be argued that since it's main that's returning in your example, the process is going to exit and memory will be reclaimed by OS, but in general, each new needs a delete.

Upvotes: 2

Related Questions