Как работает vector?

143
24 апреля 2019, 04:20

Почему пример ниже выводит 6?

#include <iostream>
#include <vector>

using namespace std;
class A
{
    public:
        static int cnt;
        A(int a){}
        ~A(){cnt++;}
};
int A::cnt = 0;
int main()
{
    vector <A> a(4,1);
    a.push_back(1);
    cout << A::cnt;
    //cout << a.size();
}

Если убрать push_back, то выводит 1. Верно я понимаю, что вектор при инициализации и добавлении элементов выделяет в куче память для них, создаёт каждый элемент по отдельности, копирует его в это место и удаляет из этого места?

  1. Зачем так сложно? Почему он сразу не создаст эти элементы в выделенной для себя памяти?

  2. И даже если я верно сформулировал мысль, не понимаю, почему в 1м примере результат = 6, а не 5

Answer 1
  1. Для создания вектора вы воспользовались конструктром

    vector(size_type count, сonst T& value, const Allocator& alloc = Allocator());
    

    то есть ваше

    vector <A> a(4, 1);
    

    эквивалентно

    vector <A> a(4, A(1), std::allocator<A>());
    

    Последующее уничтожение неявно созданного вами временного объекта A(1) дало вам первый вызов деструктора.

  2. Для добавления нового элемента в вектор вы сделали

    a.push_back(1);
    

    что эквивалентно

    a.push_back(A(1));
    

    Уничтожение этого временного объекта A(1) дало вам еще один вызов деструктора.

  3. При добавлении объекта в вектор произошло перевыделение памяти вектора с перемещением содержимого уже существовавших четырех объектов на новое место, после чего старые объекты были уничтожены. Это еще 4 вызова деструкторов.

Итого - 6 вызовов деструкторов.

Зачем так сложно? Почему он сразу не создаст эти элементы в выделенной для себя памяти?

А почему именно так? Здесь возможны две очевидные стратегии: 1) использовать предоставленный инициализатор 1 для независимого создания 4 целевых объектов A, 2) сначала создать самостоятельный временный объект-образец типа A, а затем скопировать его 4 раза в вектор.

Кто вам сказал, что первый способ лучше второго? Дизайнеры стандартной библиотеки считают по-другому. (Или по крайней мере вынужденно считали тогда, когда в С++ не было средств форвардинга аргументов.) Очевидно, что при более-менее "тяжелой" конструкции, проще сконструировать сложный объект один раз и потом несколько раз скопировать готовое, чем снова и снова конструировать объекты с нуля.

Когда же вы добавляли один-единственный элемент в вектор, никто вас не заставлял идти по пути push_back с созданием промежуточного объекта. Вы могли сделать

a.emplace_back(1);

и действительно создать новый объект напрямую на месте.

READ ALSO
Сделать return массива

Сделать return массива

Не получается вернуть массив через функциюЯ создал функцию, которая создает массив и заполняет все его элементы двойками, но не могу вернуть...

159
Qt5 кроскомпиляция из под Windows в ARM

Qt5 кроскомпиляция из под Windows в ARM

Пытаюсь настроить Qt Creator (qt510

138
c++: выделение места под контейнер

c++: выделение места под контейнер

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

127