вызов выделения векторной памяти для конструктора копирования с функцией push_back

0
#include <vector>
#include <iostream>
#include <iterator>

using namespace std;

class MoveableClass
{
public:
        MoveableClass() {
                cout << "Default constructor" << endl;
        }
        MoveableClass(const MoveableClass& src) {
                cout << "Copy constructor" << endl;
        }
        MoveableClass(MoveableClass&& src) {
                cout << "Move constructor" << endl;
        }
        MoveableClass& operator=(const MoveableClass& rhs) {
                cout << "Copy assignment operator" << endl;
                return *this;
        }
        MoveableClass& operator=(MoveableClass&& rhs) {
                cout << "Move assignment operator" << endl;
                return *this;
        }
};

int main()
{
    vector<MoveableClass> vecSource(3);
    cout << "----" << endl;
    MoveableClass mc;
    cout << "----" << endl;
    vecSource.push_back(mc);
//      vecSource.push_back(mc);
//      vecSource.push_back(mc);
//      vecSource.push_back(mc);
    cout << "----" << endl;
    // Copy the elements from vecSource to vecOne
    vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());
    cout << "----" << endl;
    // Move the elements from vecSource to vecTwo
    vector<MoveableClass> vecTwo(make_move_iterator(vecSource.begin()),
                                                         make_move_iterator(vecSource.end()));
    cout << "----" << endl;

    return 0;
}

Из приведенного выше кода у меня есть 2 сомнения:

  1. Зачем перемещать ctor не вызывается из реализованного класса, когда я использую 2 функции push_back (mc) для копирования ctor 3 раза, т.е. 1 для первого нажатия, а для второго нажатия первого вектора изменяется (последовательно растет) в другое место памяти (которое должно иметь срабатывание для первого нажатия) 3-й для 2-го нажатия

  2. Даже когда я инициализирую векторный объект размером 3, почему вызов для копирования ctor увеличивается до 4 для одного push_back (mc).

Вывод:

Default constructor
Default constructor
Default constructor
----
Default constructor
----
Copy constructor
Copy constructor
Copy constructor
Copy constructor
----
Copy constructor
Copy constructor
Copy constructor
Copy constructor
----
Move constructor
Move constructor
Move constructor
Move constructor
----

gcc версия, которую я использую:

> gcc version 4.7.3

ОБНОВИТЬ

Спасибо за ответы, я получаю где-то

для моей 1) точки, которую я хочу добавить

//    MoveableClass(const MoveableClass& src) {
//                        cout << "Copy constructor" << endl;
//                }

    MoveableClass(MoveableClass&& src) noexcept {
                    cout << "Move constructor" << endl;
            }
    ....
    void fun() {
                    cout << "hello\n";
            }

    int main()
    {
            vector<MoveableClass> vecSource(3);
    //        vector<MoveableClass>::iterator it;
       //     vecSource.reserve(3);
            cout << "----" << endl;
            MoveableClass mc;
            cout << "----" << endl;
            mc.fun();
            vecSource.push_back(mc);
    //      vecSource.push_back(move(mc));
    //      vecSource.push_back(move_if_noexcept(mc));
    //      vecSource.push_back(mc);
    //      vecSource.push_back(mc);
    //      vecSource.push_back(mc);
    //        for(it = vecSource.begin(); it != vecSource.end(); ++it )
     //          cout << (*it).fun() << endl;
            cout << "----" << endl;
            // Copy the elements from vecSource to vecOne
            vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());
        cout << "----" << endl;
        // Move the elements from vecSource to vecTwo
        vector<MoveableClass> vecTwo(make_move_iterator(vecSource.begin()),
                                                             make_move_iterator(vecSource.end()));
        cout << "----" << endl;

        return 0;
}

Я редактировал код выше

//      vecSource.push_back(move(mc));               I can call move ctor only
//      vecSource.push_back(move_if_noexcept(mc));  I can call move ctor only
                 understood..

Если я комментирую конструктор копирования, я получаю ошибку компиляции

