Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > IT форум > Помощь студентам
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 16.08.2013, 21:03   #11
rrrFer
Санитар
Старожил
 
Аватар для rrrFer
 
Регистрация: 04.10.2008
Сообщений: 2,577
По умолчанию

Цитата:
Это ещё один огромный минус нашим преподам в универах научили блин...

Они, видимо, таких книг не читают и опыта у них нет. Я имею ввиду, таких преподов, которые дают подобные задания.
И тебе задание не нравится?
Если ты внимательно-внимательно читал то правило Мейерса, то заметил, что он грит примерно так "в нашем случае интерфейса будет достаточно, поэтому дружба не нужна". Но ведь может быть много вариантов, когда интерфейса не хватит. - допустим тебе также требуется неявно приводить типы, но в классе есть данные, которые ты не хочешь предоставлять всем подряд (геттер делать по каким-то причинам опасно), но вот этой функции те защищенные данные нужны.

Пример высосан из пальца, у меня такого не было. Но в любом случае, я не вижу ничего плохого в дружественной функции. Дружба - это плохо, потому что это очень сильная связь (сильнее чем композиция). Между дружественной функцией и классом связь примерно такая же сильная как между классом и его методом. Но ведь никто не говорит что методы - плохо )).

Плохо - это дружба между классами - это факт.

А задание среднестатистическое, нормально ) Студент тоже среднестатистический - хреновый.
rrrFer вне форума Ответить с цитированием
Старый 16.08.2013, 21:06   #12
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,323
По умолчанию

Цитата:
Сообщение от Stilet Посмотреть сообщение
Я пока тоже не понял в чем же ошибка преподавателей. Где вы там нашли быдлокодерство в задании?
В задании так сказано:

Цитата:
Реализовать методы класса: ввод дроби; сложение двух дробей; вычитание двух дробей; умножение двух дробей.
К примеру, оператор умножения дробей не может быть членом класса, так как мы не сможем умножить число на дробь. Этот оператор нужно сделать отдельной функцией.

Вот пример плохого кода, когда оператор является членом класса:

Код:
class Rational {
public:

    Rational(int numerator = 0, int denominator = 1)
    : m_numerator(numerator), m_denominator(denominator) {
    }

    void setNumerator(int n);
    void setDenominator(int d);
    int getNumerator() const;
    int getDenominator() const;
    
    const Rational operator*(const Rational& rhs);

private:
    int m_numerator;
    int m_denominator;
};

const Rational Rational::operator *(const Rational& rhs) {
    Rational result;
    result.setNumerator(m_numerator * rhs.getNumerator());
    result.setDenominator(m_denominator * rhs.getDenominator());
    return result;
}

#include <ostream>

std::ostream& operator <<(std::ostream& stream, const Rational& r) {
    stream << r.getNumerator() << "/" << r.getDenominator();
}

int Rational::getNumerator() const {
    return m_numerator;
}

int Rational::getDenominator() const {
    return m_denominator;
}

void Rational::setNumerator(int n) {
    m_numerator = n;
}

void Rational::setDenominator(int d) {
    m_denominator = d;
}

#include <iostream>

int main(int argc, char** argv) {
    // Умножаем две дроби
    Rational a(2, 5);
    Rational b(2, 3);
    Rational c;
    c = a * b; // нормально
    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
    std::cout << "result = " << c << std::endl;
    
    // Умножаем дробь на число
    Rational d(1,5);
    c = d * 3; // нормально
    std::cout << c << std::endl;

    // Умножаем число на дробь
    c = 3 * d; // ОШИБКА
    return 0;
}
Это подробно описано в "Правило 24: Объявляйте функции, не являющиеся членами, когда преобразование типов должно быть применимо ко всем параметрам"

по этой ссылке: http://lib.rus.ec/b/269732/read

Последний раз редактировалось 8Observer8; 16.08.2013 в 21:16.
8Observer8 вне форума Ответить с цитированием
Старый 16.08.2013, 21:11   #13
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,323
По умолчанию

