Reputation: 11431
I have following code. And trying to understand how insert in vector works.
// create vector of 10 widget objects.
vector<Widget> v(10);
// create widget objects for insertion to vector.
Widget w1(4);
Widget w2(5);
Widget w3(6);
Widget data[] = { w1, w2, w3 };
// create insert location for beginning.
vector<Widget>::const_iterator insertLoc(v.begin());
for (int i = 0; i < 3; ++i) {
// Returns an iterator which points to the newly inserted element
// vector insert if first argument is q, then adds element before the element referenced by the iterator 'q'.
std::cout << "--- loop before insert *** size: " << v.size() << " capacity: " << v.capacity() << " insertloc val: " << *insertLoc << std::endl;
insertLoc = v.insert(insertLoc, data[i]);
insertLoc++;
}
Here I have following output
--- loop before insert *** size: 10 capacity: 10 insertloc val: Widget value is 0
Copy constructor called 4
Copy constructor called 0
Copy constructor called 0
Copy constructor called 0
Copy constructor called 0
Copy constructor called 0
Copy constructor called 0
Copy constructor called 0
Copy constructor called 0
Copy constructor called 0
Copy constructor called 0
Widget destructor is called 0
Widget destructor is called 0
Widget destructor is called 0
Widget destructor is called 0
Widget destructor is called 0
Widget destructor is called 0
Widget destructor is called 0
Widget destructor is called 0
Widget destructor is called 0
Widget destructor is called 0
I can understand above code as here we are re-allocating vector space and elments of vector are copied with copy constructor and old elements destructor is called.
--- loop before insert *** size: 11 capacity: 15 insertloc val: Widget value is 0
Copy constructor called 5
Copy constructor called 0
operator = called 0
operator = called 0
operator = called 0
operator = called 0
operator = called 0
operator = called 0
operator = called 0
operator = called 0
operator = called 0
operator = called 5
Widget destructor is called 5
Here is my question which I am not able to understand. How is following output is seeen in above output
Copy constructor called 5
Copy constructor called 0
and another question is why widget destructor is called.
Widget destructor is called 5
Thanks for your time and help.
I am providing Widget code below for reference
ifndef __WIDGET__
#define __WIDGET__
#include <iostream>
class Widget {
public:
Widget(int i = 0) : val(i), valid(true) { std::cout << "Constructor called val " << val << std::endl; }
virtual void draw(double ScaleFactor = 1) const;
int getVal() const { return val; }
int redraw() const {/* std::cout << "Drawing Widget(" << val << ")\n"; */ return 0; }
bool isCertified() /*const*/ { return val % 2 == 0;} // whether the Widget is certified
friend std::ostream &operator<<(std::ostream &, const Widget &);
friend std::istream &operator>>(std::istream &, Widget &);
friend bool operator!=(const Widget &, const Widget &);
friend bool operator==(const Widget &, const Widget &);
friend bool operator<(const Widget &, const Widget &);
int test(); // perform a self-test; mark *this
// Venkata added.
// Copy constructor
Widget(const Widget ©) :
val(copy.val), valid(copy.valid)
{
std::cout << "Copy constructor called " << val << "\n"; // just to prove it works
}
~Widget() {
std::cout << "Widget destructor is called " << val << "\n"; // just to prove it works
}
// Overloaded assignment
Widget& operator= (const Widget &w);
protected:
int val;
private:
bool valid;
};
void Widget::draw(double ScaleFactor) const
{
std::cout << "Drawing widget (val = " << val << ") using ScaleFactor " <<
ScaleFactor << "..." << std::endl;
}
// A simplistic implementation of operator= (see better implementation below)
Widget& Widget::operator= (const Widget &w)
{
// do the copy
val = w.val;
valid = w.valid;
std::cout << "operator = called "<< val << "\n"; // just to prove it works
// return the existing object so we can chain this operator
return *this;
}
inline bool operator!=(const Widget &w1, const Widget &w2)
{
std::cout << "Widget operator != called " << std::endl;
return (w1.val != w2.val);
}
inline bool operator==(const Widget &w1, const Widget &w2)
{
std::cout << "Widget operator == called " << std::endl;
return (w1.val == w2.val);
}
inline bool operator<(const Widget &w1, const Widget &w2)
{
std::cout << "Widget operator < called " << std::endl;
return (w1.val < w2.val);
}
inline std::ostream &operator<<(std::ostream &o, const Widget &w)
{
return o << "Widget value is " << w.val;
}
inline std::istream &operator>>(std::istream &o, Widget &w)
{
return o >> w.val;
}
inline int Widget::test() // perform a self-test; mark *this
{
return valid = (val % 2 == 0); // only "valid" if it is even
}
// End Widget.h
#endif
Upvotes: 1
Views: 823
Reputation: 468
In case you will try to insert a value from the very same vector and resize will be required, which will indirectly destroy referenced object, implementation creates a copy of the value inserted on the stack. This is your call of copy ctor. Then this copy is, obviously, destroyed. If you class had move assignment operator, you could see call to it instead of call to regular assignment operator.
Upvotes: 0
Reputation: 30115
I can understand above code as here we are re-allocating vector space
std::vector
does not reallocate on every insertion, it allocates more than required in order to achieve the requirement that repeated insertion at the end is O(n) on average.
capacity()
tells you how much it allocated, and will frequently be larger than size()
.
When it does resize, you will see that every Widget uses it's copy constructor, as it constructs a new internal array.
--- loop before insert *** size: 11 capacity: 15 insertloc val: Widget value is 0 Copy constructor called 5 Copy constructor called 0 operator = called 0 operator = called 0 operator = called 0 operator = called 0 operator = called 0 operator = called 0 operator = called 0 operator = called 0 operator = called 0 operator = called 5 Widget destructor is called 5
So this looks when data[1]
(5) is inserted.
The previous insert was at begin()
, with a new iterator returned to the same position by insert
, and then you incremented it by 1, so insertLoc
is going to refer to v[1]
.
So Widget value 4
is already in the correct position, and so does not need to be moved. But v[1]
is already taken by something. It also looks like the vector already allocated enough space, so there is no reallocation going on.
"Copy constructor called 5" is for some temporary object.
"Copy constructor called 0" is it expanding the array by one, by copy constructing the last element one forward. The internal array must have already been large enough, so no reallocation took place (so kinda like v[v.size()] = v.back();
).
"operator = called 0" these are it then moving the rest of the elements forward by 1, the destination objects are already constructed, so the assignment is used. v[i] = v[i - 1]
.
"operator = called 5" then with everything moved forward it assigns the temporary object it made to the desired v[1]
position.
"Widget destructor is called 5" and destructs the temporary.
Upvotes: 1