С++ для начинающих Перегрузка операторов

Одна из тех тем, которые мне было трудно понять — это тема о перегрузках операторов.

  • Перегрузка операторов предназначена для облегчения читаемости программ

Перегрузка операторов — это очень полезная возможность языка C++. Если есть собственный класс, то иногда имеет смысл научить объекты этого класса работать так, как работают объекты: сложение строки должно создавать одну строку из двух, например; вывод на экран данных по объекту возможно выводить оператором <<; Этому всему надо учить собственные классы. Вот такое научивание класса и называется перегрузкой. К сожалению, некоторые люди увлекаются перегрузкой, используют её так, что читать код становится тяжело. Главная цель перегрузки — это удобство, а не запутать всех. Это имеет смысл помнить.

Символ + для чисел обозначает одно, для строк другое. Когда программист пишет собственный класс, компилятор не знает, что нужно будет делать, если объекты этого класса попытаться, например, сложить друг с другом или вычесть один из другого. Он же не знает, что именно создаётся, и как к этому относиться, только угадывать может, а угадывать компилятор здесь не захочет. Так же происходит и с другими операторами языка C++.

Перейдём к простому примеру, напишем свой класс, который будет очень урезанной версией типа данных string, который встроен в современные компиляторы языка. Научим этот класс складывать его объекты между собой. Для сложения в C++ возможно использовать такие операторы, как +,   +=,   ++. Я, чтобы код получился понятным, использую оператор +=. Я надеюсь, что этот оператор хорошо знаком и понятен. ( (a+=b) == (a = a + b) )

  • Пример перегрузки оператора +=

Когда создаётся первый объект, вовнутрь переменной data этого объекта попадает значение "String one". Это происходит благодаря конструктору. После этого этот объект складывается с константной строкой. По той причине, что внутри класса описана функция-член, которая зовётся operator+, становится возможно использовать оператор += для объектов этого класса. В качестве параметра указывается такой объект, тип которого соответствует типу объекта, стоящего справа от оператора +=. Всё остальное — это то же самое, что и обычные функции. Важно только отметить, что для перегрузки используется ключевое слово operator.

Сложение объекта класса с константной строкой — это, конечно, хорошо, но это неполная перегрузка. Чаще всё-таки объекты класса складываются между собой. В этом, показанном выше примере, фактически складываются два разных типа данных: мой тип данных с типом константная строка. В связи с этим сложить два объекта между собой не выйдет.

Чтобы научить класс такому действию, нужно научиться понимать, что в параметр функции "operator+=" надо отдавать объект такого же типа, который в функции указан. Этот объект — это объект, который идёт сразу справа от оператора +=. Так как отдавалась константная строка вида "СТРОКА" и для параметра внутри функции указан тип константная строка (char*), то всё прошло гладко. Но если туда отдавать такой же тип данных, то вся гладкость пропадает. Этому нужно учить. Дописываем допилнительную функцию-член. Перегружаем сам operator+=

Теперь работает, как задумывалось.

Добавим ещё немного возможностей. Приспособим знак вычитания к строкам. Некоторым такая идея не нравится, делать этого не советую, но для получения знаний должно быть полезно. Знак минуса будет использоваться для удаления подстроки из строки.

В общем случае перегружать можно почти все операторы.

  • Нельзя перегружать:
  • . (точка)
  • .* (Точка+звездочка)
  • :: (два двоеточия подряд)
  • ?:(вопрос с двоеточием)
  • sizeof (тоже нельзя)

Нужно знать:

  • Чтобы перегрузить оператор, нужно определить класс, к которому оператор будет назначен
  • Когда перегружается оператор, перегрузка действует только для класса в котором он определяется. Если оператор использует неклассовые переменные, то используется стандартное определение оператора
  • Для перегрузки оператора используется ключевое слово С++ operator. Это слово определяет метод класса, который С++ вызывает каждый раз, когда переменная класса вызывает оператор
  • С++ позволяет перегружать все операторы, кроме вышеуказанных

