Перегрузка операторов бывает полезна, например, в следующих случаях:
В С++ существуют следующие операторы:
Два последних типа операторов будут разобраны в отдельной лекции.
Из всего этого списка нельзя перегружать те операторы, которые выделены красным цветом.
Для перегрузки операторов характерны следующие особенности:
a = f(), g();
//сначала вычисляется f(), потом g(), и a=f()
Оператор бинарного сложения можно перегрузить снаружи класса следующим образом:
BigNum operator+ (BigNum const &a, BigNum const &b) { ... }
А так будет выглядеть определение внутри:
BigNum BigNum::operator+ (BigNum const &b) const { ... } //первым аргументом в данном случае является *this //метод не меняет значение, поэтому const
Определение оператора снаружи класса выглядит следующим образом:
BigNum & operator+= (BigNum &a, BigNum const &b) { ... return a; }
А определение внутри класса такое:
BigNum & BigNum::operator+= (BigNum const &b) const { ... return *this; }
В частности, можно передавать операнды различных типов (например, для перегрузки BigNum+int).
Определение унарного минуса снаружи класса:
BigNum operator- (BigNum const &a) { ... }
Он же - внутри класса:
BigNum BigNum::operator- () const { ... }
Далее показана разница между определением методов ++a и a++:
BigNum & operator++ (BigNum &a); BigNum & operator++ (BigNum &a, size_t); //во втором случае используется фиктивный параметр, //который нужен только для разделения префиксного и постфиксного инкрементов
BigNum BigNum::operator[] (size_t i); BigNum BigNum::operator() (...); ... BigNum b; b(17, 34);
struct Array { int operator[] (size_t i) const { assert (i<size_); return data_[i]; } int & operator[] (size_t i) { return data_[i]; } void swap (Array &a) { std::swap(a.data_, data_); std::swap(a.size_, size_); } //swap меняет значения местами Array & operator= (Array const &a) { if (this != &a) Array(a).swap(*this); return *this; } };
Можно подключить или отключить assert, это зависит от параметров компиляции. При выходе за границы массива тогда возникнет ошибка.
Для использования ostream и istream достаточно подключить
#include <iosfwd>;т.к. iostream весьма и весьма велик.
Array a; std::cout << a; std::ostream &operator<< (std::ostream & os, Array const &a); //этот и следующий операторы могут быть переопределены только снаружи класса std::istream &operator>> (std::ostream & is, Array &a); //при чтении объект изменяется (std::cin >> a)
Замечание: swap(Array(a)) не сработает, потому что swap принимает константную ссылку, а временные объекты нельзя передавать по константной ссылке.
Оператор += лучше определять внутри класса, а оператор + — через += снаружи, как показано в примере:
struct BigNum { BigNum & operator+= (BigNum &b) { ... } ... } BigNum operator+ (BigNum a, BigNum const &b) { return a+=b; } //+, определённый снаружи, может быть вызван от аргументов, //которые может быть приведены к типу BigNum a(10); //должен быть BigNum(int) a+20; //работает и внутри, и снаружи 20+a; //работает только снаружи bool operator== (BigNum const &a, BigNum const &b) { ... } bool operator!= (BigNum const &a, BigNum const &b) { return !(a==b); }
Вообще говоря, достаточно определить < и == и легко получить все остальные сравнения. Например, (a<=b) выражается как (!(b<a))
{
smart_pstr p = new ...
}
В данном примере рассматривается перегрузка операторов p-> и *p.
struct smart_pstr { string *p_; ... string * operator-> () const { return p_; } string & operator* () const { return *p_; } }; //предположим, что мы переопределим оператор ниже: operator== (smart_pstr, smart_pstr) ... smart_pstr p; string *q; p == q; //произойдёт неявное преобразование типа, //создастся smart_pstr(q), а потом он будет удалён //для того, чтобы этого избежать, нужно конструктор сделать explicit //(т.е. запретить неявное преобразование типов)