Как контейнеры STL копируют объекты?

Я знаю, что контейнеры STL, такие как vector, копируют объект при его добавлении. push_back метод выглядит следующим образом:

void push_back ( const T& x );

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

struct Foo
{
    Foo()
    {
        std::cout << "Inside Foo constructor" << std::endl;
    }

    Foo(const Foo& f)
    {
        std::cout << "inside copy constructor" << std::endl;
    }
};

Foo f;
std::vector<Foo> foos;
foos.push_back(f);

Это копирует объект, и я вижу, что он вызывает экземпляр-конструктор.

Мой вопрос: когда push_back принимает элемент в качестве ссылки, как он вызывает экземпляр-конструктор? Или я здесь что-то не хватает?

Любые мысли..?

Ответ 1

Вероятно, он использует "размещение new" для построения объекта в своем внутреннем массиве. Размещение new не выделяет никакой памяти; он просто помещает объект, который вы укажете, и вызывает конструктор. Синтаксис new (address) Class(constructor_arguments).

Конструктор копирования T::T(T const &) вызывается для создания копии на месте. Что-то вроде этого (упрощенного):

template<T>
void vector<T>::push_back(T const &item) {
    // resize if necessary
    new (&d_array[d_size++]) T(item);
}

Обратите внимание, что для T должен быть создан конструктор копирования. По умолчанию (если вы ничего не делаете), он получает один бесплатно. Если вы определяете его явно, для работы vector<T> должно быть public.

Вот как это делает GNU libstdС++, но я сомневаюсь, что это будет очень полезно. Существует распределитель (второй аргумент шаблона vector), который делает его менее простым.

Ответ 2

С++ SDK всегда использует const T & как функциональный параметр для эффективности.

В вашем случае, если в качестве параметра требуется T, действие копирования будет выполняться дважды, одно для передачи его функции push_back(f), одно для внутреннего добавления в контейнер. И, беря const T& в качестве параметра, требуется только одна копия!

Ответ 3

Он использует новый оператор размещения и копирует его в унифицированную память;

Размещение new создает новый элемент с указанным адресом в памяти, в векторном случае, текущий конец();

void push_back(const T& val){
::new (&*end()) T(val);
[increase end]
}

посмотрите http://spotep.com/dev/devector.h, который имеет совершенно четкий код (в отличие от большинства реализаций STL).