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

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

Страниц: 1 2 [3] 4 5 ... 13   Вниз
  Печать  
Автор Тема: Как писать ООП программы?  (Прочитано 86607 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


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

Ну может и являться и тогда 1 аргумент. Но это обычно нежелательно как говорили выше. А "friend" автоматически означает НЕ член. Главного человек не понял

Код
C++ (Qt)
friend QTextStream& operator<<(QTextStream& stream, const Rational& r);
Внимание: теперь класс Rational уже не может быть использован без Qt.
Записан
Johnik
Крякер
****
Offline Offline

Сообщений: 339


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

Внимание: теперь класс Rational уже не может быть использован без Qt.
Код
C++ (Qt)
#ifdef QT_VERSION
...
#endif
Записан
8Observer8
Гость
« Ответ #32 : Февраль 20, 2014, 14:54 »

Спасибо большое! Кстати, если написать объявление в классе friend-функции и потом добавить реализацию таким образом: правой кнопкой мыши по функции -> "Refactor" -> "Add difinition in relation.cpp", то добавится такое определение: "QTextStream &Rational::operator<<" вместо "QTextStream &operator<<"

Я ещё понял такую вещь: что если операторная функция - это член класса, то она будет принимать только один аргумент (второй - через this), а если friend или свободная, то два параметра.

Помогите разобраться со следующими ошибками:

Цитировать
D:\Documents\Qt\QtOOP\0004_Shapes\Shapes\main.cpp:19: error: cannot allocate an object of abstract type 'Circle'
     Shape *pc = new Circle();
                                      ^
D:\Documents\Qt\QtOOP\0004_Shapes\Shapes\main.cpp:22: error: no matching function for call to 'Shape::calcPerimeter(double)'
     pc->calcPerimeter(2.5);
                                    ^

main.cpp
Код
C++ (Qt)
#include <QCoreApplication>
#include <QTextStream>
#include "shape.h"
#include "circle.h"
#include "rectangle.h"
 
QTextStream cin(stdin);
QTextStream cout(stdout);
 
void printShapeInfo(Shape *ps) {
   cout << "p = " << ps->perimeter() << endl;
   cout << "s = " << ps->area() << endl;
   cout.flush();
}
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
 
   Shape *pc = new Circle();
   printShapeInfo(pc);
 
   pc->calcPerimeter(2.5);
 
   return a.exec();
}
 

shape.h
Код
C++ (Qt)
#ifndef SHAPE_H
#define SHAPE_H
 
class Shape
{
public:
   Shape();
   virtual ~Shape();
 
   virtual void calcPerimeter() = 0;
   virtual void calcArea() = 0;
 
   double perimeter();
   double area();
 
protected:
   double mPerimeter;
   double mArea;
};
 
#endif // SHAPE_H
 

shape.cpp
Код
C++ (Qt)
#include "shape.h"
 
Shape::Shape() :
   mPerimeter(0.0),
   mArea(0.0)
{
}
 
Shape::~Shape()
{
}
 
double Shape::perimeter()
{
   return mPerimeter;
}
 
double Shape::area()
{
   return mArea;
}
 

circle.h
Код
C++ (Qt)
#ifndef CIRCLE_H
#define CIRCLE_H
 
#include "shape.h"
 
class Circle : public Shape
{
public:
   Circle();
   virtual double calcPerimeter(double radius);
   virtual double calcArea(double radius);
};
 
#endif // CIRCLE_H
 

circle.cpp
Код
C++ (Qt)
#include "circle.h"
#include <cmath>
 
Circle::Circle()
{
}
 
double Circle::calcPerimeter(double radius)
{
   mPerimeter = 2.0 * M_PI * radius;
   return mPerimeter;
}
 
double Circle::calcArea(double radius)
{
   mArea = M_PI * radius * radius;
   return mArea;
}
 

rectangle.h
Код
C++ (Qt)
#ifndef RECTANGLE_H
#define RECTANGLE_H
 
#include "shape.h"
 
class Rectangle : public Shape
{
public:
   Rectangle();
   virtual double calcPerimeter(double a, double b);
   virtual double calcArea(double a, double b);
};
 
#endif // RECTANGLE_H
 

rectangle.cpp
Код
C++ (Qt)
#include "rectangle.h"
 
Rectangle::Rectangle()
{
}
 
double Rectangle::calcPerimeter(double a, double b)
{
   mPerimeter = 2.0 * (a + b);
   return mPerimeter;
}
 
double Rectangle::calcArea(double a, double b)
{
   mArea = a * b;
   return mArea;
}
 
« Последнее редактирование: Февраль 20, 2014, 14:55 от 8Observer8 » Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



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

Помогите разобраться со следующими ошибками:

Цитировать
D:\Documents\Qt\QtOOP\0004_Shapes\Shapes\main.cpp:19: error: cannot allocate an object of abstract type 'Circle'
     Shape *pc = new Circle();
                                      ^
