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

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

Страниц: 1 ... 4 5 [6] 7 8 ... 13   Вниз
  Печать  
Автор Тема: Как писать ООП программы?  (Прочитано 86548 раз)
Hrundel
Гость
« Ответ #75 : Февраль 24, 2014, 13:21 »

Объекты потомки Shape, по логики вещей, не могут существовать без Viewer, иначе их существование становится безсмысленным. Поэтому создаваться они должны для Viewer. То есть внутри этого класса.
Не вижу никаких оснований для такого вывода. Напр объекты могут быть загружены из файла и сохранены в др формате - никакого вьюера и близко нет. Или наоборот, могут быть использована неск вьюерами сразу.

Ну... Тут ты меня сразу уговорил Улыбающийся

Код
C++ (Qt)
Circle *crcl = new Circle();
crcl->draw(x, y, rad);
 
Ну да, а откуда Circle возьмет контекст рисования?

Я так понял, что он хочет это для OpenGL использовать. Следовательно, напишет внутри GL-ные функции рисования и вперед. Или класс должен быть универсальным? И подходить для QPainter?
Записан
8Observer8
Гость
« Ответ #76 : Февраль 24, 2014, 20:07 »

Вот аргументы почему draw() нужно сделать методом класса Viewer, а не методом класса Shape:

Фигура - это объект, который не может совершать действий (к примеру, рисовать сама себя). Да, фигура, в моём случае, может расcчитать свою площадь и т.д., но для пользователя это незаметно, ведь он запрашивает площадь так: s = circle.area(). Можно сказать, что фигура обладает определённым набором характеристик (цвет, координаты и т.д.).

В моём случае, объект класса Viewer - это художник. У него есть холст и набор красок. Мы отдаём ему фигуру (одну за другой и viewer сохраняет их в массив), которая хранит в себе список своих характеристик. Художнику не нужно знать, что это за фигура. Он смотрит на список характеристик и находит там координаты точек. Выставляет все точки на холст и соединяет определёнными линиями, закрашивает определённым цветом, располагает фигуры в определённых местах и т.д.

А "компьютерному художнику" приходится хранить у себя массив фигур, чтобы перерисовывать, когда мы, к примеру, меняем размер окна (или перекрываем окно другим окном).

Я ещё не до конца реализовал. С помощью addForPainting(const myShapes::Shape &ps) я буду добавлять фигуру для рисования в закрытый массив std::vector<myShapes::Shape> shapes; и перерисовывать с помощью draw() весь массив фигур из shapes.

viewer.h
Код
C++ (Qt)
#ifndef VIEWER_H
#define VIEWER_H
 
#include <QGLWidget>
#include "shape.h"
 
#include <vector>
 
class Viewer : public QGLWidget
{
public:
   // ...
   void addForPainting(const myShapes::Shape &ps);
 
   // ...
private:
   std::vector<myShapes::Shape> shapes;
   void draw(const myShapes::Shape &ps, int xOffset, int yOffset);
};
 
#endif // VIEWER_H
 
« Последнее редактирование: Февраль 24, 2014, 20:13 от 8Observer8 » Записан
8Observer8
Гость
« Ответ #77 : Февраль 24, 2014, 20:17 »

Намучился с одной ошибкой. Оказывается свои классы нужно прятать в своё пространство имён. Мой класс Rectangle начал конфликтовать с включением #include <QGLWidget> Пришлось сделать так:

shape.h
Код
C++ (Qt)
#ifndef SHAPE_H
#define SHAPE_H
 
namespace myShapes {
 
class Shape
{
public:
   // ...
};
 
}
 
#endif // SHAPE_H
 

rectangle.h
Код
C++ (Qt)
#ifndef RECTANGLE_H
#define RECTANGLE_H
 
#include "shape.h"
 
namespace myShapes {
 
class Rectangle : public Shape
{
public:
   // ...
};
 
}
 
#endif // RECTANGLE_H
 
Записан
Hrundel
Гость
« Ответ #78 : Февраль 24, 2014, 20:39 »

