Dashia18 / Simple_fraction_class

Class of simple fraction
0 stars 0 forks source link

Operator overloading #4

Open Dashia18 opened 9 years ago

Dashia18 commented 9 years ago

Перегрузка операторов //объявление Simple_fraction operator+ (const Simple_fraction& sf_b); //реализация Simple_fraction Simple_fraction::operator+ (const Simple_fraction& sf_b){ ...

int n1 = numerator; int d1 = denominatior; int n2 = sf_b.get_numerator(); int d2 = sf_b.get_denominatior(); ... }

//вызов sf_a + sf_b;

Перегрузка сродни методу класса и применяется к левому операнду? sf_a.operator+(sf_b) Почему ругается что слишком много переменных если записать: //объявление Simple_fraction operator+ (const Simple_fraction& sf_a, const Simple_fraction& sf_b);

sergregory commented 9 years ago

Совершенно правильно говоришь, перегруженный оператор - то просто метод класса. Ты его даже вызвать можешь как метод:

sf_a.operator+(sf_b)

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

sergregory commented 9 years ago

Ещё можно перегрузить оператор вывода в поток и тогда можно писать код типа

Simple_fraction a, b;
std::cout << a << " " << b <<std::endl;
sergregory commented 9 years ago

Почему не работает с оператором вывода - вспоминаем ещё раз как устроены функции-члены класса. У метода класса есть всегда неявный аргумент - указатель на объект этого класса. Поэтому если мы написали

class Simple_fraction{
    ...
    std::ostream& operator<<(const Simple_fraction& f)
    {
    ...
    }
}

то работать не будет, ведь с точки зрения компилятора мы объявили функцию, которая принимает два объекта типа Simple_fraction и возвращает объект потока вывода. А пытаемся вызвать её, передавая объект потока вывода и объект типа Simple_fraction. Выход:

sergregory commented 9 years ago

А почему вызовы функций в таком виде?

sf_a.Simple_fraction::get_numerator()
Dashia18 commented 9 years ago

Ну можно без пространства имен писать:

Simple_fraction::
sf_a.get_numerator()

В этом что-то не так?

Dashia18 commented 9 years ago

Перегруженный оператор вывода. operator<< Объявление:

friend std::ostream& operator<< (std::ostream& out, const Simple_fraction& sf_a);

Реализация:

std::ostream& operator<< (std::ostream& out, const Simple_fraction& sf_a){

    out<<"(operator >> overloading) SF = "<<sf_a.get_numerator()<<"/"<<sf_a.get_denominatior()<<"\n\n";
    return out;
}

Вызов:

std::cout<<sf_a;

Для чего же здесь использовать дружественную функцию?

Так как у методов класса первый неявный параметр это указатель на объект, а оператор вывода слева должен принимать логический поток, а не параметр для печати, поэтому используют дружественную функцию, которая явно принимает левый и правый операнд. И по сути это метод (функция), которая не применяется к объекту, а вызывается как обычная функция, принимает нужные параметры но объявлена в классе.

А еще дружественная функция имеет доступ к скрытым и защищенным полям класса, и может быть вызвана где угодно даже если она объявлена после ключевого слова privat. Так же этот метод - не член этого класса и может вызываться несколькими классами. (все что я узнала про дружественные функции и приняла:) )

P.S. Вообще сложно понять для чего нужна та или иная новая штукенция. В общем так?

sergregory commented 9 years ago

Благодаря тому, что ты сделала геттеры для полей numerator и denomenator тебе не нужна дружественная функция. Попробуй убрать эти два метода - и сразу окажется что функция должна быть дружественной, иначе доступ к ним никак не получить. Вообще говоря, дружественность - это некоторый хак в языке, и с точки зрения "правильного ООП"(ТМ) нужно объявлять геттеры и получать значения полей через них, что ты и сделала. Однако, если нужно особое быстродействие (и если подтверждено что тормозит именно это место), можно избежать лишнего вызова метода и просто сделать функцию другом. Ну и да, если полей много и нужен ко всем доступ (функция всё та же самая - распечатываем содержимое объекта, так называемая сериализация), то писать на каждое поле геттер долго и нудно, проще объявить друга.

Dashia18 commented 9 years ago

Ты так сложно написал, честно... Дружественная функция не член класса, поэтому доступ к полям классам у нее все равно через get-методы. Поэтому я не понимаю про что ты пишешь, что get-методы можно убрать.

Да если сделать эту функцию вне класса, и также через get-методы, то все так же будет работать. Но я хотела сделать что бы она была в классе.

sergregory commented 9 years ago

Прости что сложно... Если функция объявлена как друг, то она может обращаться к приватным полям и методам класса напрямую. Компилятору не важно, напишешь ты реализацию дружественной функции в классе или вне него - она всё равно не будет членом класса, но сохранит доступ к его приватным полям и методам. Так что где писать реализацию - твой личный выбор, нравится больше в классе - значит будет в классе)

Dashia18 commented 9 years ago

Нет, строчка в коде где метод обращается к полям класса не будет работать без get-методов.

//operator overloading print << Simple_fraction
std::ostream& operator<< (std::ostream& out, const Simple_fraction& sf_a){

    out<<"(operator >> overloading) SF = "<<sf_a.get_numerator()<<"/"<<sf_a.get_denominatior()<<"\n\n";
    return out;
}
sergregory commented 9 years ago

Вот так работает здесь можно запустить:

#include <iostream>

class Simple_fraction{

public:
    Simple_fraction(int num, int den)
    : numerator(num)
    , denominatior(den)
    {}
    friend std::ostream& operator<< (std::ostream& out, const Simple_fraction& sf_a)
    {
        out << sf_a.numerator << '/' << sf_a.denominatior;
        return out;
    }
private:
    int numerator;
    int denominatior;
};

int main() {
    Simple_fraction s(1,2);
    std::cout << s << std::endl;
    return 0;
}