0

I have been trying to understand how the clear() function in std::vector works, I am trying to emulate the workings of a std::vector.

So far, I have learned that clear() destroys all the objects but retains the capacity of the vector.

The bit I don't understand is how does the destructor of the objects in vector are called.

class A {
  public:
    A(int a) {
        m_a = a;
        cout << "Constructed object number: " << a << endl;
    }

    ~A() {
        cout << "Destructed object number: " << m_a << endl;
    }

    int m_a;
};

int main() {
  char** memory =  new char*[100];
  A t1(1);
  memory[sizeof(A)*10] = reinterpret_cast<char *>(&t1);
  A* t = reinterpret_cast<A*>(memory[sizeof(A)*10]);
  cout << t->m_a << endl;

  //Trying to figure out on how to clear the vector. 
  memory[sizeof(A)*10] = NULL;

  //Testing on how the destructor is getting called
  vector<A*> vec;
  vec.push_back(&A(2));  // I know it is wrong, just here for understanding purposes.
  A t2(3);
  vec.push_back(&t2);
  cout << "Clear" << endl;
  vec.clear();
  cout << "End" << endl;

  return 0;
}

Clearing the vector is not calling the destructor of "t2", as it is a pointer here, but if I store objects, than destructor of "t2" is getting called in clear function.

This is only for understanding purposes as to how the std::vector actually works.

  • 1
    Destructors are called manually, like `~foo()`. This may help explain how it generally works behind the scenes: https://stackoverflow.com/questions/5304048/stl-containers-allocation-placement-new – Retired Ninja Nov 07 '17 at 04:00
  • but wouldn't calling the destructor manually, destroy the pointers too? – Anurag Aggarwal Nov 07 '17 at 04:34
  • Have you looked in the header file to see how it's implemented? Since `std::vector` is a template, the code is in the header file. – user1118321 Nov 07 '17 at 04:42
  • I tried going through it, but for allocation and it uses allocator, whose details I could not find. – Anurag Aggarwal Nov 07 '17 at 04:48
  • 1
    The destructor is just a function. Just like the constructor. calling it is completely separate from actually freeing the memory associated with the object. Have you ever stepped into operator new()? It's a little weird the first time you do it. You see the compiler first allocating the memory at a very low level with malloc. THEN it manually calls the constructor on that block of memory. Containers like vector work in a similar fashion, it's just that they allocate memory for multiple objects and then call the constructors for each – Joe Nov 07 '17 at 05:06
  • *wouldn't calling the destructor manually, destroy the pointers too?* It would destroy *pointers*, not pointed-to objects. Destroying a raw pointer is a no-op. – n. m. could be an AI Nov 07 '17 at 10:56
  • The first half of your program has nothing to do with vectors. You are shuffling pointers around, but the vector contains `A` objects stored contiguously – M.M Nov 07 '17 at 11:11

2 Answers2

0

@Anurag one very important thing regarding STL container: it's keep the copy of the object/pointer/primitive type irrespective of the container type(sequence containers, associative containers and unordered container).

Now come back to your doubt 1. with object and 2. With pointer , below I am explaining with example :

  1. Example Of Object type (See the output, you will be clear):

#include <iostream> 
#include<vector>
using namespace std;
class Achintya {
 static int counter ;
 static int i ;
  public: 
  Achintya() {
        cout<<"Achintya Constructor called : " << ++i <<endl;
    }
  ~Achintya() {
        
        cout<<"Achintya destructor called : " << ++counter <<endl;
    }
    Achintya(const Achintya&) {
         cout<<"Achintya copy constructor  called : "<<endl;
    }
};

int Achintya:: i;
int Achintya:: counter;

int main() { 
    
    vector<Achintya> VecAchintya;
    
    Achintya  a1;
    // Address of the pointer 
    cout << " 1st object address : " <<&a1 <<endl;
   
    
    //Push back to vector 
    // it is just a copy and store in vector (copy constructor is begin invoke )
    VecAchintya.push_back(a1);
   
    
    cout << " =============================================" << endl;
    cout<< " Number of element present in vector is : " << VecAchintya.size() << endl;
    cout << " =============================================" << endl;
    
  
    // cli is not iterator 
    for(auto& cli:VecAchintya ) {
                cout  << " Adress  of  1st element is : " << &cli <<endl;
    }
    
    // it clear the object it self which is being created at the time of the push_back()
    VecAchintya.clear(); 
    
    cout << " =============================================" << endl;
    cout<< " Number of element present in vector is : " << VecAchintya.size() << endl;
    cout << " =============================================" << endl;
    
    
 }
output :: 

Achintya Constructor called : 1
 1st object address : 0x7ffd70ad339f
Achintya copy constructor  called : 
 =============================================
 Number of element present in vector is : 1
 =============================================
 Adress  of  1st element is : 0x23c6c30
Achintya destructor called : 1
 =============================================
 Number of element present in vector is : 0
 =============================================
Achintya destructor called : 2
  1. Example Of pointer type (See the output, you will be clear):

#include <iostream> 
#include<vector>
using namespace std;

class Achintya {
 static int counter ;
 static int i ;
  public: 
  Achintya() {
        cout<<"Achintya Constructor called : " << ++i <<endl;
    }
  ~Achintya() {
        
        cout<<"Achintya destructor called : " << ++counter <<endl;
    }
    Achintya(const Achintya&) {
         cout<<"Achintya copy constructor  called : "<<endl;
    }
  };

int Achintya:: i;
int Achintya:: counter;

int main() { 
    
    vector<Achintya *> VecAchintya;
    
    Achintya*  a1 = new Achintya();
    // Address of the pointer 
    cout << " 1st object address : " << a1 <<endl;
   
    
    //Push back to vector 
    // it is just a copy the pointer value and store 
    VecAchintya.push_back(a1);
   
    
    cout << " =============================================" << endl;
    cout<< " Number of element present in vector is : " << VecAchintya.size() << endl;
    cout << " =============================================" << endl;
    
  
    // cli is not iterator 
    for(auto& cli:VecAchintya ) {
                cout  << " Adress  of  1st element is : " << cli <<endl;
    }
    
    // it clear the pointer it self which is being stored at the time push_back()
    VecAchintya.clear(); 
    
    cout << " =============================================" << endl;
    cout<< " Number of element present in vector is : " << VecAchintya.size() << endl;
    cout << " =============================================" << endl;
    
    // call destructor explicitly 
    delete a1;
    
    
 }

 Output ::
 Achintya Constructor called : 1
 1st object address : 0x2533c20
 =============================================
 Number of element present in vector is : 1
 =============================================
 Adress  of  1st element is : 0x2533c20
 =============================================
 Number of element present in vector is : 0
 =============================================
Achintya destructor called : 1
  • So if I understand you correctly, you are saying that vector creates a copy of Pointer, not the object, and that copy of pointer is destructed. Not the object – Anurag Aggarwal Nov 07 '17 at 08:50
  • Yes your are correct, copy of the pointer is not destructed exactly . Lets take an example , int a; int *p = &a; int *q = p ; now p and q will point to same address,but if you set q =NULL , will your p is null?. no right. – Achintya paul Nov 07 '17 at 09:11
0

You may find it easier to study pop_back and think of resize down and clear as special multi-pop calls. You could certainly implement them that way in your own implementation.

Gem Taylor
  • 5,381
  • 1
  • 9
  • 27