Вот аргументы почему draw() нужно сделать методом класса Viewer, а не методом класса Shape
...

[/quote]
Ну да, а откуда Circle возьмет контекст рисования?

Игорь, я тебя сильно уважаю, но тут ты сказал не подумав! Конечно, дико извеняюсь - но фигня это всё !!!

Если хочешь помещать потомков Shape в QGraphicsView, то ничего такого не надо!!!
Все потомки имеют метод paint и рисуют себя сами!!!
И для OpenGL та же ерунда действует.

В заголовке
Код
C++ (Qt)
class Shape : public QObject, public QGraphicsItem
{
       Q_OBJECT
 
   public:
       Shape ();
       ~Shape ();
       QRectF boundingRect() const;
       void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
 
       ... // и так далее
}
 

Дальше все от него наследуются, переопределяют paint и рисуют что хотят.
« Последнее редактирование: Февраль 25, 2014, 00:44 от Hrundel » Записан
Hrundel
Гость
« Ответ #79 : Февраль 24, 2014, 20:47 »

Оказывается свои классы нужно прятать в своё пространство имён. Мой класс Rectangle начал конфликтовать с включением #include <QGLWidget>

И здесь дико извеняюсь - полная ерунда!!!
Записан
8Observer8
Гость
« Ответ #80 : Февраль 25, 2014, 08:07 »

Реализовать рисование у меня пока не получается. Вернусь к этому позже.

Помогите мне, пожалуйста, понять вот это правило: "Правило 37: Никогда не переопределяйте наследуемое значение аргумента функции по умолчанию" http://www.e-reading.bz/chapter.php/1002058/95/Mayers_-_Effektivnoe_ispolzovanie_CPP.html

Сначала автор приводит плохой пример, что не стоит переопределять аргумент поумолчанию наследуемой виртуальной функции. Так как виртуальные функции связываются динамически, а аргументы поумолчанию - статически:

main.cpp
Код
C++ (Qt)
#include <QCoreApplication>
#include <QDebug>
 
// классы для представления геометрических фигур
class Shape {
public:
   enum ShapeColor { Red, Green, Blue };
 
   // все фигуры должны предоставлять функцию для рисования
   virtual void draw(ShapeColor color = Red) const = 0;
   // ...
 
   virtual ~Shape() {
 
   }
};
 
class Rectangle: public Shape {
public:
   // заметьте, другое значение параметра по умолчанию – плохо!
   virtual void draw(ShapeColor color = Green) const {
       qDebug() << color;
   }
};
 
class Circle: public Shape {
public:
   virtual void draw(ShapeColor color) const {
       qDebug() << color;
   }
};
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
 
   Shape *pr = new Rectangle;
   pr->draw();
   delete pr;
 
   return a.exec();
}
 

Чтобы было правильно, нужно написать то же самый код только изменить одну единственную строку (то есть убрать аргумент поумолчанию из класса потомка)

Код
C++ (Qt)
class Rectangle: public Shape {
public:
   virtual void draw(ShapeColor color) const {
       qDebug() << color;
   }
};
 

Но я не могу понять зачем автор приводит подход называемый: идиома невиртуального интерфейса (NVI) (см. код далее) Что нам это даёт? Чем это лучше предыдущего подхода (когда мы просто не переопределяем аргумент)? При подходе NVI мы так же не можем переопределить аргумент поумолчанию, так как в C++ перезагружать невиртуальные функции нельзя.

Код
C++ (Qt)
#include <QCoreApplication>
#include <QDebug>
 
class Shape {
public:
   enum ShapeColor { Red, Green, Blue };
   void draw(ShapeColor color = Red) const // теперь – невиртуальная
   {
       doDraw(color); // вызов виртуальной функции
   }
   // ...
private:
   virtual void doDraw(ShapeColor color) const = 0; // реальная работа выполняется в этой функции
};
 
