Russian Qt Forum
Октябрь 01, 2024, 04:44 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3 4 ... 6   Вниз
  Печать  
Автор Тема: Реализация интерфейса[РЕШЕНО]  (Прочитано 46370 раз)
Даниил
Гость
« Ответ #15 : Апрель 02, 2012, 17:13 »

Нарушение инкапсуляции идет. Я уже получил звездюлей за такую реализацию. О внутренних деталях класса должен знать только класс. А сторонние функции/классы должны иметь доступ только к методам.
Может быть в вашем случае большая гибкость получается - не спорю. Но типа как - не клёва ^_^  Смеющийся  Смеющийся  Смеющийся

Интересно, почему же тогда во всех серьёзных мат. библиотеках эти операторы (+ - *) делаются в виде дружественных функций?
Вот небольшой пример:
Код
C++ (Qt)
class Number
{
public:
   Number() : _x(0) {}
   Number(int x) : _x(x) {}
   Number &operator+(const Number &x) {
       _x += x._x;
       return *this;
   }
   friend std::ostream& operator<<(std::ostream&, const Number&);
   //friend const Number operator+(const Number&, const Number&);
private:
   int _x;
};
 
std::ostream& operator<<(std::ostream& out, const Number& x) {
   out << x._x;
   return out;
}
 
/*
const Number operator+(const Number&x1, const Number &x2) {
   return Number(x1._x + x2._x);
}
*/

 
Number n1 = 1;
Number n2 = 2;
 
Number n3 = n1+(1+n2);
 
cout << n3 << endl;
 
 
Попробуйте скомпилируйте это, когда оператор сложения - член класса.
А если не получится, закомментируйте его и раскомментируйте вариант, где этот оператор в виде дружественной функции.  

Операторы вывода "<<" или ввода ">>" всегда перегружают как дружественные, это исключение. Во всех учебниках написано.
P.S. И да, что закоментирован, что не закоментирован дружественный оператор  Смеющийся Это читерство
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #16 : Апрель 02, 2012, 17:18 »

Цитировать
Операторы вывода "<<" или ввода ">>" всегда перегружают как дружественные, это исключение. Во всех учебниках написано.
Я говорю о операторе сложения.
Тот пример, что приведён выше не будет работать, если оператор+ является членом класса.

У подхода, когда подобные операторы выносятся в виде функций есть ещё одно преимущество..    

Цитировать
P.S. И да, что закоментирован, что не закоментирован дружественный оператор   Это читерство
Это вам для наглядности))
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Даниил
Гость
« Ответ #17 : Апрель 02, 2012, 17:24 »

В вашем примере - может и не будет. А вот в этом:
Код
C++ (Qt)
class TPNumber
{
  /* .......... */
  TPNumber operator +(const TPNumber &p1);
  /* .......... */
}
 
TPNumber TPNumber::operator +(const TPNumber &p1)
{
       return TPNumber(this->number + p1.number, this->sistemaSchisleniya, this->tochnost);
}
 
Все работает и сдано с оценкой - отлично. Кстати отчасти этой оценке я благодарен данному форуму Улыбающийся
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #18 : Апрель 02, 2012, 17:36 »

Цитировать
Все работает и сдано с оценкой - отлично. Кстати отчасти этой оценке я благодарен данному форуму
Вы меня не понимать)

Хорошо, объясню на вашем примере.
Пусть у вас есть два класса чисел Number и NumberF.
Так вот, если вы пойдёте по пути когда оператор+ у них будет членом, то выражения вида:
Код
C++ (Qt)
Number x = 1;
NumberF y = 3.14;
 
NumberF sum = x + y; // Внимание сюда!
 
 
вам будет проблематично реализовать. Придётся лезть  в эти классы и добавлять в них доп. операторы+
Тем самым создавая связи между этими классами, от которых в ООП так пытаются избавиться)
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Даниил
Гость
« Ответ #19 : Апрель 02, 2012, 17:39 »

Цитировать
Все работает и сдано с оценкой - отлично. Кстати отчасти этой оценке я благодарен данному форуму
Вы меня не понимать)

Хорошо, объясню на вашем примере.
Пусть у вас есть два класса чисел Number и NumberF.
Так вот, если вы пойдёте по пути когда оператор+ у них будет членом, то выражения вида:
Код
C++ (Qt)
Number x = 1;
NumberF y = 3.14;
 
NumberF sum = x + y; // Внимание сюда!
 
 
вам будет проблематично реализовать. Придётся лезть  в эти классы и добавлять в них доп. операторы+
Тем самым создавая связи между этими классами, от которых в ООП так пытаются избавиться)

Собственно я никогда и не хотел складывать разнотипные объекты*. Типы всегда одинаковые, это контролируется программно. Просто я хотел, чтобы они ссылки на них хранились в указателях на их базовый класс, чтобы в процессе работы программы, я бы мог сменить тип обоих указателей и выполнить сложение двух других типов.
« Последнее редактирование: Апрель 02, 2012, 17:47 от Даниил » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #20 : Апрель 02, 2012, 17:54 »