D:\Documents\Qt\QtOOP\0004_Shapes\Shapes\main.cpp:22: error: no matching function for call to 'Shape::calcPerimeter(double)'
     pc->calcPerimeter(2.5);
                                    ^

Все описано в сообщениях.
Класс Circle остался виртуальным, поэтому объект этого класса нельзя создать. Что бы он перестал быть виртуальным, нужно в наследуемом классе переопределить calcPerimeter и calcArea так как они определены в базовом классе.
Вторая ошибка как раз из-за того, что в базовом классе методы определены без параметров, а в наследниках с параметрами.
Записан
8Observer8
Гость
« Ответ #34 : Февраль 20, 2014, 17:50 »

Внимание: теперь класс Rational уже не может быть использован без Qt.

Я кажется понял, что вот так надо:

rational.h
Код
C++ (Qt)
   template <typename stream_type>
   friend stream_type& operator<<(stream_type& stream, const Rational& r);
 

rational.cpp
Код
C++ (Qt)
template <typename stream_type>
stream_type &operator<<(stream_type &stream, const Rational &r)
{
   stream << r.mNumerator << "/" << r.mDenominator;
   return stream;
}
 

Подскажите, пожалуйста, как в main.cpp написать, чтобы работало:

Код
C++ (Qt)
#include <QCoreApplication>
#include <QTextStream>
#include "rational.h"
 
QTextStream cin(stdin);
QTextStream cout(stdout);
 
int main(int argc, char *argv[])
{
   QCoreApplication app(argc, argv);
 
   Rational a(2, 3);
   cout << "a = " << a << endl;
   cout.flush();
 
   return app.exec();
}
 
Записан
8Observer8
Гость
« Ответ #35 : Февраль 20, 2014, 17:57 »

Класс Circle остался виртуальным, поэтому объект этого класса нельзя создать. Что бы он перестал быть виртуальным, нужно в наследуемом классе переопределить calcPerimeter и calcArea так как они определены в базовом классе.
Вторая ошибка как раз из-за того, что в базовом классе методы определены без параметров, а в наследниках с параметрами.

У меня для Circle один параметр, а для Rectangle - два. Как быть?
Записан
Johnik
Крякер
****
Offline Offline

Сообщений: 339


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

У меня для Circle один параметр, а для Rectangle - два. Как быть?
Параметры объекта передавать в конструкторе.
Записан
8Observer8
Гость
« Ответ #37 : Февраль 21, 2014, 08:38 »

Параметры объекта передавать в конструкторе.

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

Код
C++ (Qt)
#ifndef SHAPE_H
#define SHAPE_H
 
class Shape
{
   // ...
   virtual void calcPerimeter() = 0;
   virtual void calcArea() = 0;
   // ...
};
 

А пользователю предоставить возможность создавать свои классы (унаследованные от моего) с требуемым ему набором параметром для calcPerimeter() и calcArea()

circle.h
Код
C++ (Qt)
#ifndef CIRCLE_H
#define CIRCLE_H
 
#include "shape.h"
 
class Circle : public Shape
{
public:
   Circle();
   double calcPerimeter(double radius);
   double calcArea(double radius);
};
 
#endif // CIRCLE_H
 

rectangle.h
Код
C++ (Qt)
#ifndef RECTANGLE_H
#define RECTANGLE_H
 
#include "shape.h"
 
class Rectangle : public Shape
{
public:
   Rectangle();
   double calcPerimeter(double a, double b);
   double calcArea(double a, double b);
};
 
#endif // RECTANGLE_H
 
« Последнее редактирование: Февраль 21, 2014, 08:40 от 8Observer8 » Записан
Johnik
Крякер
****
Offline Offline

Сообщений: 339


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

что мешает сделать так:
Код
C++ (Qt)
#ifndef CIRCLE_H
#define CIRCLE_H
 
#include "shape.h"
 
class Circle : public Shape
{
public:
   Circle(double radius);
   double calcPerimeter();
   double calcArea();
};
 
#endif // CIRCLE_H
 
Код
C++ (Qt)
#ifndef RECTANGLE_H
#define RECTANGLE_H
 
#include "shape.h"
 
class Rectangle : public Shape
{
public:
   Rectangle(double a, double b);
   double calcPerimeter();
   double calcArea();
};
 
#endif // RECTANGLE_H
 

Записан
8Observer8
Гость
« Ответ #39 : Февраль 21, 2014, 10:21 »

Спасибо! Я понял Вашу идею. А моя, похоже, в данном случае, неприменима.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


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

rational.cpp
Код
C++ (Qt)
template <typename stream_type>
stream_type &operator<<(stream_type &stream, const Rational &r)
{
   stream << r.mNumerator << "/" << r.mDenominator;
   return stream;
}
 
