C ++: Добавление автоматически размещаемых объектов в std :: vector - PullRequest
4 голосов
/ 01 июня 2011

Я написал следующий код:

#include <iostream>
#include <vector>
using namespace std;

class AClass
{
    public:
        int data;

        AClass() 
        { data = -333; cout << "+ Creating default " << data << endl; }

        AClass(const AClass &copy) 
        { data = copy.data; cout << "+ Creating copy of " << data << endl; }

        AClass(int d) 
        { data = d; cout << "+ Creating " << data << endl; }

        ~AClass() 
        { cout << "- Deleting " << data << endl; }

        AClass& operator = (const AClass &a)
        {  data = a.data; cout << "= Calling operator=" << endl; }
};

int main(void)
{
    vector<AClass> v;

    for (int i = 3; i--; )
        v.push_back(AClass(i));

    vector<AClass>::iterator it = v.begin();
    while (it != v.end())
        cout << it->data << endl, it++;

    return 0;
}

И вывод из программы:

+ Creating 2
+ Creating copy of 2
- Deleting 2
+ Creating 1
+ Creating copy of 1
+ Creating copy of 2
- Deleting 2
- Deleting 1
+ Creating 0
+ Creating copy of 0
+ Creating copy of 2
+ Creating copy of 1
- Deleting 2
- Deleting 1
- Deleting 0
2
1
0
- Deleting 2
- Deleting 1
- Deleting 0

Затем я изменил класс на:

class AClass
{
    public:
        int data;

        AClass(int d) 
        { data = d; cout << "+ Creating " << data << endl; }

        ~AClass() 
        { cout << "- Deleting " << data << endl; }
};

И получается результат:

+ Creating 2
- Deleting 2
+ Creating 1
- Deleting 2
- Deleting 1
+ Creating 0
- Deleting 2
- Deleting 1
- Deleting 0
2
1
0
- Deleting 2
- Deleting 1
- Deleting 0

Кажется, что vector делает копии существующих объектов, когда добавляются новые, но кажется, что происходит много ненужного выделения / удаления.Почему это?Кроме того, почему вторая версия работает, если я не предоставил конструктор копирования?

Ответы [ 4 ]

5 голосов
/ 01 июня 2011

Похоже, что vector делает копии существующих объектов при добавлении новых

Когда вы добавляете элемент, например, с v.push_back(AClass(i));, то, что делается, является временным AClass объект создан и передан push_back.push_back должен затем скопировать этот объект в контейнер.

Другая причина, по которой вы видите сделанные копии, заключается в том, что std::vector хранит свои элементы непрерывно в массиве.Если в базовом массиве не осталось места и вы пытаетесь добавить еще один элемент в конец, std::vector должен создать новый массив, скопировать элементы из старого массива в новый, а затем вставить новый элемент вконец.Если вы не хотите, чтобы это происходило, вы можете вызвать std::vector::reserve, чтобы зарезервировать достаточно места в std::vector, прежде чем начинать вставлять элементы, или вы можете использовать другой контейнер последовательности, такой как std::deque, который не хранит егоэлементы смежно.

кажется, что происходит много ненужного выделения / удаления

В программе на C ++ объекты часто создаются и уничтожаются.Обратите внимание, что в вашей программе AClass очень дешево копировать: его размер, вероятно, составляет четыре или восемь байтов, достаточно большой, чтобы вместить его элемент данных int.

Если у вас есть тип, который копируется дорого (например, возможно, у вас есть большая древовидная структура данных с тысячами узлов), тогда да, копирование может быть слишком дорогим.В этом случае вы можете хранить умные указатели для динамически размещаемых объектов в std::vector (например, std::vector<shared_ptr<AClass> >).Если ваш компилятор поддерживает ссылки на rvalue и имеет реализацию стандартной библиотеки с поддержкой перемещений, вы можете сделать перемещаемый тип дорогостоящим для копирования, реализовав конструктор перемещения и оператор присваивания перемещения и используя emplace_back вместо push_back.

почему вторая версия работает, когда я не предоставил конструктор копирования?

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

5 голосов
/ 01 июня 2011

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

Во второй версии, даже если вы не предоставите конструктор копирования, он будет создан автоматически.Если вы объявите приватный, а затем не реализуете его, вы увидите ошибку компилятора (потому что вы подавили генерацию по умолчанию)

3 голосов
/ 01 июня 2011

Ваши объекты копируются, потому что vector расширяет свое внутреннее хранилище.Вызовите vector::reserve заранее, чтобы предварительно выделить память, если вы хотите избежать копирования.Если вы не предоставите свой собственный ctor, компилятор сгенерирует его для вас (тот, который копирует всех участников).

0 голосов
/ 01 июня 2011

в первую очередь cunstructor копирования обычно генерируется самим c ++, если вы его не предоставляете, а vector копирует все данные, так как не уверен, что переменная, которую вы ему дали, имеет допустимое значение всякий раз, когда вы ее запрашиваете, например, выможет использовать некоторую локальную переменную и передать ее вектору, если вектор не скопирует то, что вы ему дали, и вы вернете этот вектор, будут некоторые нарушения памяти.Когда вы добавляете в него какой-то новый объект, и ему нужен больший массив для хранения всего объекта, он выделяет новый массив, а затем копирует все существующие данные в новый массив.

...