class Rectangle: public Shape {
public:
   // ...
private:
   virtual void doDraw(ShapeColor color) const { // обратите внимание на отсутствие у аргумента значения по умолчанию
       qDebug() << color;
   }
};
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
 
   Shape *pr = new Rectangle;
   pr->draw();
   delete pr;
 
   return a.exec();
}
 
« Последнее редактирование: Февраль 25, 2014, 08:14 от 8Observer8 » Записан
8Observer8
Гость
« Ответ #81 : Февраль 25, 2014, 09:19 »

Я понял. Автор использовал идиому невиртуального интерфейса (NVI), чтобы пользователь класса Shape не смог переопределить draw(), то есть не стал бы писать свой аргумент поумолчанию. Пользователь увидит, что draw() невиртуальна и не станет её переопределять (ибо нельзя).
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #82 : Февраль 25, 2014, 10:54 »

Так просто это сделать не получиться, нужно будет отказываться от возврата константной ссылки, вместе с бинарной совместимостью.
Приплыли.
Да, напр было такое использование
Код
C++ (Qt)
const QString & base = obj.filePath1(..);
 
Это придется переделать - ну, на мой взгляд, в этом больше хорошего. Возможно я просто смирюсь с копированием, но может и придумаю новое решение - если это место критично. И компилятор меня "ткнет носиком". А в погоне за (мнимой) универсальностью легко можно загнать проблему "вглубь"
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #83 : Февраль 25, 2014, 11:02 »

ну, на мой взгляд, в этом больше хорошего.
А что в этом хорошего?

А в погоне за (мнимой) универсальностью легко можно загнать проблему "вглубь"
В погоне за мнимой преждевременной оптимизацией можно упустить гораздо более важные проблемы. Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #84 : Февраль 25, 2014, 11:40 »

Помогите мне, пожалуйста, понять вот это правило: "Правило 37: Никогда не переопределяйте наследуемое значение аргумента функции по умолчанию" http://www.e-reading.bz/chapter.php/1002058/95/Mayers_-_Effektivnoe_ispolzovanie_CPP.html
Мне кажется Вы погнались за второстепенными вещами, а ведь уже получили массу гораздо более полезных уроков - неплохо бы их заучить основательно

1) Не давайте общих имен (типа Rectangle) - велика вероятность они пересекутся. Простой и хороший способ - начинать свой класс (тип) с большой буквы, напр я использую "С". Написав CRectangle ясно видно - это мой класс. Др букву "T" я использую для своих typedef. Имена членов классов лучше начинать с "m" или "m_"

2) Использование namespace - палка о двух концах. Не стоит его заводить "просто так", напр в Вашем случае легко обойтись без него.

3) Очень важная вещь прошла мимо Вашего внимания: базовый инструментарий. Вы привлекли Qt - это важное, принципиальное решение. Тогда встает вопрос - так может и шейпы лучше делать средствами Qt? Напр освоить QGraphicsItem. Да, так Вы много не на-архитектурите, увы - все уйдет в чтение букваря. Но практический результат будет на порядок выше. Задаваться вопросом "а не изобретаю ли я велосипед?" приходится всем и всегда.  

Ладно, вернемся к Вашему проектированию
Вот аргументы почему draw() нужно сделать методом класса Viewer, а не методом класса Shape:

Фигура - это объект, который не может совершать действий (к примеру, рисовать сама себя). Да, фигура, в моём случае, может расcчитать свою площадь и т.д., но для пользователя это незаметно, ведь он запрашивает площадь так: s = circle.area(). Можно сказать, что фигура обладает определённым набором характеристик (цвет, координаты и т.д.).

В моём случае, объект класса Viewer - это художник. У него есть холст и набор красок. Мы отдаём ему фигуру (одну за другой и viewer сохраняет их в массив), которая хранит в себе список своих характеристик. Художнику не нужно знать, что это за фигура. Он смотрит на список характеристик и находит там координаты точек. Выставляет все точки на холст и соединяет определёнными линиями, закрашивает определённым цветом, располагает фигуры в определённых местах и т.д.

А "компьютерному художнику" приходится хранить у себя массив фигур, чтобы перерисовывать, когда мы, к примеру, меняем размер окна (или перекрываем окно другим окном).

