Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 96 97 98 99 100 101 102 103 104 ... 297
Перейти на страницу:

Конструкторы класса Sales_data, получающие строку и объект класса istream, оба определяют неявные преобразования из этих типов в тип Sales_data. Таким образом, можно использовать тип string или istream там, где ожидается объект типа Sales_data:

string null_book = "9-999-99999-9";

// создает временный объект типа Sales_data

// с units_sold и revenue равными 0 и bookNo равным null_book

item.combine(null_book);

Здесь происходит вызов функции-члена combine() класса Sales_data со строковым аргументом. Этот вызов совершенно корректен; компилятор автоматически создаст объект класса Sales_data из данной строки. Этот вновь созданный (временный) объект класса Sales_data передается функции combine(). Поскольку параметр функции combine() является ссылкой на константу, этому параметру можно передать временный объект.

Допустимо только одно преобразование типов класса

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

// ошибка: требует двух пользовательских преобразований:

//   (1) преобразование "9-999-99999-9" в string

//   (2) преобразование временной строки в Sales_data

item.combine("9-999-99999-9");

Если данный вызов необходим, это можно сделать при явном преобразовании символьной строки в объект класса string или в объект класса Sales_data:

// ok: явное преобразование в string,

// неявное преобразование в Sales_data

item.combine(string("9-999-99999-9"));

// ok: неявное преобразование в string,

// явное преобразование в Sales_data

item.combine(Sales_data("9-999-99999-9"));

Преобразования типов класса не всегда полезны

Желательно ли преобразование типа string в Sales_data, зависит от конкретных обстоятельств. В данном случае это хорошая идея. Строка в переменной null_book, вероятнее всего, соответствует несуществующему ISBN.

Преобразование из istream в Sales_data более проблематично:

// использует конструктор istream при создании объекта для передачи

// функции combine

item.combine(cin);

Этот код неявно преобразует объект cin в объект класса Sales_item. Это преобразование осуществляет тот конструктор класса Sales_data, который получает тип istream. Этот конструктор создает (временный) объект класса Sales_data при чтении со стандартного устройства ввода. Затем этот объект передается функции same_isbn().

Этот объект класса Sales_item временный (см. раздел 2.4.1). По завершении функции combine() никакого доступа к нему не будет. Фактически создается объект, удаляющийся после того, как его значение добавляется в объект item.

Предотвращение неявных преобразований, осуществляемых конструктором

Чтобы предотвратить использование конструктора в контексте, который требует неявного преобразования, достаточно объявить его явным (explicit constructor) с использованием ключевого слова explicit:

class Sales_data {

public:

 Sales_data() = default;

 Sales_data(const std::string &s, unsigned n, double p):

            bookNo(s), units_sold(n), revenue (p*n) { }

 explicit Sales_data(const std::string &s): bookNo(s) { }

 explicit Sales_data(std::istream&); // остальные члены, как прежде

};

Теперь ни один из конструкторов не применим для неявного создания объектов класса Sales_data. Ни один из предыдущих способов применения теперь не сработает:

item.combine(null_book); // ошибка: конструктор string теперь явный

item.combine(cin);       // ошибка: конструктор istream теперь явный

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

// ошибка: ключевое слово explicit допустимо только для

// объявлений конструкторов в заголовке класса

explicit Sales_data::Sales_data(istream& is) {

 read(is, *this);

}

Явные конструкторы применяются только для прямой инициализации

Одним из контекстов, в котором происходит неявное преобразования, является использование формы инициализации копированием (со знаком =) (см. раздел 3.2.1). С этой формой инициализации нельзя использовать явный конструктор; придется использовать прямую инициализацию:

Sales_data item1(null_book); // ok: прямая инициализация

// ошибка: с явным конструктором нельзя использовать форму

// инициализации копированием

Sales_data item2 = null_book;

Явный конструктор применим только с прямой формой инициализации (см. раздел 3.2.1). Кроме того, компилятор не будет использовать этот конструктор в автоматическом преобразовании.

Применение явных конструкторов для преобразований

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

// ok: аргумент - явно созданный объект класса Sales_data

item.combine(Sales_data(null_book));

// ok: static_cast может использовать явный конструктор

item.combine(static_cast<Sales_data>(cin));

В первом вызове конструктор 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.5.4

Упражнение 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. Как, по вашему, почему вектор определяет свой конструктор с одним аргументом как явный, а строка нет?

7.5.5. Агрегатные классы

Агрегатный класс (aggregate class) предоставляет пользователям прямой доступ к своим членам и имеет специальный синтаксис инициализации. Класс считается агрегатным в следующем случае.

• Все его переменные-члены являются открытыми (public).

• Он не определяет конструкторы.

• У него нет никаких внутриклассовых инициализаторов (см. раздел 2.6.1).

• У него нет никаких базовых классов или виртуальных функций, связанных с классом средствами, которые рассматриваются в главе 15.

Например, следующий класс является агрегатным:

struct Data {

 int ival;

 string s;

};

Для инициализации переменных-членов агрегатного класса можно предоставить заключенный в фигурные скобки список инициализаторов для переменных-членов:

// val1.ival = 0; val1.s = string("Anna")

1 ... 96 97 98 99 100 101 102 103 104 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии