2010-03-04 2 views
24

И как я могу написать свой собственный класс массива, чтобы не нужен конструктор по умолчанию для его элементов? Прямо сейчас, когда я делаю новый [] для выделения пространства, мне нужен конструктор по умолчанию.Почему элементам std :: vector не нужен конструктор по умолчанию?

std :: vector нет.

Как они делают эту магию?

ответ

7

Вы можете выделить блок байтов, а затем использовать placement new, чтобы создать новый экземпляр T (ваш параметрический тип) с помощью конструктора копирования (конечно, не конструктора по умолчанию), когда новые элементы будут перенесены обратно обратно. Это не позволит сделать «вектор N-инициализированного по умолчанию Ts» (который может вывести std :: vector, поэтому делает для того, чтобы T имел конструктор по умолчанию для этой цели), но вы могли бы сделать векторы которые начинаются пустым и могут набирать Ts.

+0

Как разместить пространство в первую очередь? таНос? – anon

+2

@anon: Посмотрите, как это делает 'vector' ... он использует распределитель, например' new_allocator'. В моей (старой) установке Cygwin он работает следующим образом: '{return static_cast <_Tp*> (:: operator new (__ n * sizeof (_Tp))); } 'Или' malloc_allocator' делает это так: 'pointer __ret = static_cast <_Tp*> (malloc (__ n * sizeof (_Tp)));' – Dan

26

std::vector не нужен конструктор по умолчанию, поскольку он никогда не использует его. Каждый раз, когда ему нужно построить элемент, он делает это, используя конструктор copy, потому что каждый раз, когда ему есть что-то копировать: либо существующий векторный элемент, либо элемент, который вы сами поставили для копирования через параметр метода (явно или неявно, опираясь на аргумент по умолчанию )

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

Каждый раз, когда она появляется, как будто std::vector «требует» конструктор по умолчанию от вас, это просто означает, что где-то вы полагались на аргумент по умолчанию некоторых из vector сек методов, т.е. был вы которые пытались default- построить элемент, а не вектор. Сам вектор, опять же, никогда не будет пытаться использовать элементы по умолчанию.

Для того, чтобы избежать требования конструктора по умолчанию во время выделения памяти, стандартная библиотека выделяет сырец неинициализированного блока памяти, а затем сразу же скопировать конструирует новые элементы в этом блоке сырой памяти (что-то new[] не может сделать). Эта функциональность включена в класс std::allocator. Вы также можете использовать std::allocator в своем коде, что означает, что «волшебство» сразу же доступно для вас.

Примечание: Вышеприведенное относится к предварительной версии языка C++ 11 на языке C++. C++ 11 многое изменило. И эти изменения создают ситуации, в которых std::vector могут использовать встроенные конструкторы по умолчанию.

Кроме того, возможно, стоит отметить, что даже оригинальную C++ 98 спецификация позволили реализациям использовать функции перегрузки вместо аргументов по умолчанию для реализации стандартного интерфейса библиотеки. Это означает, что формально можно иметь действительную реализацию C++ 98 из std::vector, которая использует стандартные конструкторы внутренне.

+3

, с другой стороны, они используют некоторую магию, которая очень близка: std :: объект-распределитель. –

+1

'std :: vector :: emplace_back()' вызывает стандартный конструктор. В общем случае 'emplace_back (Args ...)' вызывает конструктор с аргументами 'Args ...' –

+2

@ Ангелор, этот ответ был написан до того, как был выпущен C++ 11 :) На самом деле C++ 11 изменил поведение 'vector x (5) '- теперь это указано как вызов конструктора по умолчанию на месте 5 раз, тогда как в C++ 03 это означает, что вы по умолчанию строите один' X', а затем вектор использует copy-construction 5 раз, и ваш по умолчанию будет уничтожен после. –

11

std::vector требует, чтобы элемент имел конструктор по умолчанию, если вы используете его таким образом, который требует конструктор по умолчанию.Так что этот код (украдено из удаленного ответа) не будет компилироваться, потому что X не CTOR по умолчанию:

#include <vector> 

struct X 
{ 
    X(int) {} 
}; 

int main(void) 
{ 
    std::vector<X> x(1); // vector of length 1, second argument defaults to X() !! 
    return 0; 
} 

Но если вы пишете main как это вместо:

int main(void) 
{ 
    std::vector<X> x; // make empty vector 
    x.push_back(X(1)); 
    return 0; 
} 

Тогда это работает хорошо.

+10

Первая версия не будет компилироваться, потому что 'std :: vector x (1)' является сокращением для 'std :: vector x (1, X())'. Это на самом деле * вы *, кто подразумевает использование конструктора по умолчанию, а не 'vector'. Аргументы по умолчанию оцениваются «на вашей стороне». – AnT

+0

@AndreyT: отличная точка. – Dan

Смежные вопросы