什么时候调用移动构造函数而不是复制构造函数?

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

我阅读了 Microsoft 教程(位于此处:https://learn.microsoft.com/en-us/cpp/cpp/move-constructors-and-move-assignment-operators-cpp?view=msvc-170)关于移动构造函数和移动赋值运算符,我不太明白何时调用移动构造函数。

下面是MemoryBlock类的代码

class MemoryBlock
{
public:
    explicit MemoryBlock(size_t length)
        : m_length(length),
          m_data(new int[length])
    {
        std::cout << "In MemoryBlock(size_t). length = "
                  << m_length << "." << std::endl;
    }

    virtual ~MemoryBlock()
    {
        std::cout << "In ~MemoryBlock(). length = "
                  << m_length << ".";

        if (m_data != nullptr)
        {
            delete[] m_data;
            m_data = nullptr;
        }

        std::cout << std::endl;
    }

    // Copy ctor
    MemoryBlock(const MemoryBlock& other)
        : m_length(other.m_length),
          m_data(new int[other.m_length])
    {
        std::cout << "In MemoryBlock(const MemoryBlock&). length = "
                        << other.m_length << ". Copying resource." << std::endl;

        std::copy(other.m_data, other.m_data + m_length, m_data);
    }

    // Copy assignment operator
    MemoryBlock& operator=(const MemoryBlock& other)
    {
        std::cout << "In operator=(const MemoryBlock&). length = "
                        << other.m_length << ". Copying resource." << std::endl;

        if (this != &other)
        {
            delete[] m_data;

            m_length = other.m_length;
            m_data = new int[m_length];

            std::copy(other.m_data, other.m_data + m_length, m_data);
        }

        return *this;
    }

    // Move ctor
    MemoryBlock(MemoryBlock&& other) noexcept
        : m_length(0),
          m_data(nullptr)
    {
        std::cout << "In MemoryBlock(MemoryBlock&&). length = "
                     << other.m_length << ". Moving resource." << std::endl;

        m_data = other.m_data;
        m_length = other.m_length;

        other.m_data = nullptr;
        other.m_length = 0;
    }

    // Move assignment
    MemoryBlock& operator=(MemoryBlock&& other) noexcept
    {
        // Avoid assigning the object to itself
        if (this != &other)
        {
            // Free the existing resource
            delete[] m_data;

            m_data = other.m_data;
            m_length = other.m_length;

            other.m_data = nullptr;
            other.m_length = 0;
        }

        return *this;
    }

private:
    size_t m_length;
    int *m_data;
};

我不明白的主要是。 理解为什么调用移动构造函数而不是复制构造函数

int main()
{
    std::vector<MemoryBlock> v;

    // In this way the copy ctor is called
    MemoryBlock m(10);
    v.push_back(m);

    // And in this way the move ctor is called, why if std::move is not invoked?
    v.push_back(MemoryBlock(25));

    return 0;
}

编译构建代码并尝试理解,也在网上搜索资料无果

c++ move-semantics
3个回答
1
投票

这是因为当你写

v.push_back(m);
时,表达式
m
具有lvalue的值类别,所以使用了
push_back( const T& )
的左值引用版本
std::vector::push_back
,它使用了复制初始化

另一方面,当您编写

v.push_back(MemoryBlock(25));
时,表达式
MemoryBlock(25)
是一个 rvalue,因此使用
push_back( T&& value )
std::vector::push_back
版本。所以这个版本使用移动构造函数。

来自std::vector::push_back:

void push_back( const T& value );      (1)
void push_back( T&& value );           (2)

将给定的元素值附加到容器的末尾。

  1. 新元素被初始化为值的副本
  2. value moved into 新元素。

0
投票

MemoryBlock&&
是一个 rvalue reference。所以移动构造函数:

MemoryBlock(MemoryBlock&& other) noexcept

只要你传递一个右值就会被调用。有很多方法可以创建右值。

std::move
只是其中一种方式。其他人,就像你的情况,正在创建一个未命名的临时文件。这也可以通过一个函数来完成:

MemoryBlock foo() { ... }

...

MemoryBlock m{foo()};

所有这些都是右值,所以移动构造函数被调用。


0
投票

总的来说:

  • 移动构造函数在
    rvalues
    用于创建新对象时被调用
  • 复制构造函数在
    lvalue
    用于创建新对象时调用
  • 当左手边的对象是
    rvalues
  • 时调用移动赋值运算符
  • 当左手边对象是
    lvalues
  • 时调用赋值运算符

回答您的问题:我不明白的主要是。理解为什么调用移动构造函数而不是复制构造函数

v.push_back(m);
行中,
m
是一个左值,因此调用了复制构造函数。在
v.push_back(MemoryBlock(25));
行中,
MemoryBlock(25)
是右值,因此调用了移动构造函数。

以下是关于此的有用阅读:

© www.soinside.com 2019 - 2024. All rights reserved.