Когда этот оператор будет использован из др cpp (не rational.cpp)- получите ошибку линковки, т.к. template должен "находиться в той же единице трансляции", перенесите тело в конец rational.h
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


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

Код
C++ (Qt)
class Rectangle : public Shape
{
public:
   Rectangle();
   double calcPerimeter(double a, double b);
   double calcArea(double a, double b);
};
 
Здесь Вы упустили мааааленькую деталь, но без нее - говнокод, с ней - нормально. Какая это деталь?

По поводу аргументов a, b (не имеют отношения к детали выше). Класс знает все для вычисления периметра и площади, поэтому в любом случае должны быть эти методы без всяких аргументов. Возможно Вы хотели иметь возможность вычислять их без создания класса - тогда так
Код
C++ (Qt)
public:
   Rectangle();
   static double calcPerimeter(double a, double b);
   static double calcArea(double a, double b);
 
Это может быть полезным (хоть и не полиморфным)
Записан
8Observer8
Гость
« Ответ #42 : Февраль 21, 2014, 15:09 »

Когда этот оператор будет использован из др cpp (не rational.cpp)- получите ошибку линковки, т.к. template должен "находиться в той же единице трансляции", перенесите тело в конец rational.h

Большое спасибо! Теперь работает! Получается, что все методы с template должны быть в заголовочном файле. Я про это когда-то читал, но забыл, потому что на практике ни разу не попробовал.

main.cpp
Код
C++ (Qt)
#include <QCoreApplication>
#include <QTextStream>
#include "rational.h"
 
QTextStream cin(stdin);
QTextStream cout(stdout);
 
int main(int argc, char *argv[])
{
   QCoreApplication app(argc, argv);
 
   Rational a(2, 3); // 2/3
   cout << "a = " << a << endl;
   cout.flush();
 
   return app.exec();
}
 

rational.h
Код
C++ (Qt)
#ifndef RATIONAL_H
#define RATIONAL_H
 
namespace RationalStuff {
class Rational;
}
 
class Rational
{
public:
   Rational(int numerator = 0, int denominator = 1);
 
   int numerator() const;
   void setNumerator(int numerator);
 
   int denominator() const;
   void setDenominator(int denominator);
 
   friend const Rational operator*(const Rational& lhs, const Rational& rhs);
   friend const Rational operator+(const Rational& lhs, const Rational& rhs);
   friend const Rational operator-(const Rational& lhs, const Rational& rhs);
   template <typename stream_type>
   friend stream_type& operator<<(stream_type& stream, const Rational& r);
 
private:
   int mNumerator;
   int mDenominator;
};
 
template <typename stream_type>
stream_type &operator<<(stream_type &stream, const Rational &r)
{
   stream << r.mNumerator << "/" << r.mDenominator;
   return stream;
}
 
#endif // RATIONAL_H
 

rational.cpp
Код
C++ (Qt)
#include "rational.h"
 
Rational::Rational(int numerator, int denominator) :
   mNumerator(numerator),
   mDenominator(denominator)
{
}
 
int Rational::numerator() const
{
   return mNumerator;
}
 
void Rational::setNumerator(int numerator)
{
   mNumerator = numerator;
}
 
int Rational::denominator() const
{
   return mDenominator;
}
 
void Rational::setDenominator(int denominator)
{
   mDenominator = denominator;
}
 
const Rational operator*(const Rational &lhs, const Rational &rhs)
{
   return Rational(lhs.mNumerator * rhs.mNumerator,
                   lhs.mDenominator * rhs.mDenominator);
}
 
const Rational operator+(const Rational &lhs, const Rational &rhs)
{
   Rational result;
 
   // Общий знаменатель
   int commonDenominator = lhs.denominator() * rhs.denominator();
 
   // Числитель
   int a = lhs.numerator() * rhs.denominator();
   int b = rhs.numerator() * lhs.denominator();
   result.setNumerator(a + b);
 
   // Знаменатель
   result.setDenominator(commonDenominator);
 
   return result;
}
 
const Rational operator-(const Rational &lhs, const Rational &rhs)
{
   Rational result;
 
   // Общий знаменатель
   int commonDenominator = lhs.denominator() * rhs.denominator();
 
   // Числитель
   int a = lhs.numerator() * rhs.denominator();
   int b = rhs.numerator() * lhs.denominator();
   result.setNumerator(a - b);
 
   // Знаменатель
   result.setDenominator(commonDenominator);
 
   return result;
}
 
Записан
8Observer8
Гость
« Ответ #43 : Февраль 21, 2014, 15:24 »

Здесь Вы упустили мааааленькую деталь, но без нее - говнокод, с ней - нормально. Какая это деталь?

По поводу детали пока не могу ответить. Думаю.

Возможно Вы хотели иметь возможность вычислять их без создания класса

