1 2 3 4 5 6 7 8 9 10 |
//Листинг #a1 Инкремент, постфиксная и префиксная формы clang #include <iostream> using std::cout; int main(){ int x = 10; cout << x++ << '\n'; //постфиксная форма: сначала значение x использовалось, только потом увеличилось (на экране 10, x==11) cout << ++x << '\n'; //префиксная форма: сначала значение x увеличилось, только потом использовалось (на экране 12, x==12) } |
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 |
//Листинг #1 Перегрузка префиксной формы ++ clang #include <iostream> using std::cout; class MyInt{ MyInt(){} //конструктор по умолчанию int x; public: MyInt(const int value):x(value){} //конвертирующий конструктор. Чтобы не писать методы, использован для сокращения кода operator int() const { return x; } //для сокращения кода, чтобы не перегружать << MyInt& operator ++(); //прототип перегрузки префиксной формы }; /* реализация префиксной перегрузки ++ */ MyInt& MyInt::operator++(){ x++; //Сначала увеличили значение или все значения return *this; //Отдали ссылку объекту на самого себя. } int main(){ MyInt x = 10; //cout << x++ << '\n'; //Не работает, потому что для постфиксной формы перегрузка не описана cout << ++x << '\n'; //Сначала значение x увеличилось, потом использовалось (x == 11, на экране 11) } |
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 |
//Листинг #2 Перегрузка префиксной и постфиксной формы ++ clang #include <iostream> using std::cout; class MyInt{ MyInt(){} //конструктор по умолчанию int x; public: MyInt(const int value):x(value){} //конвертирующий конструктор. Чтобы не писать методы, использован для сокращения кода operator int() const { return x; } //для сокращения кода, чтобы не перегружать << MyInt& operator ++(); //прототип перегрузки префиксной формы ++ MyInt operator++(const int); //прототип перегрузки постфиксной формы ++ }; /* реализация префиксной перегрузки ++ */ MyInt& MyInt::operator++(){ x++; //Сначала увеличили значение или все значения return *this; //Отдали ссылку объекту на самого себя. } /* реализация постфиксной перегрузки ++ */ MyInt MyInt::operator++(const int){ MyInt temp = *this; //сохранили не успевшее измениться состояние во временный объект x++; //изменили состояние оригинального объекта return temp; //но вернули копию, созданную из ещё не успевшего измениться объекта } int main(){ MyInt x = 10; cout << x++ << '\n'; //Сначала значение x использовалось, потом увеличилось (на экране 10, x == 11) cout << ++x << '\n'; //Сначала значение x увеличилось, потом использовалось (на экране 12, x == 12) } |
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 |
//Листинг #3 дружественная перегрузка префиксной и постфиксной формы ++ clang #include <iostream> using std::cout; class MyInt{ MyInt(){} //конструктор по умолчанию int x; public: MyInt(const int value):x(value){} //конвертирующий конструктор. Чтобы не писать методы, использован для сокращения кода operator int() const { return x; } //для сокращения кода, чтобы не перегружать << friend MyInt operator++(MyInt&, const int); //прототип friend перегрузки постфиксной формы ++ friend MyInt& operator++(MyInt&); //прототип friend перегрузки префиксной формы ++ }; MyInt& operator++(MyInt& object){ //префиксная без фиктивного параметра object.x++; return object; } MyInt operator++(MyInt& object, const int){ //постфиксная с фиктивным параметром MyInt temp = object; //сохранили не успевшее измениться состояние объекта во временный объект object.x++; //обновили состояние исходного объекта return temp; //вернули сохранённое состояние } int main(){ MyInt x = 10; cout << x++ << '\n'; //Сначала значение x использовалось, потом увеличилось (на экране 10, x == 11) cout << ++x << '\n'; //Сначала значение x увеличилось, потом использовалось (на экране 12, x == 12) } |
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 |
//Листинг #4 перегрузка постфиксной формы ++ использование параметра clang #include <iostream> using std::cout; class MyInt{ MyInt(){} int x; public: MyInt(const int value):x(value){} operator int() const {return x; } MyInt operator++(int n); }; MyInt MyInt::operator++(int n){ //параметр будет использован, поэтому о его фиктивности говорить сейчас будет неправильно if (n){ x += n; //если n не ноль, то x обновляет своё значение на сумму x+n } else { x++; //иначе ноль меняется на один } return *this; } int main(){ MyInt x = 0; cout << x.operator++(0) << '\n'; //1, согласно условию в методе-перегрузке cout << x.operator++(25) << '\n'; //1+25 = 26, согласно условию в методе-перегрузке cout << x.operator++(50) << '\n'; //26+50 = 76, согласно условию в методе-перегрузке } |
1 2 3 4 5 6 7 8 9 10 11 |
int operator+(const int){...}; //операция + object.operator+(аргумент) //вызов операции + int operator+=(const int){...}; //операция += object.operator+=(аргумент) //вызов операции += int operator++(const int){...}; //операция ++ object.operator++(аргумент) //вызов операции ++ int operator++(){...}; //операция ++ object.operator++() //вызов операции ++ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
int operator+(const int){...}; //операция + object.operator+(аргумент) //вызов операции + object + аргумент int operator+=(const int){...}; //операция += object.operator+=(аргумент) //вызов операции += object += аргумент int operator++(const int){...}; //операция ++ object.operator++(обычно фиктивный аргумент) //вызов операции ++ object ++ обычно_фиктивный_аргумент int operator++(){...}; //операция ++ object.operator++() //вызов операции ++ ++ object |
Если вы до сих пор не понимаете арност (унарный, бинарный), то, возможно, последнее вам поможет понять как операции зависят от количества аргументов. Несмотря на то, что листинг #4 показан и полностью рабочий, более простой вариант реализации подобного поведения выполняется с помощью перегрузки операции +=. Листинг #4 показан только для того, чтобы вы не начали думать, что фиктивный параметр вообще бесполезен. Использовать параметр, если прям невероятно сильно нужно, можно в таком явном виде.
Добавить комментарий