我制作了两个简单的程序,试图从不同的记忆中移出事物。
一种使用存储在堆上的向量,另一种使用数组。在运行了很多示例之后,我不理解输出。
我喜欢将move运算符看作是指针之间的交换,我可能是错的,但是如果一个堆ptr从堆栈中接收到一个指针,则当清除它时,除非有一个deap副本,否则堆ptr不会指向任何有价值的东西完成了吗?
带有矢量:
#include <iostream>
#include <utility>
#include <vector>
#include <array>
#include <string>
using namespace std;
struct Bitmap {
Bitmap() {}
Bitmap(string n) : name(n) {}
Bitmap(const Bitmap& other) {
name = other.name;
std::cout << "copy constructor" << std::endl;
}
Bitmap& operator=(const Bitmap& other) {
name = other.name;
std::cout << "assignment operator" << std::endl;
}
Bitmap(Bitmap&& other) {
name = move(other.name);
std::cout << "move constructor" << std::endl;
}
string name;
};
struct BitmapContainer {
BitmapContainer() {}
std::vector<Bitmap> data;
};
int main() {
BitmapContainer stackContainer;
BitmapContainer* heapContainer = new BitmapContainer();
Bitmap test;
Bitmap test2;
Bitmap* test3 = new Bitmap();
std::cout << "only on stack" << std::endl;
stackContainer.data.push_back(move(test));
std::cout << "stack to heap" << std::endl;
heapContainer->data.push_back(move(test2));
std::cout << "heap to heap" << std::endl;
heapContainer->data.push_back(move(*test3));
}
输出:
only on stack
move constructor
stack to heap
move constructor
heap to heap
move constructor
copy constructor
前两个动作表明可以将对象从一个内存移动到另一个内存,即使我的向量在堆上,第一个也应该像第二个一样失败。而且我不知道为什么最后要调用一个拷贝构造函数。
现在,如果我改为使用数组:
struct BitmapContainer {
BitmapContainer() {}
std::array<Bitmap, 2> data;
};
int main() {
BitmapContainer stackContainer;
BitmapContainer* heapContainer = new BitmapContainer();
Bitmap test;
Bitmap test2;
Bitmap* test3 = new Bitmap();
std::cout << "only on stack" << std::endl;
stackContainer.data[0] = move(test);
std::cout << "stack to heap" << std::endl;
heapContainer->data[0] = move(test2);
std::cout << "heap to heap" << std::endl;
heapContainer->data[1] = move(*test3);
}
输出:
only on stack
assignment operator
stack to heap
assignment operator
heap to heap
assignment operator
只有副本,我不知道为什么。
答案 0 :(得分:4)
我不明白为什么最后要调用一个拷贝构造函数。
当向量重新分配时使用复制构造函数,这意味着它的容量已满,并且没有更多空间可以在末尾插入新的(推回)元素。重新分配基本上分配了一个新的内存空间,并从原始内存中“移动”实际元素。
但是,这种“移动”是在您的情况下由复制构造函数实现的。原因是您的move构造函数不是noexcept
,并且std::vector
在这里更倾向于 strongexception guarantee 而不是性能。
如果将Bitmap::Bitmap(Bitmap&&)
构造函数设为noexcept
,请移动构造函数will be used instead。您可以放心地执行此操作,因为std::string
的move构造函数是guaranteed to be noexcept
。
为使比较合理,在两种情况下都使用不同的向量,或者reserve
预先为两个元素存储一个内存。
对于数组,您没有为Bitmap
定义移动赋值运算符。因此,除了使用定义的副本分配运算符分配数组元素外,没有其他选择。使用std::move
不能对此进行任何更改。
答案 1 :(得分:2)
您的vector
在堆栈或堆上,但是vector
的内容始终在堆上,这就是为什么前两种情况没有区别的原因。
对于array
,内容是结构的一部分,因此,如果array
位于堆栈中,则元素也位于堆栈中。
答案 2 :(得分:1)
只是想对Daniel提出的解决方案添加我的个人看法
移动不仅是两个对象之间的指针交换。它将在其所有成员上递归调用move构造函数,最后导致移动基本类型(int,array,float),这将导致复制。因此,如果将分配给堆栈的对象移动到向量,则将复制该对象中包含的所有基本类型。
此操作仅对仅留在堆(字符串,向量,对象*)上的Types有用,您可能希望在那里智能地交换指针。
如果我们以这个例子为例,看一下内存的足迹,最后我将数组复制到堆内存中。
struct Bitmap {
array<float, 100000> data;
};
int main() {
std::vector<Bitmap> bitmapContainer;
Bitmap stackObject;
Bitmap* heapObject = new Bitmap();
std::cout << "here " << std::endl;
auto c = getchar();
bitmapContainer.push_back(move(stackObject));
bitmapContainer.push_back(move(*heapObject));
std::cout << "after " << std::endl;
c = getchar();
}
在将堆栈上的400kb移动到堆上的400k之前
在将1200堆栈上的400 kb移动到堆上之后