Search code examples
c++c++11vectordestructorpush-back

Vector element Destructor not called with reserve


I have this class:

class aa
{
public:
  int i = 0;    
  ~aa(){
  std::cout << "killin in the name of" << std::endl;
    }
};

And I want to make a vector of this class. First I thought o reserving the needed size:

int main()
{
    std::vector<aa> vec;
    vec.reserve(2);
    vec[0] = *(new aa());
    vec[1] = *(new aa());
    //use the vector
    
    vec.clear();

    return 0;
}

But the destructor was not called. On the other side, when I fill the Vector using push_back

int main()
{
    std::vector<aa> vec;
    vec.push_back(*(new aa()));
    vec.push_back(*(new aa()));
    //use the vector
    
    vec.clear();

   return 0;
}

I actually get the destructor called.

Why?


Solution

  • A std::vector already does this memory management for you.

    When you use an std::vector with simple classes like this, you do not need any new or delete calls.

    reserve

    Under the hood, reserve is just making sure that a chunk of memory is preallocated to hold the specified number of member variables.

    resize

    Under the hood, resize will actually create n new objects. You do not need to explictly call new.

    Your example

    The code *(new aa()) will create a new aa object on the heap. When you write vec[0] = *(new aa()); it will attempt to copy the contents of your new object to the object that lives in the address vec[0]. Thus there are 2 distinct objects alive at this point in time ... one object vec[0] at one place in memory, and one object elsewhere in memory.

    What's worse is that you have now called new and never deleted that object. You thus have a memory leak.

    What you probably want

    Almost certainly, what you will want one of these scenarios.

    Create a vector, and then resize it to have n elements. Then use those elements:

    int main() {
        std::vector<aa> vec;
        vec.resize(2);
        vec[0].i = ...;
        vec[1].i = ...;
        //use the vector
        return 0;
    }
    

    push_back elements when you want to add things

    int main() {
        std::vector<aa> vec;
        vec.push_back(aa()); // Creates a new aa instance and copies into the vector 
    
        aa obj;
        vec.push_back(obj); // Copies the existing object's data into a new object in the vector. 
        //use the vector
        return 0;
    }
    

    The destructor of vector will delete all of the memory appropriately. No need to explicity clear in this example.

    There are more advanced ways that you can use vector, but until you understand this code, you probably should just stick to these basics.