// явное преобразование в 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(cin));

Перейти на страницу:

Похожие книги