Цитата:
Сообщение от rrrFer Посмотреть сообщение
И тебе задание не нравится?
Если ты внимательно-внимательно читал то правило Мейерса, то заметил, что он грит примерно так "в нашем случае интерфейса будет достаточно, поэтому дружба не нужна". Но ведь может быть много вариантов, когда интерфейса не хватит. - допустим тебе также требуется неявно приводить типы, но в классе есть данные, которые ты не хочешь предоставлять всем подряд (геттер делать по каким-то причинам опасно), но вот этой функции те защищенные данные нужны.

Пример высосан из пальца, у меня такого не было. Но в любом случае, я не вижу ничего плохого в дружественной функции. Дружба - это плохо, потому что это очень сильная связь (сильнее чем композиция). Между дружественной функцией и классом связь примерно такая же сильная как между классом и его методом. Но ведь никто не говорит что методы - плохо )).

Плохо - это дружба между классами - это факт.

А задание среднестатистическое, нормально ) Студент тоже среднестатистический - хреновый.
Цитата из http://lib.rus.ec/b/269732/read

Цитата:
Код:
lass Rational {
    ... // не содержит operator*
};

const Rational operator*(const Rational& lhs, // теперь свободная функция
        const Rational& rhs) {
    return Rational(lhs.numerator() * rhs.numerator(),
            lhs.denominator() * rhs.denominator());
}
Rational oneFourth(1, 4);
Rational result;
result = oneFourth * 2; // правильно
result = 2 * oneFourth; // ура, работает!
Это можно было бы назвать счастливым концом, если бы не одно «но». Должен ли operator* быть другом класса Rational?
В данном случае ответом будет «нет», потому что operator* может быть реализован полностью в терминах открытого интерфейса Rational. Приведенный выше код показывает, как это можно сделать. И мы приходим к важному выводу: противоположностью функции-члена является свободная функция, а функция – друг класса. Многие программисты на C++ полагают, что раз функция имеет отношение к классу и не должна быть его членом (например, из-за необходимости преобразовывать типы всех аргументов), то она должна быть другом. Этот пример показывает, что такое предположение неправильно. Если вы можете избежать назначения функции другом класса, то должны так и поступить, потому что, как и в реальной жизни, друзья часто доставляют больше хлопот, чем хотелось бы. Конечно, иногда отношения дружественности оправданы, но факт остается фактом: если функция не должна быть членом, это не означает автоматически, что она должна быть другом.
Сказанное выше правда, и ничего, кроме правды, но это не вся правда. Когда вы переходите от «Объектно-ориентированного C++» к «C++ с шаблонами» (см. правило 1) и превращаете Rational из класса в шаблон класса, то вступают в силу новые факторы, новые способы их учета, и появляются неожиданные проектные решения. Все это является темой правила 46.
8Observer8 вне форума Ответить с цитированием
Старый 16.08.2013, 21:16   #14
rrrFer
Санитар
Старожил
 
Аватар для rrrFer
 
Регистрация: 04.10.2008
Сообщений: 2,577
По умолчанию

Цитата:
К примеру, оператор умножения дробей не может быть членом класса, так как мы не сможем умножить число на дробь
Дак и не требуется, речь идет о умножении "двух дробей". Суть тут в том, что ты сам додумал задание, а этого делать ненадо, и на работе за такие додумки по голове настучат ))

Немного не в тему.
Неявное приведение типов, на которое упирает тут Мейерс - тоже плохо.

Чисто гипотетически, ты напишешь 2 функции
f(complex &, complex &);
f(rational &, rational &);

вызовешь f(1,1).
Если и comlex и rational могут неявно привестись - какая функция будет вызвана? - если верить Страуструпу - "вызывается наиболее подходящая".

Вобщем поэтому писать
complex a(1), b = 2 + a;
уже нехорошо.

