Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих.pdf
Скачиваний:
183
Добавлен:
01.05.2014
Размер:
3.97 Mб
Скачать

Предположим, что в классе Token не определен operator int(), тогда следующий

extern void calc( int ); Token tok( "pointer", 37 );

//если Token::operator int() не определен,

//то этот вызов приводит к ошибке

компиляции

вызов будет ошибочным: calc( tok );

Если конвертер Token::operator int() не определен, то приведение tok к типу int потребовало бы вызова двух определенных пользователем конвертеров. Сначала фактический аргумент tok надо было бы преобразовать из типа Token в тип SmallInt с помощью конвертера

Token::operator SmallInt()

а затем результат привести к типу int – тоже с помощью пользовательского конвертера

Token::operator int()

Вызов calc(tok) помечается компилятором как ошибка, так как не существует неявного преобразования из типа Token в тип int.

Если логического соответствия между типом конвертера и типом класса нет, назначение

class Date { public:

// попробуйте догадаться, какой именно член возвращается!

operator int(); private:

int month, day, year;

конвертера может оказаться непонятным читателю программы:

};

Какое значение должен вернуть конвертер int() класса Date? Сколь бы основательными ни были причины для того или иного решения, читатель останется в недоумении относительно того, как пользоваться объектами класса Date, поскольку между ними и целыми числами нет явного логического соответствия. В таких случаях лучше вообще не определять конвертер.

15.9.2. Конструктор как конвертер

Набор конструкторов класса, принимающих единственный параметр, например, SmallInt(int) класса SmallInt, определяет множество неявных преобразований в значения типа SmallInt. Так, конструктор SmallInt(int) преобразует значения типа int в значения типа SmallInt.

extern void calc( SmallInt ); int i;

//необходимо преобразовать i в значение типа SmallInt

//это достигается применением SmallInt(int)

calc( i );

При вызове calc(i) число i преобразуется в значение типа SmallInt с помощью конструктора SmallInt(int), вызванного компилятором для создания временного объекта нужного типа. Затем копия этого объекта передается в calc(), как если бы

//Псевдокод на C++

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

SmallInt

{

SmallInt temp = SmallInt( i ); calc( temp );

вызов функции был записан в форме:

}

Фигурные скобки в этом примере обозначают время жизни данного объекта: он уничтожается при выходе из функции.

class Number { public:

// создание значения типа Number из значения типа SmallInt

Number( const SmallInt & ); // ...

Типом параметра конструктора может быть тип некоторого класса:

};

В таком случае значение типа SmallInt можно использовать всюду, где допустимо

extern void func( Number ); SmallInt si(87);

int main()

{// вызывается Number( const SmallInt

&)

func( si );

// ...

значение типа Number:

}

Если конструктор используется для выполнения неявного преобразования, то должен ли тип его параметра точно соответствовать типу подлежащего преобразованию значения? Например, будет ли в следующем коде вызван SmallInt(int), определенный в классе

SmallInt, для приведения dobj к типу SmallInt?

extern void calc( SmallInt ); double dobj;

//вызывается ли SmallInt(int)? Да

//dobj преобразуется приводится от double к int

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

calc( dobj );

Если необходимо, к фактическому аргументу применяется последовательность стандартных преобразований до того, как вызвать конструктор, выполняющий определенное пользователем преобразование. При обращении к функции calc()употребляется стандартное преобразование dobj из типа double в тип int. Затем уже для приведения результата к типу SmallInt вызывается SmallInt(int).

Компилятор неявно использует конструктор с единственным параметром для преобразования его типа в тип класса, к которому принадлежит конструктор. Однако иногда удобнее, чтобы конструктор Number(const SmallInt&) можно было вызывать только для инициализации объекта типа Number значением типа SmallInt, но ни в коем случае не для выполнения неявных преобразований. Чтобы избежать такого

class Number { public:

// никогда не использовать для неявных преобразований

explicit Number( const SmallInt & ); // ...

употребления конструктора, объявим его явным (explicit):

};

Компилятор никогда не применяет явные конструкторы для выполнения неявных

extern void func( Number ); SmallInt si(87);

int main()

{ // ошибка: не существует неявного преобразования из SmallInt в Number

func( si ); // ...

преобразований типов:

}

Однако такой конструктор все же можно использовать для преобразования типов, если оно запрошено явно в форме оператора приведения типа: