Перегрузка операторов в какой-то степени творческий процесс. С её помощью, у программистов появляется возможность облегчить для себя написание кода, а для других – повысить читабельность. Например, когда какое-то действие приходится повторять в коде много раз, и просто мучительно постоянно использовать для этого специальные функции – можно перегрузить оператор для этого.
Допустим, в коде надо часто объединять строки (дописывать строки в строку элемент класса). Это можно оформить по-разному. Но мы сделаем так, чтобы объединение строк происходило тогда, когда мы используем оператор + :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Overload { private: char str[256]; public: // какой-то код ... } int main { char strіng_1[] = "Название нашего сайта: "; char strіng_2[] = "PureCodeCpp"; Overload Object; // объявление объекта Object + strіng_1; Object + strіng_2; return 0; } |
Результатом этого должно стать изменение элемента str. Он вместит в себе обе строки: “Название нашего сайта PureCodeCpp”.
Пока мы явно не перегрузим оператор +, компилятор будет “ругаться”, так как мы складываем не числа, а строки. Но сейчас мы научимся указывать ему, как надо действовать, когда мы просим выполнить нестандартное действие, используя +.
Приступаем к практике. Определим в примере 4 строки. Далее соберем из них анекдот, объединив их в правильном порядке в одну строку.
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | #include <iostream> using namespace std; class Overload { private: char str[256]; //строка, которая вместит все строки public: Overload() // очистим строку от "мусора" { for (int i = 0; i < 256; i++) str[i] = '\0'; } void operator +(char*); //прототип метода класса где будет перегружен + void showStr(); }; void Overload::operator +(char *s) // то, что надо выполнить при нахождении в коде оператора + { strcat_s(str, s); // дописать строку s в строку str } void Overload::showStr() { cout << str << endl << endl; } int main() { setlocale(LC_ALL, "rus"); char *str1 = "С лучезарным Челси на макушке.\n"; char *str2 = "Вся в гирляндах нефтяная вышка\n"; char *str3 = "Ёлка олигарха, так уж вышло,\n"; char *str4 = "Вовсе не дешёвая игрушка –\n"; cout << "1. " << str1; cout << "2. " << str2; cout << "3. " << str3; cout << "4. " << str4 << endl; Overload Joke; // объявляем объект и дописываем в его элемент строки используя уже перегруженный + Joke + str3; Joke + str4; Joke + str2; Joke + str1; cout << "=================================================" << endl; cout << "Анекдот, после записи строк в правильном порядке: " << endl; cout << "=================================================" << endl << endl; Joke.showStr(); // Сразу отметим, что для чисел оператор + будет выполнять сложение, как и должен int num_a = 15; int num_b = 5; int num_c = 0; num_c = num_a + num_b; cout << "=================================================" << endl << endl; cout << "num_a = " << num_a << endl << "num_b = " << num_b << endl; cout << "num_c = " << num_a << " + " << num_b << " = " << num_c << endl << endl; return 0; } |
Не будем говорить об очевидном – перейдем к разбору самой перегрузки. В строке 15 мы видим прототип метода класса Overload: void operator +(char*); Метод этот заставит работать оператор + так, как мы того захотим (так как мы определим ниже). Чтобы перегрузить какой-либо оператор нужно использовать ключевое слово operator. В нашем случае, метод не возвращает значений поэтому void, далее ключевое слово operator и сам оператор +. Принимает этот метод указатель на строку.
В строках 20 – 23 располагается определение метода перегрузки оператора. (Как определить метод вне класса читайте в статье Классы в С++) В нем, используя функцию strcat_s(str, s); производится запись строки s в конец строки str (элемент класса). Действует это так – как только в коде встретится оператор + за которым будет располагаться строка – будет вызван метод перегрузки оператора и эта строка передастся в него по указателю.
В главной функции у нас определены в случайном порядке 4 строки. Отображаем их на экране (строки 39 – 42). Ниже, в строке 44, объявлен объект Joke. Во время его создания, конструктор класса, очистит элемент класса str от “мусора” и он будет готов для записи строк. Осталось выполнить простые действия (строки 46 – 49) – используя перегруженный + записать все строки в одну (str) в правильном порядке.
Результат:
Все получилось. Еще, как видно в результате, для числовых данных оператор + сработал правильно. Поэтому можно спокойно применять его для арифметических операций в коде – компилятор вас “поймет”. Еще один момент – перегрузка оператора действует только в пределах того класса, для которого она определена. Если мы определим еще один класс (Overload2 например) но не перегрузим для него оператор, то попытка использовать + для записи строки куда-либо приведет к ошибке.
Есть ряд исключений в С++ – не все операторы можно перегрузить. Вот перечень:
И еще немного теории:
– перегрузка операторов не может изменить приоритет и порядок выполнения операций;
– нет возможности, с помощью перегрузки, создать новые символы для операций;
– бинарные операторы не могут быть использованы для переопределения унарной операции и наоборот – унарный оператор не переопределит бинарную операцию.
Перегрузка операторов, конечно, “вещь” интересная. Только не стоит увлекаться. Используйте её только по мере острой необходимости – если это действительно будет приносить больше удобства, экономить вам время и положительно скажется на читабельности кода. Старайтесь перегружать операторы так, чтобы это было как можно ближе к их логическому значению. То есть не надо складывать строки, перегружая оператор –, например. Логичнее использовать +.
Отмечу, что многие программисты не очень любят перегрузку операторов, так как некоторые чрезмерно ней увлекаются и читать код становится сложно. Так что внимательно взвешивайте все за и против, принимая решение о перегрузке операторов.
Я планирую в будущем написать еще одну статью о перегрузке операторов, где на примерах хочу показать как перегрузить ++ инкремент, — декремент, == равенство, = присваивание new и delete.
Дополнительно посмотрите видео по теме:
>> Например, когда какое-то действие приходится повторять в коде много раз, и просто мучительно постоянно использовать для этого специальные функции – можно перегрузить оператор для этого.
В целом то верно, но перегрузка – это шикарный способ выстрелить себе в ногу (и еще отстрелить что-нибудь товарищу). Очень опасная штука. Она может как повысить читаемость кода, так и все испортить.
У Маерса были правила на эту тему (если их не соблюдать – в аду вас заставят поддерживать свой код все 7 кругов). Например:
– оператор присваивания должен возвращать ссылку на *this;
– в операторе присваивания осуществляйте проверку самоприсваивания;
– есть целыйел про перегрузку new и delete, но ИМХО 99% смертных просто не нужно трогать эти операторы;
– объявляйте функции, не являющиеся членами, когда преобразование типов должно быть применимо ко всем параметрам.
Ну и ряд других правил (в нескольких книжках).
Предлагаю уделить внимание последнему правилу из списка. У вас описан оператор, который сработает для
Joke + str1;
Но не сработает для
str1 + Joke;
Но было бы здорово если оператор работал бы в обе стороны. Для этого достаточно сделать его не членом класса (обычной функцией). Это ведь совсем не сложно? )
Visual Studio 2017 выдает ошибку на : char *str1 = “С лучезарным Челси на макушке.\n”;
Ошибка C2440 инициализация: невозможно преобразовать “const char [32]” в “char *”
– что где настроить нужно?
И еще в каком-то уроке ошибка: Ошибка C3863 тип массива “bool [& n=’функция’+]” является неназначаемым на строке bool a[n = atoi(argv[1]) + 1];
А в QT компилируется нормально (хотя синтаксис подсвечивает ошибку)
Я обошел как: char *str1 = (char*)”С лучезарным Челси на макушке.\n”;
И еще, разве не достаточно для очищения указать
char str[256] = {}; //строка, которая вместит все строки