Я ещё не до конца реализовал. С помощью addForPainting(const myShapes::Shape &ps) я буду добавлять фигуру для рисования в закрытый массив std::vector<myShapes::Shape> shapes; и перерисовывать с помощью draw() весь массив фигур из shapes.
1) Типичная (очень популярная) ошибка - неудачный выбор std::vector. Вспомните как "уплывали" адреса - теперь уже трудно будет подавать элементы вектора по указателю/ссылке, все время придется оглядываться "а не изменился ли вектор". Здесь хороший выбор QList

2) С какой это стати "художник" хранит все шейпы? Они могут прекрасно жить без всякого художника (см пример выше). Да, машине рисования они должны быть известны (иначе нечего рисовать), но это все, добавлением/удалением/редактированием шейпов рисование не занимается.

3) Аргументы "фигура не должна уметь себя рисовать" пока неубедительны, и что Вы предлагаете взамен - неясно. Попробуйте ответить на вопрос "а что плохого если фигура сама себя рисует?", он уже звучал выше

Да, и терминов типа "холст" лучше избегать, уж очень отдает дилетантством Улыбающийся К тому же в современном рисовании нет аналога/подобия "холста"

 
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #85 : Февраль 25, 2014, 11:46 »

2) Использование namespace - палка о двух концах. Не стоит его заводить "просто так", напр в Вашем случае легко обойтись без него.
Можно подробней о "двух концах", в чем по вашему может быть проблема и почему по вашему его не стоит заводить просто так? И когда же по вашему его стоит заводить? Улыбающийся
А я бы рекомендовал его использовать почаще, ибо он прекрасно справляется со своей ролью. Нужно просто с ним разобраться один раз.
« Последнее редактирование: Февраль 25, 2014, 11:50 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #86 : Февраль 25, 2014, 13:01 »

Можно подробней о "двух концах", в чем по вашему может быть проблема и почему по вашему его не стоит заводить просто так? И когда же по вашему его стоит заводить? Улыбающийся
А я бы рекомендовал его использовать почаще, ибо он прекрасно справляется со своей ролью. Нужно просто с ним разобраться один раз.
Пример где я мучался продолжая разработку предшественников. Первый написал
Код
C++ (Qt)
namespace TriangulateUtils {
 
class TriangleLoop {
...
 
Ничего плохого нет. Второй писал др фрагмент и сделал так
Код
C++ (Qt)
namespace FontTriangulateUtils {
typedef TriangulateUtils::TriangleLoop TriangleLoop;
 
Т.к. "сопля была слишком длинной". Третий перелил это в свой namespace. В результате я не мог быстро посмотреть описание самого класса, браузер выходит на typedef - и все  Улыбающийся Много времени съедается на отслеживание пересечений, а отделаться using не удается.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #87 : Февраль 25, 2014, 13:09 »

Много времени съедается на отслеживание пересечений, а отделаться using не удается.
Дааа. Главное что бы эти "предшественники" не пользовались шаблонами, никогда. А с дуру можно что хош сломать. Улыбающийся

Совет остается в силе:
Нужно просто с ним разобраться один раз.
« Последнее редактирование: Февраль 25, 2014, 13:13 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #88 : Февраль 25, 2014, 14:21 »

Совет остается в силе:
Нужно просто с ним разобраться один раз.
Так резво не выйдет. Надо пописать типа "MyNamespace::" раз так тыщу, чтобы почувствовать уместно/нужно ли оно или так, для понту
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #89 : Февраль 25, 2014, 14:24 »

Так резво не выйдет. Надо пописать типа "MyNamespace::" раз так тыщу, чтобы почувствовать уместно/нужно ли оно или так, для понту
Ну это только если с ними не разобраться "MyNamespace::" нужно писать раз так тыщу. Если не компетентен, то должен страдать. Да. Так везде. Улыбающийся
Записан
Страниц: 1 ... 4 5 [6] 7 8 ... 13   Вверх
  Печать  
 
Перейти в:  


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