【问题标题】:vector resize behavior copy and assignment矢量调整大小行为复制和分配
【发布时间】:2019-12-17 08:58:34
【问题描述】:

我有以下代码。并试图了解插入向量的工作原理。

// 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

感谢您的时间和帮助。

我在下面提供 Widget 代码以供参考

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 &copy) :
        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

【问题讨论】:

  • 您是否介意在问题中包含 Widget 类。主要是复制/移动赋值运算符和构造函数及其析构函数?
  • 添加了小部件代码。谢谢

标签: c++ vector stl


【解决方案1】:

我可以理解上面的代码,因为这里我们正在重新分配向量空间

std::vector 不会在每次插入时重新分配,它分配的比需要的多,以达到最后重复插入平均为 O(n) 的要求。

capacity() 告诉您它分配了多少,并且通常会大于size()

当它调整大小时,你会看到每个 Widget 都使用它的复制构造函数,因为它构造了一个新的内部数组。

--- 插入前循环 *** size: 11 capacity: 15 insertloc val: Widget value is 0
复制构造函数调用 5
复制构造函数调用 0
运算符 = 调用 0
运算符 = 调用 0
运算符 = 调用 0
运算符 = 调用 0
运算符 = 调用 0
运算符 = 调用 0
运算符 = 调用 0
运算符 = 调用 0
运算符 = 调用 0
运算符 = 调用 5
Widget 析构函数被称为 5

所以当data[1] (5) 被插入时看起来是这样的。

之前的插入在begin(),一个新的迭代器由insert返回到相同的位置,然后你将它增加1,所以insertLoc将引用v[1]

所以小部件值4 已经在正确的位置,所以不需要移动。但是v[1] 已经被某些东西占用了。看起来向量已经分配了足够的空间,因此没有进行重新分配。

“名为 5 的复制构造函数”用于一些临时对象。

“名为 0 的复制构造函数”是通过将最后一个元素向前复制构造来将数组扩大一倍。内部数组必须已经足够大,所以没有发生重新分配(有点像v[v.size()] = v.back();)。

"operator = called 0" 这些是它然后将其余元素向前移动 1,目标对象已经构建,因此使用分配。 v[i] = v[i - 1].

"operator = called 5" 然后随着一切向前移动,它将临时对象分配到所需的v[1] 位置。

“Widget析构函数被称为5”并析构临时。

【讨论】:

  • 感谢您的详细解释。现在输出有意义了。
【解决方案2】:

如果您尝试从同一个向量中插入一个值并且需要调整大小,这将间接破坏引用的对象,则实现会创建一个插入到堆栈中的值的副本。这是您对复制 ctor 的调用。那么这个副本显然被销毁了。如果你的班级有移动赋值运算符,你可以看到对它的调用而不是对常规赋值运算符的调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多