knils @knils-HP: IteratorAdapters $ g++ -g -std = С++ 0x MoveIterators.cpp

Внутренняя ошибка компилятора: снова введены процедуры регистрации ошибок.

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

См. Инструкции. Предварительно обработанный источник, хранящийся в файле /tmp/ccHhV599.out, приложите его к вашему отчету об ошибке.

почему он дает эту ошибку, почему не использует свою копию по умолчанию ctor

для 2) точки, когда я инициализирую для размера 3, означает ли это, что 3 ячейки памяти инициализируются экземпляром класса?

for(it = vecSource.begin(); it != vecSource.end(); ++it )
    cout << (*it).fun() << endl;

Я не могу использовать выше код. Он дает ошибку

MoveIterators.cpp:48:30: note: mismatched types 'const _CharT* and 'void

Чтобы добавить, я думаю, здесь лежит diff для изменения размера резерва, где резерв не вызывается по умолчанию ctor и оставляет память неинициализированной.

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

ОБНОВИТЬ

для частичного изменения кода

   vector<MoveableClass> vecSource;

    vecSource.push_back(mc);
    vecSource.push_back(mc);

Я получаю сообщение o/p

Копировать конструктор Копировать конструктор Переместить конструктор

Меня здесь смущает порядок. Я ожидаю его Копировать конструктор Переместить конструктор Копировать конструктор

потому что для первого нажатия It инициализирует один размер (копия) для второго, он перераспределяет, поэтому перемещает существующую память в новое местоположение (перемещение), а копия второго нажатия в новом месте (копировании) компилятора отличает почему..

С уважением!

  • 0
    Маркировка конструктора перемещения noexcept позволяет перемещать предыдущие три элемента, когда вы push_back четвертый элемент (с автоматическим перераспределением).
  • 0
    Если у вас есть два не связанных вопроса (а в вашем случае эти два вопроса не связаны), вы должны опубликовать два вопроса.
Теги:
c++11
constructor

2 ответа

5
Лучший ответ

вектор изменяется (последовательно растет) в другое место памяти (которое должно было вызвать движение для первого нажатия)

std::vector будет использовать конструктор move во время перераспределения, только если этот конструктор-конструктор объявлен с помощью спецификатора noexcept или если нет доступного конструктора-копии (подробнее см. std::move_if_noexcept):

Добавив следующие незначительные изменения:

MoveableClass(MoveableClass&& src) noexcept {
//                                 ~~~~~~~^
        cout << "Move constructor" << endl;
}

Вы получите результат:

Copy constructor
Move constructor
Move constructor
Move constructor

Спецификатор noexcept сообщает реализации std::vector что он может безопасно применять семантику move к ее контенту. В противном случае у вас не будет надежной гарантии безопасности, которая в основном заявляет, что вектор остается нетронутым, если перераспределение не удается из-за исключения:

§23.3.6.5 [vector.modifiers]/p1:

Требуется: если исключение генерируется иначе, чем конструктор копирования, переместите конструктор, оператор присваивания или оператор назначения перемещения из T или с помощью любой операции InputIterator никаких эффектов. Если исключение вызывается конструктором перемещения non- CopyInsertable T, эффекты не CopyInsertable T.

Кроме того, функция-член push_back не будет пытаться переместить-построить новый элемент, если его аргумент не может быть связан ссылкой на non- const rvalue, если нет, то он возвращается к построению копии. Если вы хотите переместить-построить новый элемент на основе экземпляра mc в вызове push_back, вам нужно передать значение x mc:

vecSource.push_back(std::move(mc));
//                  ~~~~~~~~^

Вывод:

Move constructor

Даже когда я инициализирую векторный объект размером 3, почему вызов для копирования ctor увеличивается до 4 для одного push_back (mc).

Первоначальная мощность vecSource, что можно запросить с .capacity() функцией - члена, предположительно установлена 3 в вашем случае, это означает, что любая попытку сохранить больше элементов приводят к необходимости перераспределения, который требует, чтобы все элементов уже сохраненный в векторе, который будет скопирован в новую ячейку памяти.

