Конструктор как конвертер
Набор конструкторов класса, принимающих единственный параметр, например, 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
можно использовать всюду, где допустимо значение типа Number:
extern void func( Number ); SmallInt si(87); int main() { // вызывается Number( const SmallInt & ) func( si ); // ... |
}
Если конструктор используется для выполнения неявного преобразования, то должен ли тип его параметра точно соответствовать типу подлежащего преобразованию значения? Например, будет ли в следующем коде вызван 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, но ни в коем случае не для выполнения неявных преобразований. Чтобы избежать такого употребления конструктора, объявим его явным (explicit):
class Number { public: // никогда не использовать для неявных преобразований explicit Number( const SmallInt & ); // ... |
Компилятор никогда не применяет явные конструкторы для выполнения неявных преобразований типов:
extern void func( Number ); SmallInt si(87); int main() { // ошибка: не существует неявного преобразования из SmallInt в Number func( si ); // ... |
Однако такой конструктор все же можно использовать для преобразования типов, если оно запрошено явно в форме оператора приведения типа:
SmallInt si(87); int main() { // ошибка: не существует неявного преобразования из SmallInt в Number func( si ); func( Number( si ) ); // правильно: приведение типа func( static_cast< Number >( si ) ); // правильно: приведение типа |