Кроме уже описанных ограничений на перегрузку операторов, имеют место другие ограничения.

  • Старшинство операций не может быть изменено перегрузкой
  • Ассоциативность операций не может быть изменена перегрузкой
  • Изменить количество операндов, которое берет операция невозможно: Перегруженные унарные операции остаются унарными, перегруженные бинарные остаются бинарными
  • Каждая из операций & * + — может иметь и унарный, и бинарный варианты. Эти унарные и бинарные варианты могут перегружаться отдельно
  • Создавать новые операции невозможно. Возможно только использовать существующие.
  • Нельзя изменить операцию для объекта встроенного типа (уже упоминалось чуть другими словами)
  • Неявной перегрузки не существует (например object1=object1+object2 не равно object1+=object2 если явно перегрузки не прописано)

Нужно помнить, что перегрузка операторов нужна для облегчения читаемости кода и злоупотребление этим вместо пользы может приносить вред.

Перегружать операторы таким образом, как было показано выше — хорошо, но этот способ перегрузки, когда функция перегрузки оператора возвращает void, имеет определённые недостатки. По коду может быть сложно сказать, что делает оператор для объектов класса; к тому же нельзя сохранить полученный результат в отдельную переменную. Давайте посмотрим наглядный пример. Перегрузим обычный +.

Поэтому возвращаемые значения для функций оператор-перегрузки часто написаны с типом не void. Иногда имеет смысл ничего не возвращать, но, если результат в перспективе будет нужно сохранять, но нужно возвращать соответствующий тип данных. В показанном примере этот тип был int.

  • Нельзя перегрузить возвращаемый тип

Этот код лучше вглядит вот таким образом (здесь всё исправно):

Все комментарии на сайте проверяются, поэтому ваш комментарий может появиться не сразу. Для вставки кода в комментарий используйте теги: [php]ВАШ_КОД[/php]

