С++ для начинающих


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


Набор конструкторов класса, принимающих единственный параметр, например, 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 ) );  // правильно: приведение типа

}


Содержание раздела