【问题标题】:Using move to insert into a STL container使用 move 插入 STL 容器
【发布时间】:2014-12-30 15:44:22
【问题描述】:

我试图更好地理解将元素插入 STL 容器时移动的工作原理。我正在查看使用 new(在堆上创建)分配对象的代码。稍后,当插入完成时,STL 容器似乎分配了一个新对象并调用了移动构造函数。有没有办法让STL容器直接使用对象(接管被分配的对象(ptr))。

我有以下示例代码可以解决一些其他问题:

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

class foo
{
    public:
        string   s1;

        foo() = default;

        foo(const foo& fm)
        { cout << "copy ctor" << endl; }

        foo(const foo&& fm)
        { cout << "move ctor" << endl; }

        foo& operator= (const foo& fm)
        { cout << "copy assign" << endl; return *this; }
};

vector<foo> vec_f;

int main()
{
    foo f1;
    vec_f.push_back(f1);
    cout << endl;

    foo f2;
    vec_f.push_back(move(f2));
    cout << endl;

    foo *f3 = new (foo);
    vec_f.push_back(*f3);
    cout << endl;

    foo *f4 = new (foo);
    vec_f.push_back(move(*f4));
    cout << endl;
}

输出如下:

copy ctor

move ctor
copy ctor

copy ctor
copy ctor
copy ctor

move ctor

f1 : foo 是使用复制 ctor 创建的。但是,这个对象是否是由于 STL 代码执行新的 OR 部分将对象按值传递给函数(与 STL 无关,而是编译器的事情)而创建的?我猜是后者。如果是这样,当元素插入容器时,对象创建在哪里?

f2 : foo 是由 STL 代码使用 move ctor 创建的,对吗?为什么/如何在之后立即调用复制 ctor?是因为 f2 是在堆栈(而不是堆)上创建的吗?如果是这样,STL 代码是如何计算出来的?

f3 : 最让我费解的。猜测第一个复制 ctor 是作为按值传递参数的一部分调用的(与 STL 无关)。如何/为什么调用其他两个复制 ctor。

f4 : STL 调用 move ctor,因此 move ctor 可以使用 foo 对象的胆量。是否可以让 STL 代码只使用 ptr(因为它是在堆上创建的)。我的理解是 STL 容器中的对象无论如何都是在堆上创建的。

抱歉,帖子太长了。希望有人能解释一下。

问候, 艾哈迈德。

【问题讨论】:

  • 删除了您告诉我们忽略的所有位。下次请自己做。

标签: c++11 stl


【解决方案1】:

首先要意识到std::move 实际上并没有移动对象。它将把它 移动到哪里 ?它只允许移动。

要意识到的第二件事是vector 管理自己的内存。您可以将许多对象移动到 vector 中,但不能将内存分配给 vector(在这种情况下,您甚至根本不会移动对象)

接下来是vector::push_back。这可以采用T const&amp;T&amp;&amp;,具体取决于参数。只有后一种情况具有将值直接移动到vector 的适当形式。第一种和第三种情况失败,因为使用的表达式不能被移出。第二种情况看起来没有优化(那里的复制 ctor 可能会在发布版本中被优化)。

【讨论】:

  • #2 和#3 中的额外副本是由于重新分配和他的类的移动ctor 不是noexcept,所以push_back 必须复制以保持强大的异常安全保证。调试/发布与它无关。
【解决方案2】:

额外的复制和移动是由vector 自己进行的,因为vector 的大小发生了重新分配。

可以打印内存地址,如cout &lt;&lt; &amp;vec_f[0] &lt;&lt; endl;,每push_back()后其值都会改变

样本输出:

copy ctor
0077C350
move ctor
move ctor
0077C588
move ctor
move ctor
copy ctor
0077C690
move ctor
move ctor
move ctor
move ctor
0077C8A8

如果您在vector&lt;foo&gt; vec_f; 之后添加vec_f.reserve(4);,则输出将是:

copy ctor
0087C308
move ctor
0087C308
copy ctor
0087C308
move ctor
0087C308

这应该是你的期望。

附:我正在使用 VS2013 Express。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 1970-01-01
    • 2019-09-23
    • 2023-03-24
    • 2012-05-20
    相关资源
    最近更新 更多