19 комментариев: С++ для начинающих Перегрузка операторов

  • Gen говорит:

    С большим трудом удалось скомпелировать в Code::Block,но так и не понял что происходит.У меня просто копируется введенная строка и удаляется символ.А как складываются строки так и не увидел..С этой строкой

    вылетают ошибки,заменяю на

    ошибок нет,но мне кажется и не работает.Пожалуйста объясните в чем дело.Название класса пришлось поменять из за конфликта с инклудой string,и переменные несколько изменить

    • admin говорит:

      Пример неправильно описан. Поэтому буду переделывать, но это в течении след. недели. Сейчас нет возможности.
      а про конфликты) не юзайте using namespace std и будет меньше геморроя) пишите прямо std::cout,std::string и т.п.

    • admin говорит:

      Точнее с минусом неправильно. С плюсом работает.
      Например вы вводите 2 слова:
      Привет
      Пока
      Сначала происходит сложение этих двух строк = «Привет Пока»
      Потом должен удалиться символ «в», что там получится должно не помню. В общем это зависит от того, что у минуса в классе описано. Можно 1 символ удалять, можно все, можно каждый второй и т.п.

      Чтоб другие не мучались код для CodeBlock

  • Gen говорит:

    Вот так у меня работает правильно.С тегами не получается в виде текста покажу код.
    #include
    #include
    #include

    using namespace std;
    //СОЗДАЕМ СОБСТВЕННЫЙ КЛАСС
    class stroka
    {
    public:
    stroka (char*); //конструктор класса принимающий один параметр
    void operator+(char*); //определение оператора +
    void operator-(char); //определение оператора —
    void show_stroka(void); //метод класса для отображения строки
    private:
    char data[256]; //символьный массив, доступный только классу
    };

    stroka::stroka(char *str) //Транзитом через конструктор
    {
    strcpy(data, str); //копируем в символьный массив класса данные из принимаемой извне строки
    }

    void stroka::operator+(char *str) //Определяем оператор +
    {
    strcat(data, str); //как функцию сложения двух строк
    }

    void stroka::operator-(char letter)//Определяем оператор —
    {
    char temp[256]; // будем создавать новую строку
    int i, j; //счетчики циклов
    //Проходим по всей строке класса с помощью цикла и если символ строки не равен принятому символу (параметру), то копируем его в новую строку
    for (i = 0, j = 0; data[i]; ++i) if (data[i] != letter) temp[j++] = data[i];
    temp[j] = NULL;
    strcpy(data, temp); //Копируем новую строку в символьный массив класса
    }
    void stroka::show_stroka(void)
    {
    cout << data << endl; //Показываем символьный массив класса
    }

    int main()
    {

    char st[256], st2[256]; //Объявление двух указателей для строк
    cin.get(st, 256); //Считывание первой строки с клавиатуры
    cin.get();
    cin.get(st2, 256); //Считывание второй строки с клавиатуры
    stroka title(st); //Объявление переменной типа нашего класса и передача в конструктор первой строки
    title + " "; //С помощью перегрузки операторов добавили к строке пробел
    title + st2; //C помощью перегрузки операторов добавили к строке вторую строку
    title.show_stroka(); //Отобразили результирующую строку на экране
    title — 'b'; //При помощи перегрузки операторов пытаемся удалить символ в
    title.show_stroka(); //Отобразили результирующую строку

    return 0;
    }
    только в этой строке предупреждение старое преобразование title + " "; //С помощью перегрузки операторов добавили к строке пробел

  • Gen говорит:

    Разобрался и с этим.Переписал так void operator+(char const*); //определение оператора + и void stroka::operator+(char const* str) //Определяем оператор +j.Теперь кампиляция чистая и все работает.

  • Gen говорит:

    Наверное правильней будет не temp[j] = NULL; а temp[j] = »;

  • Gen говорит:

    temp ]j] = »;

  • Gen говорит:

  • Gen говорит:

    не получается в ‘ ‘

  • Gen говорит:
  • Gen говорит:

    \ 0

    • дон говорит:

      почему ты не использует char* ? :3

      Автор сайта отвечает:
      Потомучто char* нельзя задать во время выполнения программы.
      (если не выделять память c помощью new…)

      дон говорит:
      ещё знаю, спасибо !

  • дон говорит:

    в
    private:
       char data[256];
    я думаю, что очень хорошо

  • Mr.Ex говорит:

    Можно все сделать гораздо проще)

     

  • big-town говорит:

    Ну когда уже научатся люди задействовать свой мозг! Ctrl+c,v уже задолбало!

    Не ужели этот пример может чему то научить?! Особенно начинающего!
    fn.cpp

    CFLAGS=-std=gnu++11
    all: fn.cpp
    g++ $(CFLAGS) -o fn fn.cpp

    • admin говорит:

      Пример немного неправильно написан. Вот так лучше:

      А вообще, спасибо за участие. Хороший пример.

      • big-town говорит:

        Нет, не забыл это осталось от предыдущего кода. operator-вообще не обязан что либо возвращать, это тоже известное заблуждение из копипастов. Проверьте, вот так тоже будет работать.

        #include "cstring"
        #include "cstdio"

        class MyStr
        {
        public:
        char str[256];
        void operator=(char *pstr)
        {
        strcpy(str,pstr);
        }
        };

        int main()
        {
        MyStr a;
        a="This is string";
        puts(a.str);
        return 0;
        }

        Сейчас уже очень мало людей которые пишут на чистом Си и даже С++. Немодно :). Сейчас принято плодить говнокод на различных фрэймворках и явах. И аргумент — это скорость программирования. Может быть это и так, а вот качество кода при этом ужасно страдает. Быстрых и не требовательных приложений очень мало. И то что существуют такие порталы как ваш — это очень хорошо. Но не отпугивайте начинающих примерами монстрами в которых и опытный прогер не сразу разберется. Удачи, надеюсь что популяция сишников восстановится :)))!

Добавить комментарий

Ваш e-mail не будет опубликован.

Поиск

 
     
Яндекс.Метрика

НАГРАДИ АВТОРА САЙТА
WEBMONEY
R375024497470
U251140483387
Z301246203264
E149319127674

- У меня компьютер сам выключается... че делать? - У тебя системник внизу стоит? - Да. - Поменяй носки... он сознание теряет.

Выражаю свою признательность

  • Максиму очень признателен за указание на мои ошибки и неточности.
  • Sergio ===> за оказание помощи в исправлении моих ошибок
  • Gen ===> за правильное стремление помочь другим новичкам и выявления моих ошибок