Как мы уже знаем из урока о неявном преобразовании типов данных, C++ позволяет конвертировать значения из одного типа данных в другой. Например, преобразуем значение типа int в значение типа double:
1 2 |
int a = 7; double b = a; // значение типа int неявно конвертируется в значение типа double |
Язык C++ по умолчанию знает, как выполнять преобразования встроенных типов данных. Однако он не знает, как выполнять конвертацию с пользовательскими типами данных (например, с классами). Именно здесь вступает в игру перегрузка операций преобразования типов данных. Рассмотрим следующий класс:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Dollars { private: int m_dollars; public: Dollars(int dollars=0) { m_dollars = dollars; } int getDollars() { return m_dollars; } void setDollars(int dollars) { m_dollars = dollars; } }; |
Класс Dollars содержит некое количество долларов в виде целого числа (переменная-член m_dollars
) и предоставляет функции доступа для получения и установления значения m_dollars
. В нем также есть конструктор для конвертации значений типа int в тип Dollars (при создании объекта пользователь передает в качестве аргумента значение типа int, которое затем преобразуется в значение типа Dollars).
Если мы можем конвертировать int в Dollars, то логично было бы, если бы мы могли конвертировать и Dollars обратно в int, не так ли? Иногда это может быть полезным.
В следующем примере мы используем метод getDollars() для конвертации значения типа Dollars в тип int для его последующего вывода через функцию printInt():
1 2 3 4 5 6 7 8 9 10 11 12 |
void printInt(int value) { std::cout << value; } int main() { Dollars dollars(9); printInt(dollars.getDollars()); // выведется 9 return 0; } |
Согласитесь, вызывать каждый раз метод getDollars() не очень удобно. Было бы проще перегрузить операцию преобразования значений типа Dollars в тип int. Делается это следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Dollars { private: int m_dollars; public: Dollars(int dollars=0) { m_dollars = dollars; } // Перегрузка операции преобразования значений типа Dollars в значения типа int operator int() { return m_dollars; } int getDollars() { return m_dollars; } void setDollars(int dollars) { m_dollars = dollars; } }; |
Здесь есть две вещи, на которые следует обратить внимание:
В качестве функции перегрузки используется метод operator int()
. Обратите внимание, между словом operator и типом, в который мы хотим выполнить конвертацию (в данном случае, тип int), находится пробел.
Функция перегрузки не имеет типа возврата. Язык C++ предполагает, что вы будете возвращать корректный тип.
Теперь функция printInt() вызывается проще:
1 2 3 4 5 6 7 |
int main() { Dollars dollars(9); printInt(dollars); // выведется 9 return 0; } |
Сначала компилятор видит, что функция printInt() должна принимать целочисленный параметр (из определения printInt()). Затем он видит, что переменная dollars
не является типа int. Он смотрит, предоставили ли мы способ конвертации значения типа Dollars в тип int. Так как это у нас есть, то вызывается operator int(), который возвращает значение типа int, и это значение передается в printInt().
Теперь мы можем явно конвертировать объект класса Dollars в тип int:
1 2 |
Dollars dollars(9); int d = static_cast<int>(dollars); |
Вы можете перегружать операции преобразования любых типов данных, включая ваши собственные (пользовательские) типы данных!
Например, вот класс Cents, в котором осуществлена перегрузка операции преобразования значения типа Cents в значение типа Dollars:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Cents { private: int m_cents; public: Cents(int cents=0) { m_cents = cents; } // Выполняем конвертацию Cents в Dollars operator Dollars() { return Dollars(m_cents / 100); } }; |
Таким образом мы можем напрямую конвертировать центы в доллары:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#include <iostream> class Dollars { private: int m_dollars; public: Dollars(int dollars = 0) { m_dollars = dollars; } // Перегрузка операции преобразования значения типа Dollars в значение типа int operator int() { return m_dollars; } int getDollars() { return m_dollars; } void setDollars(int dollars) { m_dollars = dollars; } }; class Cents { private: int m_cents; public: Cents(int cents=0) { m_cents = cents; } // Выполняем конвертацию Cents в Dollars operator Dollars() { return Dollars(m_cents / 100); } }; void printDollars(Dollars dollars) { std::cout << dollars; // dollars неявно конвертируется в int здесь } int main() { Cents cents(700); printDollars(cents); // cents неявно конвертируется в Dollars здесь return 0; } |
Результат выполнения программы:
7
Всё логично: 700 центов = 7 долларов!
будьте осторожнее, потому что оператор преобразования типов данных возвращает объект по значению. Это значит, что будет вызывать один из конструкторов возвращаемого объекта. И если это будет конструктор копирования класса, в котором есть массив данных, то жди беды! Жаль об этом не написал автор. По сути оператор преобразования данных выглядит следующим образом (если брать пример из данного урока): Dollars operator Dollars (Cents* this)
(указатель this неявный для методов класса)
попробуйте запустить эту программу
В этой инструкции выполняется конструктор класса Dollars? При том что объект Dollars не объявлен?
Если Вы про return, то там создается временный объект класса Dollars.
Кстати, в этой строке можно просто писать:
компилятор сам выполнит преобразование типа к Dollars, опираясь на тип возвращаемого значения функции
Dollars(m_cents / 100) — Анонимный объект
Действительно ли в последнем примере в функции printDollars переменная dollars неявно конвертируется в int в строке вывода?
По-моему, компилятор не знает, что ему нужно конвертировать в int, как выводить dollars он тоже в этом примере не знает (перегрузки оператора << нет) — должна быть ошибка. Может быть, подразумевалась функция printInt с параметром int?
Прежде чем компилятор выведет dollars, он будет искать способы конвертации Dollars в int. А такой способ как раз и предоставили перегрузкой оператора преобразования типа, который и возвращает необходимый int. Поэтому ошибки быть не должно.
"Прежде чем компилятор выведет dollars, он будет искать способы конвертации Dollars в int."
Я бы так не сказал.
Для того, чтобы вывести "dollars", компилятор попробует отыскать перегрузку оператора вывода << для данного объекта. В данном примере её нет, поэтому он попробует конвертировать данный класс в любой другой тип, у которого такая перегрузка есть. В данном случае это будет int (т.к. мы определили операцию преобразования значения типа Dollars в значение типа int). Если же вы, например, вместо int укажете char, то перед выводом он конвертирует данный класс в char.
а как выполнить обратную операцию:
преобразовать базовый тип в свой
?
например, есть тип
class Int {};
для которого перегружен оператор + (Int)
а я хочу использовать выражения типа int + Int
Понятно, что можно писать так:
Int{int} + Int
используя конструктор, но можно ли как-то не перегружая еще раз оператор заставить неявно преобразовывать тип int к Int ?
Для начала перегрузить оператор приведения типов для своего класса,
а затем можно использовать явное приведение типов:
Добрый день!
У меня вопрос по части практики.
Кто-нибудь из завсегдатаев этого ресурса упражняется в играх типа
codecombat или codingame?
Или может кто-то знает похожие ресурсы, где можно поднять практику по C++ в игровой форме?
Ссылки не привожу, дабы не обидеть админа!
Админу на заметку: реально ли в обозримом будущем вашему ресурсу обзавестись похожими практиками? Я готов купить премиум, если он будет!
На мой вкус решение игровых головоломок позволяет дольше акцентировать свое внимание на программировании, чем перечитывание справочников и написание сухого кода..
Привет, о таких ресурсах тоже слышал и тоже есть идеи по поводу добавления именно практических заданий без теории. Возможно, это сделаю, но обещать ничего не буду, так как на это тоже нужно время и усилия.
Ух. Все понятно, но прям накапливается количество информации, которую нужно учитывать читая код, что уже приходится задумываться и медленнее читать уроки 🙂