Цитировать
Просто я хотел, чтобы они ссылки на них хранились в указателях на их базовый класс, чтобы в процессе работы программы, я бы мог сменить тип обоих указателей и выполнить сложение двух других типов.
Как вы себе это представляете?
Так:
Код
C++ (Qt)
Number *n1 = new NumberF(3.14);
Number *n2 = new NumberF(2.71);
 
*n2 = *n2 + *n1;
 
Так не получится. Number - чисто абстрактный класс и разыменовать n1 и n2 не получится.
Какой смысл объявлять в интерфейсном классе Number эти операторы?
Интересно, кто у вас там методички пишет?)    
« Последнее редактирование: Апрель 02, 2012, 18:06 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #21 : Апрель 02, 2012, 18:01 »

Код
C++ (Qt)
TPNumber TPNumber::operator +(const TPNumber &p1)
{
       return TPNumber(this->number + p1.number, this->sistemaSchisleniya, this->tochnost);
}
 
Все работает и сдано с оценкой - отлично.
Вполне заслуженно, но с минусом: этот оператор должен быть константным
Код
C++ (Qt)
TPNumber TPNumber::operator +(const TPNumber &p1) const
 
Вообще для оператора + "что в лоб что по лбу" - ведь он использует сам класс и все. Поэтому оператор-член и friend равноценны

вам будет проблематично реализовать. Придётся лезть  в эти классы и добавлять в них доп. операторы+
Тем самым создавая связи между этими классами, от которых в ООП так пытаются избавиться)
m_ax говорит умные вещи (иногда), стоит прислушаться. Почему те же операторы слива в поток обычно friend? Потому что если бы это были операторы-члены - нам пришлось бы писать так

Код
C++ (Qt)
#include <fstream>  
 
class PNumber {
...
};
 
Потом, по каким-то причинам мы решили перевести I/O напр на Qt. Придется писать  #include <QFile>, удалять #include <fstream>  и кромсать хедер в котором PNumber. Вот они - те "плохие" связи. Поэтому лучше сделать PNumber независимым
 
Так не получится. Number - чисто абстрактный класс и разыменовать n1 и n2 не получится.
Какой смысл объявлять в интерфейсном классе Number эти операторы?
Интересно, кто у вас там методички пишет?)   
Чего ж это я не могу разыменовать указатель на абстрактный класс? Ну получу ссылку на абстрактный класс, все нормуль.
Записан
Даниил
Гость
« Ответ #22 : Апрель 02, 2012, 18:59 »

Мы отошли немного от темы. Вопрос в принципе теперь в лоб, т.к. мы уже разобрали множество вариантов: как лучше организовать? через касты или другие методы искать?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #23 : Апрель 02, 2012, 19:21 »

Мы отошли немного от темы. Вопрос в принципе теперь в лоб, т.к. мы уже разобрали множество вариантов: как лучше организовать? через касты или другие методы искать?
Ну выбора-то у Вас нет, кастить придется. Мое мнение что базовый класс должен иметь операторы которые вызывают виртуальные ф-ции которые уже вызывают "родные" операторы. См пример реализации выше. Не хотите "показаться слишком умным" - ну это можно понять, нормально. Тогда тупо передираете текст методички, только типы возвращаемых значений везде ставите TNumber (по-другому не получится). Ну и каститесь внутри каждого оператора.
Записан
Даниил
Гость
« Ответ #24 : Апрель 02, 2012, 20:42 »

Вобщем, я пока так решил сделать, по вашим советам через касты:
Код
C++ (Qt)
class Number
{
  /* ... ... ... */
public:
   virtual Number& operator =(Number &n) = 0;
   virtual Number& operator +(Number &n) = 0;
   /* ... ... ... */
};
 
class PNumber : public Number
{
  /* ... ... ... */
   Number& operator =(Number &p1);
   Number& operator +(Number &p1);
  /* ... ... ... */
};
 
Number& PNumber::operator +(PNumber &p1)
{
   PNumber *pn;
   Number *n = &p1;
   pn = dynamic_cast<PNumber *>(n);
   pn->number += this->number;
   return *pn;
}
 
Возможно слишком криво, но вполне отвечает задумке Подмигивающий
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #25 : Апрель 02, 2012, 20:57 »

Цитировать
Код
C++ (Qt)
Number& PNumber::operator +(PNumber &p1)
{
   PNumber *pn;
   Number *n = &p1;
   pn = dynamic_cast<PNumber *>(n);
   pn->number += this->number;
   return *pn;
}
 
Что-то страшное вы здесь написали)
 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Даниил
Гость
« Ответ #26 : Апрель 02, 2012, 21:11 »

Напишите проще, если сможете  Показает язык
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #27 : Апрель 02, 2012, 23:23 »

Напишите проще, если сможете  Показает язык
Нужно стремиться делать не как проще, а как правильнее)

Я бы сделал примерно так (упрощённый вариант)
Код
C++ (Qt)
enum NumberType {TNumberInt, TNumberF};
 
class Sum;
 
class Number
{
public:
   virtual ~Number() {}
   virtual NumberType type() const = 0;
   virtual Number& operator=(const Number&) = 0;
   virtual Number& operator=(const Sum&) = 0;
};
 
