Читаем Программирование полностью

Date holiday(1978, Date::jul, 4);    // инициализация

Date d2 = holiday;

Date d3 = Date(1978, Date::jul, 4);

holiday = Date(1978, Date::dec, 24); // присваивание

d3 = holiday;

Обозначение Date(1978, Date::dec, 24) означает создание соответствующего неименованного объекта класса Date, которое затем можно соответствующим образом использовать. Рассмотрим пример.

cout << Date(1978, Date::dec, 24);

В данном случае конструктор класса действует почти как литерал. Это часто удобнее, чем сначала создавать переменную или константу, а затем использовать ее лишь один раз.

А если нас не устраивает копирование по умолчанию? В таком случае мы можем либо определить свое собственное копирование (см. раздел 18.2), либо создать конструктор копирования и закрытый оператор копирующего присваивания (см. раздел 14.2.4). 

<p id="AutBody_Root170"><strong>9.7.3. Конструкторы по умолчанию</strong></p>

Неинициализированные переменные могут быть источником серьезных ошибок. Для того чтобы решить эту проблему, в языке С++ предусмотрено понятие конструктора, гарантирующее, что каждый объект класса будет инициализирован. Например, мы объявили конструктор Date::Date(int,Month,int), чтобы гарантировать, что каждый объект класса Date будет правильно проинициализирован. В данном случае это значит, что программист должен предоставить три аргумента соответствующих типов. Рассмотрим пример.

Date d1;                // ошибка: нет инициализации

Date d2(1998);          // ошибка: слишком мало аргументов

Date d3(1,2,3,4);       // ошибка: слишком много аргументов

Date d4(1,"jan",2);     // ошибка: неправильный тип аргумента

Date d5(1,Date::jan,2); // OK: используется конструктор с тремя

                        // аргументами

Date d6 = d5;           // OK: используется копирующий конструктор

Обратите внимание на то, что, даже несмотря на то, что мы определили конструктор для класса Date, мы по-прежнему можем копировать объекты класса Date. Многие классы имеют вполне разумные значения по умолчанию; иначе говоря, для них существует очевидный ответ на вопрос: какое значение следует использовать, если инициализация не выполнена? Рассмотрим пример.

string s1;             // значение по умолчанию: пустая строка ""

vector v1;     // значение по умолчанию: вектор без элементов

vector v2(10); // вектор, по умолчанию содержащий 10 строк

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

Для типа T обозначение T() — значение по умолчанию, определенное конструктором, заданным по умолчанию. Итак, можно написать следующий код: 

string s1 = string();   // значение по умолчанию: пустая строка ""

vector v1 = vector(); // значение по умолчанию:

                                 // пустой вектор; без элементов

vector v2(10,string());  // вектор, по умолчанию содержащий

                                 // 10 строк

Однако мы предпочитаем эквивалентный и более краткий стиль.

string s1;             // значение по умолчанию: пустая строка ""

vector v1;     // значение по умолчанию: пустой вектор;

                       // без элементов

vector v2(10); // вектор, по умолчанию содержащий 10 строк

Для встроенных типов, таких как int и double, конструктор по умолчанию подразумевает значение 0, так что запись int() — это просто усложненное представление нуля, а double() — долгий способ записать число 0.0.

  Опасайтесь ужасных синтаксических проблем, связанных с обозначением () при инициализации.

string s1("Ike"); // объект, инициализированный строкой "Ike"

string s2();      // функция, не получающая аргументов и возвращающая

                  // строку

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже