7.5.3. Роль стандартного конструктора

Стандартный конструктор автоматически используется всякий раз, когда объект инициализируется по умолчанию. Инициализация по умолчанию осуществляется в следующем случае.

• При определении нестатических переменных (см. раздел 2.2.1) или массивов (см. раздел 3.5.1) в области видимости блока без инициализаторов.

• Когда класс, который сам обладает членами типа класса, использует синтезируемый стандартный конструктор (см. раздел 7.1.4).

• Когда переменные-члены типа класса не инициализируются явно в списке инициализации конструктора (см. раздел 7.1.4).

Инициализация значением по умолчанию осуществляется в следующем случае.

• Во время инициализации массива, когда предоставляется меньше инициализаторов, чем элементов массива (см. раздел 3.5.1).

• При определении локального статического объекта без инициализатора (см. раздел 6.1.1).

• Когда явно запрашивается инициализация значением по умолчанию в форме выражения Т(), где T — это имя типа. (Конструктор вектора, получающий один аргумент, чтобы определить размер вектора (см. раздел 3.3.1), использует аргумент этого вида для инициализации значением по умолчанию своего элемента.)

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

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

class NoDefault {

public:

 NoDefault(const std::string&);

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

};

struct А { // my_mem является открытой по умолчанию; см. раздел 1.2

 NoDefault my_mem;

};

А а;       // ошибка: невозможен синтезируемый конструктор для А

struct В {

 В() {}    // ошибка: нет инициализатора для b_member

 NoDefault b_member;

};

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

Применение стандартного конструктора

Следующее объявление объекта obj компилируется без проблем. Но при попытке его использования компилятор жалуется на невозможность применения к функции синтаксиса доступа к члену.

Sales_data obj(); // ok: но определена функция, а не объект

if (obj.isbn() == Primer_5th_ed.isbn()) // ошибка: obj - функция

Проблема в том, что, несмотря на намерение объявить инициализированный значением по умолчанию объект obj, фактически была объявлена функция без параметров, возвращающая объект типа Sales_data.

Чтобы правильно определить объект, использующий стандартный конструктор для инициализации, следует убрать пустые круглые скобки:

// ok: obj - объект, инициализированный значением по умолчанию

Sales_data obj;

Распространенной ошибкой среди новичков в С++ является объявление объекта, инициализированного стандартным конструктором, следующим образом:

Sales_data obj(); // упс! Это объявление функции, а не создание объекта

Sales_data obj2;  // ok: obj2 - это объект, а не функция

Упражнения раздела 7.5.3

Упражнение 7.43. Предположим, имеется класс NoDefault, у которого есть конструктор, получающий параметр типа int, но нет стандартного конструктора. Определите класс С, у которого есть переменная-член типа NoDefault. Определите стандартный конструктор для класса С.

Упражнение 7.44. Допустимо ли следующее объявление? Если нет, то почему?

vector<NoDefault> vec(10);

Упражнение 7.45. Определите вектор, содержащий объекты типа С из предыдущего упражнения?

Упражнение 7.46. Которое из следующих утверждений, если таковое имеется, ложно? Почему?

(a) Класс должен предоставить по крайней мере один конструктор.

(b) Стандартный конструктор — это конструктор с пустым списком параметров.

(c) Если для класса не нужно никаких значений по умолчанию, то класс не должен предоставлять стандартный конструктор.

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