从堆栈移动到堆,向量和数组

时间:2020-01-15 08:16:05

标签: c++ memory

我制作了两个简单的程序,试图从不同的记忆中移出事物。

一种使用存储在堆上的向量,另一种使用数组。在运行了很多示例之后,我不理解输出。

我喜欢将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

只有副本,我不知道为什么。

3 个答案:

答案 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之前

enter image description here

在将1200堆栈上的400 kb移动到堆上之后

enter image description here