13.11 – Перегрузка операторов преобразования типов данных
В уроке «8.5 – Явное преобразование (приведение) типов данных и static_cast
» вы узнали, что C++ позволяет преобразовывать один тип данных в другой. В следующем примере показано преобразование int
в double
:
int n{ 5 };
auto d{ static_cast<double>(n) }; // преобразование int в double
C++ уже умеет преобразовывать встроенные типы данных. Однако он не знает, как преобразовывать какой-либо из наших пользовательских классов. Вот здесь и вступает в игру перегрузка операторов приведения типов.
Пользовательские преобразования позволяют нам преобразовать наш класс в другой тип данных. Взгляните на следующий класс:
class Cents
{
private:
int m_cents;
public:
Cents(int cents=0)
: m_cents{ cents }
{
}
int getCents() const { return m_cents; }
void setCents(int cents) { m_cents = cents; }
};
Этот класс довольно прост: он содержит какое-то количество центов в виде числа int
и предоставляет функции доступа для получения и установки количества центов. Также он предоставляет конструктор для преобразования int
в Cents
.
Если мы можем преобразовать int
в Cents
, то, возможно, будет полезно иметь возможность преобразовывать Cents
обратно в int
? В некоторых случаях это может быть не так, но в данном случае это имеет смысл.
В следующем примере мы должны использовать getCents()
для преобразования нашей переменной Cents
обратно в число int
, чтобы мы могли распечатать ее с помощью printInt()
:
void printInt(int value)
{
std::cout << value;
}
int main()
{
Cents cents{ 7 };
printInt(cents.getCents()); // напечатает 7
std::cout << '\n';
return 0;
}
Если мы уже написали много функций, которые принимают числа int
в качестве параметров, наш код будет завален вызовами getCents()
, что сделает его более беспорядочным, чем нужно.
Чтобы упростить задачу, мы можем предоставить пользовательское преобразование, перегрузив приведение типа в int
. Это позволит нам напрямую преобразовать наш класс Cents
в тип int
. В следующем примере показано, как это делается:
class Cents
{
private:
int m_cents;
public:
Cents(int cents=0)
: m_cents{ cents }
{
}
// перегруженное приведение в int
operator int() const { return m_cents; }
int getCents() const { return m_cents; }
void setCents(int cents) { m_cents = cents; }
};
Следует отметить три вещи:
- Чтобы перегрузить функцию, которая приводит наш класс к типу
int
, мы пишем в нашем классе новую функцию с именемoperator int()
. Обратите внимание, что между словомoperator
и типом, к которому мы выполняем преобразование, есть пробел. - Пользовательские преобразования не принимают параметров, так как нет возможности передать им аргументы.
- Пользовательские преобразования не имеют возвращаемого типа. C++ предполагает, что вы вернете правильный тип.
Теперь в нашем примере мы можем вызвать printInt()
следующим образом:
int main()
{
Cents cents{ 7 };
printInt(cents); // напечатает 7
std::cout << '\n';
return 0;
}
Компилятор сначала заметит, что функция printInt
принимает параметр типа int
. Затем он заметит, что переменная cents
не является int
. Наконец, он станет проверять, предоставили ли мы способ преобразования Cents
в int
. Поскольку этот способ у нас есть, он вызовет нашу функцию operator int()
, которая возвращает int
, и возвращенный int
будет передан в printInt()
.
Теперь мы также можем явно преобразовать нашу переменную Cents
в int
:
Cents cents{ 7 };
int c{ static_cast<int>(cents) };
Вы можете предоставить пользовательские преобразования в любой тип данных, который захотите, включая ваши собственные пользовательские типы данных!
Вот новый класс под названием Dollars
, который обеспечивает перегруженное преобразование в Cents
:
class Dollars
{
private:
int m_dollars;
public:
Dollars(int dollars=0)
: m_dollars{ dollars }
{
}
// позволяет нам преобразовывать Dollars в Cents
operator Cents() const { return Cents{ m_dollars * 100 }; }
};
Это позволяет нам преобразовывать объект Dollars
напрямую в объект Cents
! Это позволяет делать что-то вроде этого:
#include <iostream>
class Cents
{
private:
int m_cents;
public:
Cents(int cents=0)
: m_cents{ cents }
{
}
// перегруженное преобразование в int
operator int() const { return m_cents; }
int getCents() const { return m_cents; }
void setCents(int cents) { m_cents = cents; }
};
class Dollars
{
private:
int m_dollars;
public:
Dollars(int dollars=0)
: m_dollars{ dollars }
{
}
// позволяет нам преобразовывать Dollars в Cents
operator Cents() const { return Cents(m_dollars * 100); }
};
void printCents(Cents cents)
{
std::cout << cents; // cents будет здесь неявно преобразован в int
}
int main()
{
Dollars dollars{ 9 };
printCents(dollars); // dollars будет здесь неявно преобразован в Cents
std::cout << '\n';
return 0;
}
Следовательно, эта программа напечатает значение:
900
что имеет смысл, так как 9 долларов – это 900 центов!