Можно избежать неожиданных перераспределений, зарезервировав достаточное количество места для хранения до вызовов push_back которые следуют:

vector<MoveableClass> vecSource;
vecSource.reserve(4);    // reserve a storage for 4 elements
vecSource.resize(3);     // default-construct 3 elements
cout << "----" << endl;
MoveableClass mc;
vecSource.push_back(mc); // copy-construct 4th element

Вывод:

Default constructor
Default constructor
Default constructor
----
Default constructor
Copy constructor

2), когда я инициализирую для размера 3, означает ли это, что 3 ячейки памяти инициализируются экземпляром класса?

Да, задав начальную емкость в вызове векторного конструктора или используя функцию члена resize, в С++ 11 вы получите это количество построенных по умолчанию (в С++ 03 - копирование из построенного по умолчанию элемента), которые готовы к доступу и использованию.


cout << (*it).fun() << endl;

Я не могу использовать выше код. Он дает ошибку

Вы не можете распечатать результат вызова функции, объявляющего void как возвращаемый тип. Просто удалите часть cout и скомпилируйте ее:

for(auto it = vecSource.begin(); it != vecSource.end(); ++it )
    (*it).fun();

Если я комментирую конструктор копирования, я получаю ошибку компиляции

Для некоторых операций требуется, чтобы тип векторного элемента был CopyConstructible; в вашем коде это:

vecSource.push_back(mc);
//...
vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());

"Копировать конструктор Копировать конструктор Переместить конструктор." Меня здесь смущает порядок. Я ожидаю его "Копировать конструктор Move constructor Copy constructor"

Для следующего фрагмента кода:

vector<MoveableClass> vecSource;
vecSource.push_back(mc);
vecSource.push_back(mc);

Согласно вашему результату, происходит следующее:

  1. Начальная емкость vector равна 0.
  2. Первый вызов push_back: mc копируется в новое выделенное хранилище (конструктор копирования).
  3. Второй вызов push_back: mc пытается быть вставлен в очередь. Емкость vector слишком мала, поэтому выделяется новое хранилище. Копия mc вставляется в новое хранилище (конструктор копирования). Затем остальные элементы перемещаются в новое место памяти (Move constructor).

Я не думаю, что порядок, в котором копирование-конструирование присоединенного элемента идет до перенастройки, задан Стандартом, а именно, как он реализован в libstdc++, который вы используете.


Боковые заметки:

  1. Предпочитаете -std=c++11 to -std=c++0x если компилятор поддерживает первое.

  2. Вы не должны повторно использовать экземпляр, который уже был перемещен. Надеюсь, вы делаете это только для тестирования.

  • 0
    Привет Петр Я не получаю vecSource.resize (3); // конструируем 3 элемента по умолчанию, инициализирует ли он 3 ячейки памяти, если мы можем использовать этот экземпляр для вызова функций-членов класса
  • 0
    @knils см. обновление
Показать ещё 3 комментария
0

Я не понял ваши вопросы. Сожалею. Но, на мой взгляд, единственное место в коде, где может возникнуть вопрос, почему не используется конструктор move, - это то, где вы вызываете push_back

vecSource.push_back( mc );

В этом месте вектор перераспределяет память, чтобы разместить еще один элемент, который является копией mc. Если бы вектор использовал бы конструктор перемещения для его уже существующих элементов, если в случае исключения состояние вектора было бы неопределенным. Использование конструктора копирования гарантирует, что даже в случае исключения состояние вектора будет действительным, поскольку исходные элементы не будут изменены.

Но если вы объявите конструктор перемещения как не выбрасывающее исключение, например

MoveableClass( MoveableClass &&src ) noexcept
{
    std::cout << "Move constructor" << std::endl;
}

то выход будет

Copy constructor
Move constructor
Move constructor
Move constructor

Ещё вопросы

Сообщество Overcoder
Наверх
Меню