Цитата:
Цитата:
В данном случае ответом будет «нет»,
вот я о том же.
rrrFer вне форума Ответить с цитированием
Старый 16.08.2013, 21:23   #15
rrrFer
Санитар
Старожил
 
Аватар для rrrFer
 
Регистрация: 04.10.2008
Сообщений: 2,577
По умолчанию

Написал я выше про опасности всякие и даже покритиковал советы Мейерса, а вот пример, только что придумал (чуть подправил пример с cplusplus):
Код:
#include <iostream>     // std::cout
#include <numeric>      // std::accumulate

int main () {
  float numbers[] = {10.1,20.3,30.7};

  std::cout << std::accumulate(numbers,numbers+3, 1);
            << '\n';
            << std::accumulate(numbers,numbers+3, 1.f);
}
Ну вот примерно такие беды подстерегают тебя из за implicit. Если ты словишь такую ошибку - искать ее будет пи*дец как долго, я проверял.
(ну вобщем поэтому весь шум вокруг выноса функции из класса у Мейерса спорный очень). Если ты хочешь сложить число и комплектное число - так и пиши
b = complex(2) + a; // начало смотри в 14 посте

Вобщем, у Мейерса годные хорошие советы, но народная мудрость не зря грит "доверяй, но проверяй"

Последний раз редактировалось rrrFer; 16.08.2013 в 21:26.
rrrFer вне форума Ответить с цитированием
Старый 16.08.2013, 21:50   #16
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,323
По умолчанию

Цитата:
Сообщение от rrrFer Посмотреть сообщение
Дак и не требуется, речь идет о умножении "двух дробей". Суть тут в том, что ты сам додумал задание, а этого делать ненадо, и на работе за такие додумки по голове настучат ))
Мейерс тоже писал класс для умножения "двух дробей". А потом он вдруг сказал: "Хм..., а ведь пользователь может умножить число на дробь, ведь это вполне естественно".

Нужно сразу правильно учиться делать, чтобы на работе потом по голове не настучали. Разработчику нужно учиться предугадывать действия пользователя. Этому и учит Мейерс на данном примере. Причём пример то один в один!
8Observer8 вне форума Ответить с цитированием
Старый 20.08.2013, 23:27   #17
Вадим Мошев

Старожил
 
Аватар для Вадим Мошев
 
Регистрация: 12.11.2010
Сообщений: 8,568
По умолчанию

Цитата:
Сообщение от rrrFer Посмотреть сообщение
МБ расскажешь подробнее в чем ошибка? - я наверное быдлокодер, но ничего предрассудительного в задаче не вижу ))

Я не читал все последующие обсуждения, но мне кажется, что проблема заключается в том, что, ИМХО, методы классов не должны содержать операторов ввода/вывода. Единственное, что ему можно сделать, вернуть результат, а вывод на экран - уже извне, то есть, в основной программе.
Вадим Мошев вне форума Ответить с цитированием
Старый 21.08.2013, 03:47   #18
rrrFer
Санитар
Старожил
 
Аватар для rrrFer
 
Регистрация: 04.10.2008
Сообщений: 2,577
По умолчанию

Цитата:
Я не читал все последующие обсуждения, но мне кажется, что проблема заключается в том, что, ИМХО, методы классов не должны содержать операторов ввода/вывода. Единственное, что ему можно сделать, вернуть результат, а вывод на экран - уже извне, то есть, в основной программе.
Тебе именно кажется. В задаче нет слова "экран" вобще ниразу, есть слова "ввод" и "вывод". Если классы "не должны содержать операторов ввода/вывода.", нахрена эти операторы вобще в языке есть? - явно не просто так их в каждый стандарт проталкивают.

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

Последний раз редактировалось rrrFer; 21.08.2013 в 04:00.
rrrFer вне форума Ответить с цитированием
Старый 21.08.2013, 09:58   #19
Utkin
Старожил
 
Аватар для Utkin
 
