В первом вызове конструктор Sales_data() используется непосредственно. Этот вызов создает временный объект класса Sales_data, используя конструктор Sales_data(), получающий строку. Во втором вызове используется оператор static_cast (см. раздел 4.11.3) для выполнения явного, а не неявного преобразования. В этом вызове оператор static_cast использует для создания временного объекта класса Sales_data конструктор с параметром типа istream.
У некоторых библиотечных классов, включая уже использованные ранее, есть конструкторы с одним параметром.
• Конструктор класса string, получающий один параметр типа const char* (см. раздел 3.2.1), не является явным.
• Конструктор класса vector, получающий размер вектора (см. раздел 3.3.1), является явным.
Упражнение 7.47. Объясните, должен ли быть явным конструктор Sales_data(), получающий строку. Каковы преимущества объявления конструктора явным? Каковы недостатки?
Упражнение 7.48. С учетом того, что конструктор Sales_data() не является явным, какие операции происходят во время следующих определений:
string null_isbn("9-999-99999-9");
Sales_data item1(null_isbn);
Sales_data item2("9-999-99999-9");
Что будет при явном конструкторе Sales_data()?
Упражнение 7.49. Объясните по каждому из следующих трех объявлений функции combine(), что происходит при вызове i.combine(s), где i — это объект класса Sales_data, a s — строка:
(a) Sales_data &combine(Sales_data);
(b) Sales_data &combine(Sales_data&);
(c) Sales_data &combine(const Sales_data&) const;
Упражнение 7.50. Определите, должен ли какой-либо из конструкторов вашего класса Person быть явным.
Упражнение 7.51. Как, по вашему, почему вектор определяет свой конструктор с одним аргументом как явный, а строка нет?
• Все его переменные-члены являются открытыми (public).
• Он не определяет конструкторы.
• У него нет никаких внутриклассовых инициализаторов (см. раздел 2.6.1).
• У него нет никаких базовых классов или виртуальных функций, связанных с классом средствами, которые рассматриваются в главе 15.
Например, следующий класс является агрегатным:
struct Data {
int ival;
string s;
};
Для инициализации переменных-членов агрегатного класса можно предоставить заключенный в фигурные скобки список инициализаторов для переменных-членов:
//
Data val1 = { 0, "Anna" };
Инициализаторы должны располагаться в порядке объявления переменных-членов. Таким образом, сначала располагается инициализатор для первой переменной-члена, затем для второй и т.д. Следующей пример ошибочен:
//
//
Data val2 = { "Anna" , 1024 };
Как и при инициализации элементов массива (см. раздел 3.5.1), если в списке инициализаторов меньше элементов, чем переменных-членов класса, последние переменные-члены инициализируются значением по умолчанию. Список инициализаторов не должен содержать больше элементов, чем переменных-членов у класса.
Следует заметить, что у явной инициализации переменных-членов объекта класса есть три существенных недостатка.
• Она требует, чтобы все переменные-члены были открытыми.
• Налагает дополнительные обязанности по правильной инициализации каждой переменной-члена каждого объекта на пользователя класса (а не на его автора). Такая инициализация утомительна и часто приводит к ошибкам, поскольку достаточно просто забыть инициализатор или предоставить неподходящее значение.
• Если добавляется или удаляется переменная-член, придется изменить все случаи инициализации.
Упражнение 7.52. На примере первой версии класса Sales_data из раздела 2.6.1 объясните следующую инициализацию. Найдите и исправьте возможные ошибки.
Sales_data item = {"978-0590353403", 25, 15.99};