调整大小时 std::vector 会复制/移动元素吗?

问题描述 投票:0回答:1

我正在与移动c'tors进行学习/刷新练习,我遇到了一些意想不到的事情。下面我有一个类

person
,其中包含
std::string m_name;
。我使用它作为复制/移动c'tors的测试类。

以下是快速参考的代码:

#include <iostream>
#include <vector>

class person
{
public:
    std::string m_name;
    explicit person(const std::string &name) : m_name(name)
    {
        std::cout << "created " << m_name << std::endl;     
    }
    
    ~person()
    {
        std::cout << "destroyed " << m_name << std::endl;       
    }   
    
    person(const person &other) : m_name(other.m_name)
    {
        m_name += ".copied";
        std::cout << "copied " << other.m_name << " -> " << m_name << std::endl;
    }
    
    person(const person &&other) noexcept : m_name(std::move(other.m_name))
    {
        m_name += ".moved";
        std::cout << "moved " << other.m_name << " -> " << m_name << std::endl;
    }   
};

int main()
{
    std::vector<person> people;
    people.reserve(10);
    
    std::cout << "\ncopy bob (lvalue):" << std::endl;
    person bob{"bob"};
    people.push_back(bob);

    std::cout << "\nmove fred (lvalue):" << std::endl;
    person fred{"fred"};
    people.push_back(std::move(fred));

    std::cout << "\ntemp joe (rvalue):" << std::endl;
    people.push_back(person{"joe"});
    
    std::cout << "\nterminating:" << std::endl;
}

这给了我期望的输出(主要是,除了为什么

std::string
内容没有“移动”?):https://godbolt.org/z/-J_56i

然后我删除

std::vector
reserve
,以便
std::vector
在我添加元素时必须“增长”。现在我得到了一些我真的没想到的东西:https://godbolt.org/z/rS6-mj

现在我可以看到 bob 被复制,然后在添加 fred 时移动,然后在添加 joe 时再次移动。我的印象是,当

std::vector
必须重新分配空间时,它会“移动”。但我认为它进行了内存复制/移动,而不是逐个对象的复制/移动。我真的没想到它会调用移动构造函数。

现在如果我删除 move c'tor,我发现 bob 被复制了三遍!:https://godbolt.org/z/_BxnvU 这看起来效率很低。

来自cplusplus.com:

push_back()

在末尾添加元素 在向量的末尾添加一个新元素, 在当前的最后一个元素之后。 val 的内容被复制(或 移动)到新元素。

这有效地将容器大小增加一倍,从而导致 自动重新分配已分配的存储空间当且仅当 新的向量大小超过了当前向量的容量。

调整大小()

调整容器大小,使其包含 n 个元素。

如果n小于当前容器大小,则内容为 减少到前 n 个元素,删除超出的元素(并销毁 他们)。

如果n大于当前容器大小,则内容为 通过在末尾插入所需数量的元素来扩展以达到 n 的大小如果指定了 val,则新元素将初始化为 val 的副本,否则,它们将被值初始化。

如果n也大于当前容器容量,则自动 已分配的存储空间发生重新分配。

注意这个函数改变了容器的实际内容 通过插入或删除其中的元素。

我想它并没有真正描述它“如何”进行重新分配,但内存复制肯定是将向量移动到新分配的内存空间的最快方法?

那么为什么当添加

std::vector
而不是内存复制时会调用复制/移动函数呢?

旁注/问题:(也许这应该是一个单独的问题):亲自移动c'tor为什么打印

moved fred -> fred.moved
而不是
moved -> fred.moved
。看起来
std::string
移动分配并没有真正“移动”数据......

c++ c++11 vector move-semantics
1个回答
4
投票

如果需要重新定位,会使用类似

std::move
(xold.begin(), xold.end(), xnew.begin());
的东西。这取决于值类型,向量通常会进行自己的内部放置 new 。但如果它能动的话它就会动。

你的移动构造函数

person(const person &&other) noexcept;

有一个缺陷:

other
不应该是
const
,因为必须允许它更改
other
窃取其资源。在这个移动构造函数中

person(person&& other) noexcept : m_name(std::move(other.m_name)) {}

std::string
自己的移动构造函数将执行与此类似的操作:

string(string&& other) noexcept : 
    the_size(other.the_size),
    data_ptr(std::exchange(other.data_ptr, nullptr))
{}

您还需要添加移动赋值运算符:

person& operator=(person &&other) noexcept;
© www.soinside.com 2019 - 2024. All rights reserved.