Требуется ли типу конструктор по умолчанию для объявления его массива? - PullRequest
3 голосов
/ 09 февраля 2010

Я заметил, что когда вы объявляете массив, необходим конструктор по умолчанию. Это правильно? Есть ли исключение?

Например,

struct Foo{
Foo(int i  ) {}
};

int main () {
    Foo f[5];
    return 0;
}

Код выше не компилируется.

Ответы [ 9 ]

7 голосов
/ 09 февраля 2010

Проблема вообще не связана с массивами.

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

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

Эта декларация

Foo f; // ERROR

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

Foo f(3); // OK

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

Foo f[5] = { 1, 2, 3, 4, 5 };

Конечно, если вы окажетесь в контексте, где синтаксис агрегатного инициализатора не разрешен (в текущей версии стандарта C ++), например, в списке инициализатора конструктора или в новом выражении, то вы действительно испорчены. В таком контексте единственный выход - предоставить конструктор по умолчанию в типе элемента массива (если вы придерживаетесь встроенных массивов).

7 голосов
/ 09 февраля 2010

С другими ответами все в порядке, но для полноты: Вы также можете использовать синтаксис инициализации массива:

Foo f[5] = {1,2,3,4,5};

Это работает, если ctor Foo не является явным.Если бы это было так, вы должны были бы быть ... явными:

Foo f[5] = {Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)};

Примечание 1 : между этими двумя случаями может быть разницане является очевидным и, следовательно, стоит отметить: во-первых, элементы массива напрямую создаются из int s в списке инициализации, вызывая Foo(int) ctor.Во втором список инициализации состоит из Foo s, созданных с помощью explicit Foo(int) ctor, а элементы массива представляют собой экземпляр, построенный из элементов в списке инициализации.Таким образом, в последнем случае требуется копия ctor для Foo.

[1] Спасибо MSalters за комментарий.

3 голосов
/ 09 февраля 2010

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

  1. Сделать массив вектором и передать ему нужный размер плюс один элемент - это дает каждому элементу одинаковый аргумент.

  2. Сделайте массив массивом указателей и создайте каждый элемент, используя цикл for и оператор new. Недостатком, конечно, является то, что вы должны освободить каждый элемент позже.

2 голосов
/ 09 февраля 2010

См. C ++ FAQ Lite, раздел 10,5

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

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

Предпочитаю использовать std :: vector вместо встроенных массивов.

1 голос
/ 09 февраля 2010

Нет, это не так.

Массив в C / C ++ - это блок памяти. Создание массива резервирует этот блок. Создание объектов: «1. Выделение пространства 2. Запуск конструкторов в каждом блоке» Итак, если у вас есть объект без конструктора, вы все равно можете создать его массив (так как этот объект имеет размер и память понимает » размер ").

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

1 голос
/ 09 февраля 2010

Обратите внимание, что конструктор по умолчанию не требуется, если вы используете std::vector вместо массива - вы можете указать конструктор, который будет использоваться:

std::vector <Foo> f;                // OK
std::vector <Foo> f( 5, Foo(0) );   // also OK
1 голос
/ 09 февраля 2010

Да, вам нужен конструктор по умолчанию, потому что Foo f[5]; на самом деле создает 5 Foo с. Вы можете обойти это, сделав Foo* f[5], а затем создав 5 Foo s с new.

Например:

Foo* f[5];
for(int i = 0; i < 5; ++i) {
    f[i] = new Foo(i);
}

// later on...
f[0]->whatever();
1 голос
/ 09 февраля 2010

Нет исключений. В том, что можно рассматривать как исключение, есть конструктор, объявленный конструктором по умолчанию.

0 голосов
/ 09 февраля 2010

Предупреждение: немного не по теме .

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

boost::optional<Foo> foos[4];  // Stack storage allocated but no objects 
                               // constructed (roughly equivalent to what 
                               // you get with vector<T>::reserve)

if(foos[2]) // Check if the third element has been constructed
{
     foos[2]->bar(); // Access members of Foo with arrow    
}

foos[1] = Foo(1, "a"); // Constructs the second element

foos[1].reset(); // Destroy second element (storage remains there though)

К сожалению, вы не сможете передать это функции, ожидающей истинное значение Foo[].

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...