从堆栈“移动”到堆?

时间:2019-02-05 15:49:06

标签: c++ c++17

我需要围绕C ++库编写C包装程序,并且需要在堆上分配对象。 C ++ lib的函数和方法使用并返回分配在堆栈上的对象。

我知道,我可以通过复制即auto heapObj = new Foo(stackObj);将对象从堆栈“转移”到堆上,但是如果可以的话,我想避免复制并尝试到move

这似乎“起作用”(令我惊讶)。幕后有事吗?如果不是,使用此模式是否安全?

main.h

class Foo {
   public:
    std::vector<int> big;

    explicit Foo(size_t len);

    Foo(Foo&& other) noexcept;

    // remove copy constructor
    Foo(const Foo &) = delete;

    // delete assignment operator
    Foo &operator=(const Foo &) = delete;


    size_t size();
};

main.cpp

#include <iostream>
#include "main.h"

Foo::Foo(size_t len) : big(len) {}

Foo::Foo(Foo&& other) noexcept : big(std::move(other.big)) {}

size_t Foo::size() { return this->big.size(); }

int main() {
    Foo ms(1000);  // on the stack
    ms.big[0] = 42;

    auto mh = new Foo(std::move(ms));  // on the heap (no copy?)

    std::cout << mh->size() << ", " << mh->big[0] << std::endl;

    delete mh;
}

2 个答案:

答案 0 :(得分:4)

首先,移动int或指针等效于副本。也就是说,如果您有一个

struct X {
  int a, b;
  int* data;
};

然后移动它不会比复制它便宜(现在忽略data的所有权)。巧合的是,以上基本上就是std::vector的样子:sizecapacity成员以及指向大块内存的一些指针。

移动与复制的重要之处在于资源所有权会发生什么。 std::vector拥有某些堆内存(data)的所有权。如果您复制 std::vector,则必须复制该堆内存,以便原始副本和副本都可以拥有自己的数据的所有权。但是,如果您移动,则只有移至的向量需要保留所有权,因此data指针可以从一个指针移到另一个指针(而不是所有数据),因为所有权可以从移出的对象中“窃取”。

这就是为什么将对象从堆栈“移动”到堆没有冲突的原因:对象本身基本上还是从一个位置复制到另一位置,但是将资源(或其子对象,如{{1 }})拥有者不会被复制而是被移动(“被盗”)。

答案 1 :(得分:1)

每一次“实际发生”移动,都是因为移动的事物中存在一些间接资源。可以廉价交换的句柄引用的某些资源(例如,指针被复制;指针被保留在原处)。通常,这是通过指向动态分配的事物(例如存储在向量中的数据)的指针来实现的。

您要移动的内容是矢量。这样,它已经被动态分配了,移动起来很容易。实际的std::vector对象生活在哪里,Foo生活在哪里都没有关系-如果有间接资源,则可能有可能搬家。

在其他情况下,移动构造函数或移动分配实际上只会触发内部任何数据的副本。当“事物”中的所有内容(递归)都具有自动存储期限时,您几乎可以保证将需要一个副本。但这不是事实。