class Sum
{
public:
   Sum(const Number& x1, const Number& x2) : _x1(x1), _x2(x2) {}
   const Number& x1() const { return _x1; }
   const Number& x2() const { return _x2; }
   NumberType type() const { return _x1.type(); }
private:
   const Number& _x1;
   const Number& _x2;
};
 
 
class NumberInt : public Number
{
public:
   NumberInt() : _x(0) {}
   NumberInt(int x) : _x(x) {}
 
   virtual NumberType type() const { return TNumberInt; }
 
   virtual Number& operator=(const Number&x) {
       if (x.type() != TNumberInt)
           throw "error!";
 
       const NumberInt& num = static_cast<const NumberInt&>(x);
       _x = num._x;
       return *this;
   }
 
   virtual Number& operator=(const Sum& s) {
       if (s.type() != TNumberInt)
           throw "error!";
 
       const NumberInt& i1 = static_cast<const NumberInt&>(s.x1());
       const NumberInt& i2 = static_cast<const NumberInt&>(s.x2());
       _x = i1._x + i2._x;
       return *this;
   }
 
   friend const NumberInt operator+(const NumberInt&, const NumberInt&);
   friend std::ostream& operator<<(std::ostream&, const NumberInt&);
private:
   int _x;
};
 
 
std::ostream& operator<<(std::ostream& out, const NumberInt& x) {
   out << x._x;
   return out;
}
 
const Sum operator+(const Number& x1, const Number& x2) {
   return Sum(x1, x2);
}
 
const NumberInt operator+(const NumberInt& x1, const NumberInt& x2) {
   return NumberInt(x1._x + x2._x);
}
 
 
int main()
{
   Number *n1 = new NumberInt(1);
   Number *n2 = new NumberInt(2);
 
   *n1 = *n2 + *n2;
 
   cout << static_cast<const NumberInt&>(*n1);
 
   return 0;
}
 
Этот приём используется в boost ublas и в blitz, но совсем для другой цели - чтоб избавится от создания временных объектов (матриц, векторов) при операциях сложения, вычитания, умножения и пр. с ними.   
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Даниил
Гость
« Ответ #28 : Апрель 03, 2012, 06:13 »

Что-то страшное вы здесь написали)
Это я то страшное написал?!
Чем ваш вариант правильнее моего? Тем, что уже так кто-то делал в популярной библиотеке?

P.S. Хорошо, варианты реализации могут быть самыми разными. Я подстраивал свое решение под конкретную проблему, чтобы после иметь возможность добавлять другие классы не переписывая управляющих конструкций. Если интересно - позже могу выложить весь материал по данной проблеме.
« Последнее редактирование: Апрель 03, 2012, 06:16 от Даниил » Записан
Tonal
Гость
« Ответ #29 : Апрель 03, 2012, 08:53 »

И никто не вспомнил о методе двойной диспетчеризации. Улыбающийся
Правда, в данном случае для стандартных сигнатур операторов красиво не выйдет, поэтому проще показывать на обычных функциях.
Это делается примерно так:
Код
C++ (Qt)
struct Number {
 virtual const Number* add(const Number* other) const = 0;
};
 
// Собственно операции с "числами"
Number* add(const Number* x, const Number* y) {
 return x->add(y);
}
...
// Реализация двойной деспеччиризации
struct Int;
struct Rational;
struct Real;
 
struct Int : Number {
 virtual const Number* add(const Number* other) const {
   return other->addInt(this);
 }
 
 const Int* addInt(const Int* other) const {
   return new Int(val + other->val);
 }
 const Rational* addRational(const Rational* other) const {
   return other->addInt(this);
 }
 const Real* addReal(const Real* other) const {
   return other->addInt(this);
 }
 int get_val() const {
   return val;
 }
 private:
   int val;
};
 
struct Rational : Number {
 virtual const Number* add(const Number* other) const {
   return other->addRational(this);
 }
 
 const Rational* addInt(const Int* o) const {
   return new Rational(nom + denom * o->get_val(), denom);
 }
 const Rational* addRational(const Rational* o) const {
   return Rational(nom * o->denom + o->nom + denom, denom * o->denom);
 }
 const Real* addReal(const Real* other) const {
   return other->addRational(this);
 }
 private:
   int nom, denom;
};
...
 
Как можно убедится нигде нет нужды в явном приведении типов. Улыбающийся
Да и по эффективноси, данный метод добавляет всего 1 косвенный вызов, вместо переборов перечислений или сравнений строк. Улыбающийся

Двойная диспетчеризация - это частный случай реализации мультиметодов в С++.
Работает с полиморфизмом по 2м параметрам вместо 1 как в обычной виртуальности.
Можно расширить и на большее количество аргументов, если не боятся много писать. Улыбающийся

Короче, читайте Страуструпа - он рулит! Улыбающийся
« Последнее редактирование: Апрель 03, 2012, 09:12 от Tonal » Записан
Страниц: 1 [2] 3 4 ... 6   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.511 секунд. Запросов: 23.