Перемещение из стека в кучу, вектор и массив - PullRequest
0 голосов
/ 15 января 2020

Я сделал две простые программы, в которых я пытаюсь переместить вещи из разных воспоминаний.

Один использует вектор, хранящийся в куче, другой - массив. После запуска примеров thoose, я не понимаю вывод.

Мне нравится видеть оператор перемещения как обмен между указателями, я могу ошибаться, но если ptr кучи получает указатель из стека, когда он очищается, куча 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));
}

output:

only on stack
move constructor
stack to heap
move constructor
heap to heap
move constructor
copy constructor

2 first Перемещение подразумевает, что можно перемещать объект из одной памяти в другую, даже если мой вектор находится в куче, первый должен был потерпеть неудачу, как и второй. И я не понимаю, почему у меня в конце вызывается конструктор копирования.

Теперь, если вместо этого я использую массив:

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);
}

output:

only on stack
assignment operator
stack to heap
assignment operator
heap to heap
assignment operator

Только копии, я не знаю почему.

Ответы [ 3 ]

4 голосов
/ 15 января 2020

И я не понимаю, почему в конце я получил конструктор копирования.

Конструктор копирования используется, когда вектор перераспределяет , что означает , что его емкость заполнена и больше нет места для вставки нового (отодвинутого) элемента в конце. Перераспределение в основном выделяет новое пространство памяти и «перемещает» фактические элементы из исходной памяти.

Однако этот «ход» реализован с помощью копировщика в вашем случае. Причина в том, что ваш конструктор перемещения не является noexcept, а std::vector предпочитает строгое исключение , а не производительность.

Если вы сделаете ваш Bitmap::Bitmap(Bitmap&&) конструктор noexcept, вместо него будет использоваться конструктор перемещения . Вы можете сделать это безопасно, поскольку конструктор перемещения std::string равен , гарантированно равным noexcept.

Чтобы сделать сравнение справедливым, либо используйте разные векторы в обоих случаях, либо reserve память для обоих элементов заранее.


Что касается массивов, вы не определяете оператор присваивания перемещения для Bitmap. Следовательно, нет другого выбора, кроме как использовать определенный оператор назначения копирования для назначения элементов массива. Использование std::move не может ничего изменить.

2 голосов
/ 15 января 2020

Ваш vector находится в стеке или куче, но содержимое vector всегда в куче, поэтому нет разницы между вашими первыми двумя случаями.

С array содержимое является частью структуры, поэтому, если array находится в стеке, элементы также находятся в стеке.

1 голос
/ 15 января 2020

Просто хочу добавить мой личный взгляд на решение, предложенное Дэниелом

Перемещение - это не просто замена указателя между двумя объектами. Он будет рекурсивно вызывать конструктор перемещения для всех своих членов, в конце концов получая перемещение типа basi c (int, array, float), что приведет к копированию. Поэтому, если вы переместите объект, размещенный в стеке, в вектор, вы скопируете все базовые c типы, содержащиеся в объекте.

Этот шаг полезен только для типов, которые остаются только в куче (строка, vector, Object *), там вы можете ожидать умного обмена указателями.

Если мы возьмем этот пример и посмотрим на память footPrint, в конце я дублировал массив в кучной памяти.

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();
}

до перемещения 400 КБ в стеке 400 КБ в куче

enter image description here

после перемещения 400 КБ в стеке 1200 в куче

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...