С Новым годом! Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.56/18: Рейтинг темы: голосов - 18, средняя оценка - 4.56
67 / 65 / 61
Регистрация: 11.11.2015
Сообщений: 392
1

Как правильно перегружать операторы?

22.09.2016, 22:03. Показов 3363. Ответов 14
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Добрый день. Более-менее освоил Си, и вроде бы неплохо в нем разбираюсь. Решил взяться за плюсы.
Помогите разобраться с перегрузкой операторов. Я понял, что существуют разные виды перегрузки (как дружественная функция, как функция-член класса).
Но когда и какой способ предпочтительно использовать?

Вот есть у меня некий класс

C++
1
2
3
4
5
6
7
8
9
10
class Point
{
public:
    Point(int,int,int);
    
private:
    int x;
    int y;
    int z;
};
C++
1
2
3
4
5
6
7
#include "point.h"
Point::Point(int x,int y,int z)
{
    this->x=x;
    this->y=y;
    this->z=z;
}
Собственно, как сделать, чтобы можно было написать, что-то вроде этого:

C++
1
2
3
4
    Point p1(0,1,2);
    Point p2(1,2,3);
    Point p3(0,0,0);
    p3=p1+p2;
и чтобы при этом сложились их x,y,z.
Ну и чтобы можно было пользоваться оператором +=?
Заранее спасибо.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
22.09.2016, 22:03
Ответы с готовыми решениями:

Как же все-таки лучше всего перегружать операторы?
1. Нужно ли использовать friend там, где это возможно? (или не стоит злоупотреблять где-нибудь?)...

Как правильно перегружать оператор?
Есть такой класс. class LampaO { private: int svet; int job; int pro; public:...

Как правильно перегружать унарный минус?
Дайте правильный прототип перегрузки унарного минуса.

какие операторы нельзя перегружать?
какие операторы нельзя перегружать?

14
7803 / 6567 / 2988
Регистрация: 14.04.2014
Сообщений: 28,705
22.09.2016, 22:06 2
Как функцию-член класса реализуй. Дружественная только для ввода/вывода, с потоками.
2
495 / 209 / 70
Регистрация: 27.05.2016
Сообщений: 557
22.09.2016, 22:24 3
Лучший ответ Сообщение было отмечено lonelyhunter как решение

Решение

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Point
{
public:
    Point(int x_, int y_, int z_) :
        x(x_), y(y_), z(z_) {}
 
    Point& operator +=(const Point& rhs)
    {
        x += rhs.x;
        y += rhs.y;
        z += rhs.z;
        return *this;
    }
 
private:
    int x, y, z;
};
 
const Point operator +(const Point& lhs, const Point& rhs)
{
    return Point(lhs) += rhs;
}
1
67 / 65 / 61
Регистрация: 11.11.2015
Сообщений: 392
22.09.2016, 22:39  [ТС] 4
А почему для += нужно возвращать ссылку, а для + константу?
0
Эксперт С++
1624 / 954 / 782
Регистрация: 06.02.2016
Сообщений: 2,452
Записей в блоге: 31
22.09.2016, 22:41 5
lonelyhunter,
C++
1
 return *this; // потому что возвращается сам изменённый объект
1
495 / 209 / 70
Регистрация: 27.05.2016
Сообщений: 557
22.09.2016, 22:55 6
Цитата Сообщение от lonelyhunter Посмотреть сообщение
А почему для += нужно возвращать ссылку
Такое поведение определено для встроенных типов в С++ и по логике наш оператор += (и все другие операторы присваивания) тоже должен это делать.
Цитата Сообщение от lonelyhunter Посмотреть сообщение
а для + константу
Защита от такого кода:
C++
1
(p1 + p2) = p3;
1
67 / 65 / 61
Регистрация: 11.11.2015
Сообщений: 392
22.09.2016, 22:58  [ТС] 7
C++
1
...Point(lhs)


В C++ в таком случае что происходит? (21 строка)
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
22.09.2016, 23:07 8
Цитата Сообщение от lonelyhunter Посмотреть сообщение
А почему для += нужно возвращать ссылку, а для + константу?
Потому что для + ссылка будет на локальную переменную (на аргументы нельзя - плюс их не меняет) и сдохнет сразу по завершении оператора.
C++
1
2
3
4
5
6
const Type&operator+(const Type&value)const
{
    Type temp=*this;
    temp+=value;
    return temp;//после этой строчки temp подохло и ссылка ведет в никуда
}
0
2549 / 1208 / 358
Регистрация: 30.11.2013
Сообщений: 3,826
22.09.2016, 23:16 9
Лучший ответ Сообщение было отмечено gru74ik как решение

Решение

Цитата Сообщение от lonelyhunter Посмотреть сообщение
Но когда и какой способ предпочтительно использовать?
Добрый вечер,

давайте разбираться - имеем 2 способа перегрузки операторов:
1) в классе
2) вне класса.