Регистрация: 04.02.2009
Сообщений: 17,351
По умолчанию

Цитата:
Главное, меня интересует "почему нельзя?", "почему не должны?" - пусть даже речь об экране идет.
Потому что числам не свойственно выводится на экран. Этим должен заниматься хранилище чисел или контроллер, не важно как оно там называется. Но не числа. Числа могут предоставлять информацию о себе в обобщенном ввиде, а уже другой класс через всякие интерфейсы и прочие полиморфизмы решает как и через что выводить числа.
Ну вот бредовая аналогия - я вот посчитал на калькуляторе два числа, а они хлоп! и сами на бумажку записались. Блин, удобно, но записываются карандашом. А мне надо ручкой гелевой.
Маньяк-самоучка
Utkin появился в результате деления на нуль.
Осторожно! Альтернативная логика

Последний раз редактировалось Utkin; 21.08.2013 в 10:00.
Utkin вне форума Ответить с цитированием
Старый 21.08.2013, 10:45   #20
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,323
По умолчанию

Что-то я запутался... В противоположенность оператору вывода - операция умножения целого числа на дробь свойственна дробям?

Код:
"дробь" = "целое число" * "дробь"
Да, в задании об этом не говорится. Студент, со одной стороны, не обязан делать того, что в задании не требуется. Но всё же ведь свойственна?

Противоречие. С одной стороны, операция умножения свойственна дробям. Значит оператор умножения должен быть членом класса. С другой стороны, язык не позволит определить оператор умножения внутри класса, который принимает два аргумента . Точнее, если оператор внутри класса, то он принимает правый параметр через аргумент, а левый через this. И тогда умножение числа на дробь будет невозможна:
Код:
class Rational {
public:

    Rational(int numerator = 0, int denominator = 1)
    : m_numerator(numerator), m_denominator(denominator) {
    }

    void setNumerator(int n);
    void setDenominator(int d);
    int getNumerator() const;
    int getDenominator() const;
    
    const Rational operator*(const Rational& rhs);

private:
    int m_numerator;
    int m_denominator;
};

const Rational Rational::operator *(const Rational& rhs) {
    Rational result;
    result.setNumerator(m_numerator * rhs.getNumerator());
    result.setDenominator(m_denominator * rhs.getDenominator());
    return result;
}

#include <ostream>

std::ostream& operator <<(std::ostream& stream, const Rational& r) {
    stream << r.getNumerator() << "/" << r.getDenominator();
}

int Rational::getNumerator() const {
    return m_numerator;
}

int Rational::getDenominator() const {
    return m_denominator;
}

void Rational::setNumerator(int n) {
    m_numerator = n;
}

void Rational::setDenominator(int d) {
    m_denominator = d;
}

#include <iostream>

int main(int argc, char** argv) {
    // Умножаем две дроби
    Rational a(2, 5);
    Rational b(2, 3);
    Rational c;
    c = a * b; // нормально
    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
    std::cout << "result = " << c << std::endl;
    
    // Умножаем дробь на число
    Rational d(1,5);
    c = d * 3; // нормально
    std::cout << c << std::endl;

    // Умножаем число на дробь
    c = 3 * d; // ОШИБКА
    return 0;
}
Я хочу, чтобы число можно было умножать на дробь. Получается, что нужно либо делать оператор умножения дружественной, либо свободной. Какой вариант считаете лучшим?

Последний раз редактировалось 8Observer8; 21.08.2013 в 11:02.
8Observer8 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Работа с классами АндрейBAN C# (си шарп) 4 17.12.2012 13:10
Работа с классами на С++ darinadarina Помощь студентам 1 08.05.2012 18:34
Работа с классами petrovich2005 Помощь студентам 2 24.06.2011 17:32
Работа с классами Superlotles Общие вопросы C/C++ 6 16.06.2010 17:15
С++: работа с классами MR_Andrew Общие вопросы C/C++ 23 01.04.2010 23:46