Спасибо! Как вариант. Да, но тогда выпадает необходимость в базовом абстрактном классе. Хотелось бы, чтобы абстрактный класс был более полезен и указывал разработчикам производных классов, что нужно переопределить функции для рассчёта периметра и площади.

По поводу аргументов a, b (не имеют отношения к детали выше). Класс знает все для вычисления периметра и площади, поэтому в любом случае должны быть эти методы без всяких аргументов.

Большое спасибо! Я теперь так и сделал. Посмотрите, пожалуйста, в моём коде сейчас есть ли какие-то грубые недочёты:

main.cpp
Код
C++ (Qt)
#include <QCoreApplication>
#include <QTextStream>
#include "shape.h"
#include "circle.h"
#include "rectangle.h"
 
QTextStream cin(stdin);
QTextStream cout(stdout);
 
void printShapeInfo(Shape *ps) {
   cout << "p = " << ps->perimeter() << endl;
   cout.flush();
   cout << "s = " << ps->area() << endl;
   cout.flush();
}
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
 
   Shape *pc = new Circle(3.0);
   pc->calcPerimeter();
   pc->calcArea();
   printShapeInfo(pc);
   delete pc;
 
   Shape *pr = new Rectangle(5, 10);
   pr->calcPerimeter();
   pr->calcArea();
   printShapeInfo(pr);
   delete pr;
 
   return a.exec();
}
 

shape.h
Код
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
 

shape.cpp
Код
C++ (Qt)
#include "shape.h"
 
Shape::Shape() :
   mPerimeter(0.0),
   mArea(0.0)
{
}
 
Shape::~Shape()
{
}
 
double Shape::perimeter() const
{
   return mPerimeter;
}
 
double Shape::area() const
{
   return mArea;
}
 

circle.h
Код
C++ (Qt)
#ifndef CIRCLE_H
#define CIRCLE_H
 
#include "shape.h"
 
class Circle : public Shape
{
public:
   Circle(double r = 0.0);
 
   /*virtual*/ double calcPerimeter();
   /*virtual*/ double calcArea();
 
   void setRadius(double r);
   double radius() const;
 
private:
   double mRadius;
};
 
#endif // CIRCLE_H
 

circle.cpp
Код
C++ (Qt)
#include "circle.h"
#include <cmath>
 
Circle::Circle(double r) :
   mRadius(r)
{
}
 
/*virtual*/ double Circle::calcPerimeter()
{
   mPerimeter = 2.0 * M_PI * mRadius;
   return mPerimeter;
}
 
/*virtual*/ double Circle::calcArea()
{
   mArea = M_PI * mRadius * mRadius;
   return mArea;
}
 
void Circle::setRadius(double r)
{
   mRadius = r;
}
 
double Circle::radius() const
{
   return mRadius;
}
 

rectangle.h
Код
C++ (Qt)
#ifndef RECTANGLE_H
#define RECTANGLE_H
 
#include "shape.h"
 
class Rectangle : public Shape
{
public:
   Rectangle(double l = 0.0, double w = 0.0);
 
   /*virtual*/ double calcPerimeter();
   /*virtual*/ double calcArea();
 
   void setLength(double l);
   double length() const;
 
   void setWidth(double w);
   double width() const;
 
private:
   double mLength;
   double mWidth;
};
 
#endif // RECTANGLE_H
 

rectangle.cpp
Код
C++ (Qt)
#include "rectangle.h"
 
Rectangle::Rectangle(double l, double w) :
   mLength(l),
   mWidth(w)
{
}
 
/*virtual*/ double Rectangle::calcPerimeter()
{
   mPerimeter = 2.0 * (mLength + mWidth);
   return mPerimeter;
}
 
/*virtual*/ double Rectangle::calcArea()
{
   mArea = mLength * mWidth;
   return mArea;
}
 
void Rectangle::setLength(double l)
{
   mLength = l;
}
 
double Rectangle::length() const
{
   return mLength;
}
 
void Rectangle::setWidth(double w)
{
   mWidth = w;
}
 
double Rectangle::width() const
{
   return mWidth;
}
 

P.S. Узнал, что если у базового класса невиртуальный деструктор, то нельзя будет уничтожить полностью объект производного класса через указатель на базовый (как у меня это сделано в main.cpp), так как не будет вызван деструктор производного класса, таким образом будет утечка памяти. Вот здесь прочитал: "Правило 7: Объявляйте деструкторы виртуальными в полиморфном базовом классе" http://www.e-reading.co.uk/chapter.php/1002058/26/Mayers_-_Effektivnoe_ispolzovanie_CPP.html
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


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

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

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

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

Да, и для пр-ка традиционно "Height" (а не "Length")
Записан
Страниц: 1 2 [3] 4 5 ... 13   Вверх
  Печать  
 
Перейти в:  


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