堆分配的对象能够移动到栈上吗?

7

问题

我们能否使用移动语义将堆上分配的对象移到栈上?

示例

#include <boost/asio.hpp>
#include <memory>

class connection
{
public:
    connection(boost::asio::ip::tcp::socket&& socket);

    void start();

private:
    boost::asio::ip::tcp::socket m_socket;
};

class server
{
public:
    // Not relevent here

private:
    void accept();

    boost::asio::io_service        m_io_service;
    boost::asio::ip::tcp::acceptor m_acceptor;
};

void server::accept()
{
    auto socket = new boost::asio::ip::tcp::socket(m_io_service);

    m_acceptor.async_accept(*m_socket,
    [&, socket](const boost::system::error_code& error)
    {
        if (!error)
        {
            auto connection = std::make_shared<connection>(std::move(*socket));
            connection->start();
        }

        accept();
    });
}
1个回答

9

std::move并不是将对象重新定位,而是移动值。(想一下在某些机器语言中的MOV R1, R2指令:它并没有移动寄存器,只是移动了内容!或者memmove库函数。这种“移动”的意义并不是在复制或压缩垃圾收集器中发生的那种移动,那种移动会导致对象在保留其确切身份的同时驻留在不同的地址,需要在计算机中找到每个对移动对象的引用,并更新它们。)

移动语义是C++中的一个新特性,它允许以一种不必保留旧值的方式复制对象。这在旧对象将不再需要时非常有用,因为它可以更快。

例如,将一个向量从一个std::vector<X>移动到另一个向量可能会导致源对象成为一个零长度向量,并且所有数据都可以在没有任何内存分配或复制的情况下移动。由于旧向量不再使用,所以被清空为零长度并不重要。

在不同存储位置(例如,从自由存储区(“堆”)到自动存储区(“栈”)),这种操作是可行的,因为在C++中,这些操作数之间的复制构造一直可以正常工作。

[编辑2020年]

然而,如果我们将一个对象从堆移动到其他地方,就会遇到问题。在堆中的旧内存被放弃而没有进行任何销毁。那块内存不再是一个对象;要再次使用它,我们必须将对象移动或构造到其中。否则,我们应该以低级方式删除它(而不调用析构函数)。


只是想澄清一下(我是个新手),当你说“复制”或“移动值”时,你的意思是“复制地址”或“移动地址”,对吗?我的理解是,当你从一个 std::vector<BigClass> 移动时,不是每个 BigClass 实例都被复制到新向量中,而是每个实例的地址都被复制到新向量中;这很快,因为你不必复制每个可能很大的 BigClass。这正确吗? - Ray
@Ray 完全不是这样。地址是不能移动的。对地址的引用(例如指针)可以移动,但这不是 C++ 中移动语义的重点。它是关于将一个值有效地移动到新的位置(新地址),而不需要构造一个新对象和销毁旧对象的开销,这在 C++ 中可能涉及构造函数和析构函数的调用。 - Kaz
@Ray 移动一个 std::vector 实际上是将底层的内存分配移动到另一个实例,这样另一个向量现在拥有引用的内存,而原始实例不再拥有任何东西。虽然第一个实例被移动后的状态取决于特定容器类型,但您仅保证它仍然可以在之后被销毁或重新分配,而不能保证它会变为空或任何其他可用状态。 (除非规范明确说明)。 - Ext3h

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接