我需要围绕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;
}
答案 0 :(得分:4)
首先,移动int
或指针等效于副本。也就是说,如果您有一个
struct X {
int a, b;
int* data;
};
然后移动它不会比复制它便宜(现在忽略data
的所有权)。巧合的是,以上基本上就是std::vector
的样子:size
和capacity
成员以及指向大块内存的一些指针。
移动与复制的重要之处在于资源所有权会发生什么。 std::vector
拥有某些堆内存(data
)的所有权。如果您复制 std::vector
,则必须复制该堆内存,以便原始副本和副本都可以拥有自己的数据的所有权。但是,如果您移动,则只有移至的向量需要保留所有权,因此data
指针可以从一个指针移到另一个指针(而不是所有数据),因为所有权可以从移出的对象中“窃取”。
这就是为什么将对象从堆栈“移动”到堆没有冲突的原因:对象本身基本上还是从一个位置复制到另一位置,但是将资源(或其子对象,如{{1 }})拥有者不会被复制而是被移动(“被盗”)。
答案 1 :(得分:1)
每一次“实际发生”移动,都是因为移动的事物中存在一些间接资源。可以廉价交换的句柄引用的某些资源(例如,指针被复制;指针被保留在原处)。通常,这是通过指向动态分配的事物(例如存储在向量中的数据)的指针来实现的。
您要移动的内容是矢量。这样,它已经被动态分配了,移动起来很容易。实际的std::vector
对象生活在哪里,Foo
生活在哪里都没有关系-如果有间接资源,则可能有可能搬家。
在其他情况下,移动构造函数或移动分配实际上只会触发内部任何数据的副本。当“事物”中的所有内容(递归)都具有自动存储期限时,您几乎可以保证将需要一个副本。但这不是事实。