Прежде чем говорить о выборе того или другого способа давайте
обсудим операторы, которые можно перегрузить только в один способ:
а) Перегрузить только как член класса можно:
- operator =
- operator ()
- operator[]
- operator ->
- operator new
- operator delete

Заметим, что операторы new и delete перегружаются только как статические
методы класса - ведь без объекта можно вызвать только статические. А тут
важная критерия "без объекта" особенно для оператора new - думаю вы понимаете.

б) Перегружать только глобально:
- сюда относится только 1 тип операторов, и вопрос я считаю сложным (лично я его и
завалил на испыте и получил 11, а не 12 Ответ в спойлере
Кликните здесь для просмотра всего текста
Операторы левый оперант, которых фундаментальный тип или другой пользовательский тип


И так мы определились с правилами без которых ни куда. Далее есть рекомендация от
создателя языке С++:
"Если оператор подразумевает изменение объекта - его стоит перегрузить как член-класса,
в противном случаи - через глобальную функцию"

Поэтому если у вас встанет вопрос, какой же выбрать - вспомните эту рекомендацию.

Теперь третий и последний вопрос: мы уже определились, что оператор будет перегружен
как глобальная функция, но данный оператор является неотъемлемой частью класса
в частности оператор вывода/ввода - так как левый оперант у нас "неправильный" и
мы вынуждены перегрузить только глобально, то встаёт вопрос - сколько сеттеров/геттеров
нужно будет вызвать в этих операторах - хотя эту функцию будет писать тот же программист,
и объявлена она будет в том же файле, где и класс. Поэтому очень часто этой функции
предоставляют карт-бланш делая её дружественной. Тем самым мы архитектурно говорим,
что она наша - просто сложились такие обстоятельства, что она снаружи класса.
3
67 / 65 / 61
Регистрация: 11.11.2015
Сообщений: 392
22.09.2016, 23:30  [ТС] 10
Спасибо, буду разбираться дальше.
0
Вездепух
Эксперт CЭксперт С++
12792 / 6669 / 1795
Регистрация: 18.10.2014
Сообщений: 16,882
22.09.2016, 23:48 11
Во-первых, немодифицирующие операторы с симметричными (по типу) операндами (типа бинарных арифметических), рекомендуется реализовывать отдельностоящей [friend] функцией, а не членом класса. Членом класса обычно реализуются только модифицирующие операторы (типа += или ++)

Во-вторых, чтобы два раза не делать одно и то же, устойчивой идиомой является реализация бинарного + через +=.
0
67 / 65 / 61
Регистрация: 11.11.2015
Сообщений: 392
23.09.2016, 09:35  [ТС] 12
Интересно. Мнения разделились. Выходит строгих правил нет.
0
Эксперт С++
3225 / 1752 / 436
Регистрация: 03.05.2010
Сообщений: 3,867
23.09.2016, 10:01 13
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Во-первых, немодифицирующие операторы с симметричными (по типу) операндами (типа бинарных арифметических), рекомендуется реализовывать отдельностоящей [friend] функцией, а не членом класса.
Важно подчеркнуть, что, если инициализировать второй и третий параметры конструктора значениями по умолчанию, то складывать скаляр с вектором можно будет только если функция сложения определена не как член класса:
C++
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
#include <iostream>
#include <string>
///////////////////////////////////////////////////////////////////////////////
typedef std::string     T_str;
///////////////////////////////////////////////////////////////////////////////
class   T_point
{
    //-------------------------------------------------------------------------
    int     x;
    int     y;
    int     z;
    //-------------------------------------------------------------------------
public:
    //-------------------------------------------------------------------------
    T_point (
                int     x_,
                int     y_  =   {},
                int     z_  =   {}
            )
            :
        x(x_),
        y(y_),
        z(z_)
    {}
    //-------------------------------------------------------------------------
    T_point     &   operator +=( T_point    const   &   R )
    {
        x   +=  R.x;
        y   +=  R.y;
        z   +=  R.z;
 
        return  *this;
    }
    //-------------------------------------------------------------------------
    friend
    std::ostream    &   operator<<
        (
            std::ostream            &   ostr,
            T_point         const   &   point
        )
    {
        ostr    <<  '{'
                <<  point.x     <<  ','
                <<  point.y     <<  ','
                <<  point.z
                <<  '}';
 
        return  ostr;
    }
    //-------------------------------------------------------------------------
};
///////////////////////////////////////////////////////////////////////////////
const   T_point     operator+
    (
        const   T_point   &   L,
        const   T_point   &   R
    )
{
    return  T_point(L)  +=  R;
}
///////////////////////////////////////////////////////////////////////////////
int     main()
{
    T_point   q{1, 1, 1};
    std::cout   <<  1 + q   <<  std::endl;
}
Здесь этот пример кажется несколько натянутым, но если, например, складывать скаляр с комплексным числом или рациональной дробью, то выгоды очевидны.

Добавлено через 1 минуту
Цитата Сообщение от lonelyhunter Посмотреть сообщение
Мнения разделились.
В чем же, по-вашему?
0
67 / 65 / 61
Регистрация: 11.11.2015
Сообщений: 392
23.09.2016, 10:03  [ТС] 14
Ну выше была рекомендация использовать дружественные функции, только для работы с потоками. < >
0
Эксперт С++
3225 / 1752 / 436
Регистрация: 03.05.2010
Сообщений: 3,867
23.09.2016, 12:49 15
Цитата Сообщение от lonelyhunter Посмотреть сообщение
Ну выше была рекомендация использовать дружественные функции, только для работы с потоками. < >
Не знаю, где вы это вычитали, здесь вроде бы все согласились, что
Цитата Сообщение от rikimaru2013 Посмотреть сообщение
"Если оператор подразумевает изменение объекта - его стоит перегрузить как член-класса,
в противном случаи - через глобальную функцию"
Саттер и Александреску в своей книге "Стандарты программирования на С++" рекомендуют предпочитать функции, которые не являются ни членами, ни друзьями (рекомендации 27 и 44).
На самом деле это одно из применений основного принципа ООП - всемерной минимизации взаимозависимостей.
1
23.09.2016, 12:49
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
23.09.2016, 12:49
Помогаю со студенческими работами здесь

Как правильно перегружать потоковый оператор вывода в файл?
Нужен только прототип оператора, реализацию сделаю сам. Обязательное требование: должна...

Для енама нельзя перегружать операторы?
Для енама нельзя перегружать операторы?

Как правильно записывать условные операторы и как прервать проверку при достижении результата?
доброго времени суток форумчане, подскажите пожалуйста как остановить условие после правильно...

Как правильно перегрузить операторы для собственных классов?
Операторы перегрузки не выполняются. Мне кажется, я не правильно передаю объекты класса в функцию....


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru