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

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

Страниц: 1 2 3 [4] 5 6 ... 13   Вниз
  Печать  
Автор Тема: Как писать ООП программы?  (Прочитано 86630 раз)
8Observer8
Гость
« Ответ #45 : Февраль 21, 2014, 20:52 »

Ладно, не буду мучить - Вы неск раз упустили const, с точки зрения "профи" это грубая, непростительная ошибка. Очень важно дать понять модифицирует ли метод объект или нет.

Спасибо большое! Я запомню, что если метод не модифицирует члены-данные объекта, то нужно писать const и что объекты нужно передавать в методы через ссылку на константу, к примеру: void setName(const QString& name), тогда мы гарантируем, что не испортим объект, который нам доверили по ссылке.

В моём настоящем коде члены-данные объекта модифицируются, поэтому не const:

Код
C++ (Qt)
/*virtual*/ double Rectangle::calcPerimeter()
{
   mPerimeter = 2.0 * (mHeight + mWidth);
   return mPerimeter;
}
 
/*virtual*/ double Rectangle::calcArea()
{
   mArea = mHeight * mWidth;
   return mArea;
}
 

Да, и для пр-ка традиционно "Height" (а не "Length")

Спасибо! Заменил.

Также напрашиваются общие методы:
BoundingRect() // вмещающий пр-к
IsConvex()  // выпуклая фигура или нет
Scale()

Пока это сложно. Не понимаю, к примеру, что такое Scale() (вроде масштабирование). Тут уже чувствуется визуализация (вывод на экран и OpenGL) и реализация draw(). Сейчас это направление развития для меня - неприступная крепость.

Но вот появляется более сложная фигура Polygon (замкнутый контур из N вершин, причем N может быть приличным) . Вот для него уже кешировать вычисление периметра есть смысл, а площади - тем более. Развейте эту идею

Про полигон подумаю. Спасибо за идею!
« Последнее редактирование: Февраль 21, 2014, 20:53 от 8Observer8 » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



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

В моём настоящем коде члены-данные объекта модифицируются, поэтому не const:
А для чего вы сделали их членами класса?
Записан
8Observer8
Гость
« Ответ #47 : Февраль 21, 2014, 21:36 »

В моём настоящем коде члены-данные объекта модифицируются, поэтому не const:
А для чего вы сделали их членами класса?

Потому что периметр и площадь - это общие параметры для всех геометрических фигур на плоскости Улыбающийся
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



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

Потому что периметр и площадь - это общие параметры для всех геометрических фигур на плоскости Улыбающийся
Которые высчитываются при каждом вызове метода, для чего вы их храните в членах-данных?
Записан
8Observer8
Гость
« Ответ #49 : Февраль 21, 2014, 22:07 »

Которые высчитываются при каждом вызове метода, для чего вы их храните в членах-данных?

Да, но по моей схеме вызвать методы для расчёта нужно только один раз (а потом мы можем узнавать периметр и площадь с помощью методов: perimeter() и area(), которые расположены в базовом классе). Если меняем, к примеру, радиус, то нужно опять вызвать методы. Может конечно быть ситуация, что, пользователь изменит радиус и забудет вызвать функции для расчёта периметра и площади. Тогда может лучше вызов методов ещё добавить и в функцию setRadius() ?
« Последнее редактирование: Февраль 21, 2014, 22:11 от 8Observer8 » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



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

Не должно быть в интерфейсе неоднозначностей.
Если я вызову area до calcArea, он мне вернет неправильный результат.
Т.е. я должен знать и помнить, что перед получением значения я должен его вычислить с помощью другого метода. Для чего?
Пусть area и считает сразу.
Записан
8Observer8
Гость
« Ответ #51 : Февраль 22, 2014, 07:59 »

Пусть area и считает сразу.

Да, это отличный вариант, но посмотрите на мой абстрактный класс Shape. Что от него останется, если я последую Вашему совету? Зачем он тогда нужен?

Код
C++ (Qt)
#ifndef SHAPE_H
#define SHAPE_H
 
class Shape
{
public:
   Shape();
   virtual ~Shape();
 
   virtual double calcPerimeter() = 0;
   virtual double calcArea() = 0;
 
   double perimeter() const;
   double area() const;
 
protected:
   double mPerimeter;
   double mArea;
};
 
#endif // SHAPE_H
 
Записан
Bepec
Гость
« Ответ #52 : Февраль 22, 2014, 10:35 »

Чтобы пользователь мог его взять и пользоваться к примеру Улыбающийся
Записан
8Observer8
Гость
« Ответ #53 : Февраль 22, 2014, 10:39 »

Old, смотрите что стало из-за Вас с моим "грандиозным" проектом! Shape теперь не нужен. От всего проекта осталось два малюсеньких файла (не считая демонстрацию в main.cpp). Весь мой полиморфный мир разрушен Грустный

Получается, что функции для расчёта периметра и площади плохие кандидаты для применения полиморфизма? Для чего класс Shape можно применить?

Output:
Цитировать
Circle:
Radius = 5.5
Perimeter = 34.5575
Area = 95.0332

Rectangle:
Height = 5
Width = 10
Perimeter = 30
Area = 50

main.cpp
Код
C++ (Qt)
#include <QCoreApplication>
#include <QTextStream>
#include "circle.h"
#include "rectangle.h"
 
QTextStream cin(stdin);
QTextStream cout(stdout);
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
 
   Circle *pc = new Circle;
   double radius = 5.5;
   cout << endl;
   cout << "Circle:" << endl;
   cout << "Radius = " << radius << endl;
   cout << "Perimeter = " << pc->perimeter(radius) << endl;
   cout << "Area = " << pc->area(radius) << endl;
   cout.flush();
   delete pc;
 
   Rectangle *pr = new Rectangle;
   double height = 5.0;
   double width = 10.0;
   cout << endl;
   cout << "Rectangle:" << endl;
   cout << "Height = " << height << endl;
   cout << "Width = " << width << endl;
   cout << "Perimeter = " << pr->perimeter(height, width) << endl;
   cout << "Area = " << pr->area(height, width) << endl;
   cout.flush();
   delete pr;
 
   return a.exec();
}
 

circle.h
Код
C++ (Qt)
#ifndef CIRCLE_H
#define CIRCLE_H
 
#include <cmath>
 
class Circle
{
public:
   inline double perimeter(double radius) const {
       return (2.0 * M_PI * radius);
   }
 
   inline double area(double radius) const {
       return (M_PI * radius * radius);
   }
};
 
#endif // CIRCLE_H
 

rectangle.h
Код
C++ (Qt)
#ifndef RECTANGLE_H
#define RECTANGLE_H
 
class Rectangle
{
public:
   inline double perimeter(double height, double width) const {
       return 2.0 * (height + width);
   }
 
   inline double area(double height, double width) const {
       return height * width;
   }
};
 
#endif // RECTANGLE_H
 
« Последнее редактирование: Февраль 22, 2014, 10:40 от 8Observer8 » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


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

объекты нужно передавать в методы через ссылку на константу, к примеру: void setName(const QString& name), тогда мы гарантируем, что не испортим объект, который нам доверили по ссылке.
Не только. Константная ссылка разрешает создание временного объекта, напр
Код
C++ (Qt)
setName("test");
Здесь будет создан временный (безымянный) объект QString, он будет подан в ф-цию и удален после того как ф-ция вернет управления. А если аргумент - не константная ссылка - так нельзя.

Конечно это "совсем просто", но вещь базовая и ее надо изучить очень тщательно
Записан
8Observer8
Гость
« Ответ #55 : Февраль 22, 2014, 11:15 »

Не только. Константная ссылка разрешает создание временного объекта, напр
Код
C++ (Qt)
setName("test");
Здесь будет создан временный (безымянный) объект QString, он будет подан в ф-цию и удален после того как ф-ция вернет управления. А если аргумент - не константная ссылка - так нельзя.
Конечно это "совсем просто", но вещь базовая и ее надо изучить очень тщательно

Спасибо! Попытаюсь понять на примере. Почему он мне выдаёт сообщение:

Цитировать
main.cpp:19: error: no matching function for call to 'Person::Person(const char [5])'
     Person ivan("Ivan");
                             ^

main.cpp
Код
C++ (Qt)
#include <QCoreApplication>
#include <QTextStream>
 
QTextStream cin(stdin);
QTextStream cout(stdout);
 
class Person {
public:
   void setName(const QString& name) {
       mName = name;
   }
 
   QString name() const {
       return mName;
   }
private:
   QString mName;
};
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
 
   Person ivan("Ivan");
 
   return a.exec();
}
 
« Последнее редактирование: Февраль 22, 2014, 11:18 от 8Observer8 » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


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

Old, смотрите что стало из-за Вас с моим "грандиозным" проектом! Shape теперь не нужен. От всего проекта осталось два малюсеньких файла (не считая демонстрацию в main.cpp). Весь мой полиморфный мир разрушен Грустный

Получается, что функции для расчёта периметра и площади плохие кандидаты для применения полиморфизма? Для чего класс Shape можно применить?
Напротив, они отличные кандидаты для полиморфизма.
Код
C++ (Qt)
class Circle
{
public:
   inline double perimeter(double radius) const {
       return (2.0 * M_PI * radius);
Такое вычисление имеет смысл, но оно никак не зависит от самого класса, его членов. Значит это static метод. Но так Вам надо иметь радиус "на стороне". Хорошо увязать это вместе, напр
Код
C++ (Qt)
class Circle : public Shape
{
public:
...
   static double perimeter(double radius)
  {
       return (2.0 * M_PI * radius);
  }
 
  virtual double perimeter( void ) const
  {
     return perimeter(radius);
  }
 
private:  
 double m_radius;  
 
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


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

Спасибо! Попытаюсь понять на примере. Почему он мне выдаёт сообщение:
А разве у Вас есть конструктор принимающий QString? И зачем метод name возвращает QString по значению? Для этого нет никаких оснований, правильно вернуть константную ссылку
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



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

Old, смотрите что стало из-за Вас с моим "грандиозным" проектом! Shape теперь не нужен. От всего проекта осталось два малюсеньких файла (не считая демонстрацию в main.cpp). Весь мой полиморфный мир разрушен Грустный
Почему? Полиморфизм можно оставить. Улыбающийся
Смотрите:
Код
C++ (Qt)
class Shape
{
public:
   virtual ~Shape() {}
 
   virtual double perimeter() const = 0;
   virtual double area() const = 0;
};
 

Код
C++ (Qt)
class Circle : public Shape
{
public:
   explicit Circle( double radius ) : m_radius( radius )
   {
   }
 
   double perimeter() const {
       return (2.0 * M_PI * m_radius);
   }
 
   double area() const {
       return (M_PI * m_radius * m_radius);
   }
 
private:
    double m_radius;
};
 

Тогда можно будет считать perimeter и area для любой фигуры, не задумываясь какая она конкретно.

« Последнее редактирование: Февраль 22, 2014, 11:54 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #59 : Февраль 22, 2014, 12:26 »

Общее замечание - излишнее упрощение. Не хочется возиться с полигоном - ведь это якобы "не имеет отношения к С++". Формально так, на деле - нет. Будет больше ф-ционала - будет и больше архитектуры, проблем что нужно решать. А месить простейшие круг и пр-к - да тут и классы-то (по большому счету) не нужны. И вообще, культурный человек должен уметь подсчитать площадь полигона  Улыбающийся   
Записан
Страниц: 1 2 3 [4] 5 6 ... 13   Вверх
  Печать  
 
Перейти в:  


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