我有以下代码。并试图了解向量中插入的工作原理。
// 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++;
}
这里有以下输出
--- 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
我可以理解上面的代码,因为这里我们正在重新分配向量空间,并且向量的元素使用复制构造函数进行复制,并调用旧的元素析构函数。
--- 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
这是我无法理解的问题。在上面的输出中如何看到下面的输出
Copy constructor called 5
Copy constructor called 0
另一个问题是为什么调用小部件析构函数。
Widget destructor is called 5
感谢您的时间和帮助。
我在下面提供窗口小部件代码以供参考
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
我可以理解上面的代码,因为这里我们正在重新分配向量空间
[std::vector
不会在每次插入时都重新分配,它分配的数量超出了所需的数量,以达到末尾重复插入平均平均为O(n)的要求。
[capacity()
告诉您分配了多少,并且通常大于size()
。
当它调整大小时,您将看到每个Widget都在使用它的副本构造函数,因为它构造了一个新的内部数组。
---插入前循环***大小:11容量:15 insertloc val:小部件值为0复制构造函数称为5复制构造函数称为0运算符=称为0运算符=称为0运算符=称为0运算符=称为0运算符=称为0运算符=称为0运算符=称为0运算符=称为0运算符=称为0运算符=叫5小部件析构函数称为5
因此,当插入data[1]
(5)时看起来。
先前的插入是在begin()
,并且新的迭代器在insert
之前返回到相同位置,然后将其递增1,所以insertLoc
将引用v[1]
。
因此,微件值4
已经在正确的位置,因此不需要移动。但是v[1]
已经被东西拿走了。看起来矢量已经分配了足够的空间,因此没有重新分配。
“复制构造函数称为5”是用于某些临时对象。
“复制构造函数称为0”是通过复制向前构造最后一个元素,将数组扩展1。内部数组必须已经足够大,因此没有发生重新分配(有点像v[v.size()] = v.back();
)。
“运算符=称为0”,是这样,然后将其余元素向前移动1,目标对象已被构造,因此使用分配。 v[i] = v[i - 1]
。
““操作员=被称为5”,然后在一切向前移动的情况下,将其临时对象分配到所需的v[1]
位置。
“小部件析构函数称为5,并破坏临时结构。
如果要尝试从完全相同的向量中插入一个值,并且需要调整大小,这将间接破坏引用的对象,则实现会创建插入到堆栈中的值的副本。这是您的复制控制权。然后,显然该副本被销毁了。如果您的班级有移动分配运算符,则可以看到对其的调用,而不是对常规分配运算符的调用。