Я хотел бы удостовериться, что пишу все правильно и адекватно. Подскажите пожалуйста какие ошибки присутствуют в коде и желательно советы, как сделать правильно.
#include <iostream>
using namespace std;
class Point {
private:
int xPos, yPos;
public:
Point(int x, int y) : xPos(x), yPos(y) { }
Point() : Point(0,0) { }
int x() { return xPos; }
int y() { return yPos; }
void setX(int x) { this->xPos = x; }
void setY(int y) { this->yPos = y; }
};
class Square {
private:
void(*callBackFunc)(Point point);
Point posLeftUp;
public:
Square(){}
Square(Point pos) : posLeftUp(pos) {}
//Имитация движения квадрата по окну
void Move(char key) {
if (key == 'A')
posLeftUp.setX(posLeftUp.x() - 2);
else if (key == 'D')
posLeftUp.setX(posLeftUp.x() + 2);
else if (key == 'W')
posLeftUp.setY(posLeftUp.y() + 2);
else if (key == 'S')
posLeftUp.setY(posLeftUp.y() - 2);
callBackFunc(posLeftUp);
}
void setCallbackFunc(void(*fn)(Point point)) {
callBackFunc = fn;
}
};
//Имитация окна
class MainWindow {
private:
static void getPosition(Point point);
Square square;
public:
MainWindow() {
Point pos(10, 10);
square = Square(pos);
square.setCallbackFunc(getPosition);
square.Move('A');
square.Move('D');
square.Move('W');
square.Move('S');
}
};
void MainWindow::getPosition(Point point){
cout << point.x() << " : " << point.y() << endl;
}
int main() {
MainWindow test;
return 0;
}
Вот некоторые моменты, о которых я могу высказаться.
В определении класса я бы сначала указал public
, так как это является интерфейсом класса.
class Point {
public:
Point(int x, int y) : xPos(x), yPos(y) { }
Point() : Point(0,0) { }
int x() { return xPos; }
int y() { return yPos; }
void setX(int x) { xPos = x; }
void setY(int y) { yPos = y; }
private:
int xPos;
int yPos;
};
Не объединяйте декларации в одну строку:
// плохо:
// int xPos, yPos;
// хорошо:
int xPos;
int yPos;
В setX
и setY
не используйте this
:
void setX(int x) { xPos = x; }
void setY(int y) { yPos = y; }
Используйте switch
в Move()
. Он сюда очень хорошо вписывается.
void Move(char key) {
switch(key)
{
case 'A': posLeftUp.setX(posLeftUp.x() - 2); break;
case 'D': posLeftUp.setX(posLeftUp.x() + 2); break;
case 'W': posLeftUp.setY(posLeftUp.y() + 2); break;
case 'S': posLeftUp.setY(posLeftUp.y() - 2); break;
}
callBackFunc(posLeftUp);
}
Используйте const
переменные, где они не должны меняться. Компилятор поможет вам избежать глупых ошибок:
void Move(const char key) {
В main()
можно убрать return 0;
. Когда main()
достигнет конца, и return
при этом отсутствует, то return 0;
будет подразумеваться автоматически.
Под "Отделять объявления от определений в классе", о чём упомянул @Andrey, имеется ввиду создание отдельных файлов для объявления класса и для определения его методов. Взять за пример ваш class Square
:
Создадим два файла: square.hpp
и square.cpp
.
square.hpp
:
#ifndef SQUARE_HPP
#define SQUARE_HPP
#include <functional>
#include "Point.hpp" // Файл, содержащий объявление класса Point
class Square {
public:
Square();
Square(const Point pos);
void Move(const char key);
void setCallbackFunc(std::function<void(const Point p)>);
private:
std::function<void(const Point p)> callBackFunc;
Point posLeftUp;
};
#endif //SQUARE_HPP
В этом файле мы объявили класс Square
, то есть перечислили его методы и члены. Этого достаточно для того, чтобы знать как использовать этот класс где-либо ещё в другом месте.
Вот эта конструкция называется Header guard ("страж заголовка" - так это будет по-русски?)
#ifndef SQUARE_HPP
#define SQUARE_HPP
// ...
#endif //SQUARE_HPP
Она предотвращает многократное вложение этого hpp
файла в один и тот же cpp
файл при компиляции.
Детали же того, как реализованы данные методы поместим во второй файл
square.cpp
:
#include "square.hpp"
Square::Square() : callBackFunc(nullptr)
{}
Square::Square(const Point pos) :
callBackFunc(nullptr),
posLeftUp(pos)
{}
void Square::Move(const char key)
{
switch(key)
{
case 'A': posLeftUp.setX(posLeftUp.x() - 2); break;
case 'D': posLeftUp.setX(posLeftUp.x() + 2); break;
case 'W': posLeftUp.setY(posLeftUp.y() + 2); break;
case 'S': posLeftUp.setY(posLeftUp.y() - 2); break;
}
if(callBackFunc)
{
callBackFunc(posLeftUp);
}
}
void Square::setCallbackFunc(std::function<void(const Point p)> fn)
{
callBackFunc = fn;
}
Таким образом мы отделяем интерфейс класса от реализации его методов.
Кстати, класс Point
на самом деле мог бы быть просто структурой, так как его приватные члены xPos
и yPos
полностью доступны для чтения и записи через методы, которые вы определили. setX()
и setY()
не выполняют никакой дополнительной работы, то есть, здесь нет никакой выгоды в том, чтобы прятать x
и y
как приватные члены:
struct Point{
Point() : x(0), y(0) {};
Point(const int x, const int y) : x(x), y(y) {};
int x;
int y;
};
Другое дело, если бы мы обязали setX()
и setY()
выполнять проверку переданных значений, то прятать x
и y
как приватные было бы оправданно:
void Point::setX(const int val)
{
constexpr int min_value = -100;
constexpr int max_value = 100;
if (val < min_value || val > max_value)
{
// Ошибка. Переданное значение неприемлемо.
throw std::runtime_error("setX(): значение вне диапазона");
}
x = val;
}
По поводу calback. Используйте инструменты из заголовка <functional>
:
std::function<>
- класс оболочка, который может держать в себе функцию.
std::bind()
- функция, привязывающая выполнение какой-либо функции к другой функции, с передачей ей аргументов, которые могут быть предопределены.
#include <functional>
#include <iostream>
using namespace std;
class Point {
public:
Point(int x, int y) : xPos(x), yPos(y) { }
Point() : Point(0,0) { }
int x() { return xPos; }
int y() { return yPos; }
void setX(int x) { xPos = x; }
void setY(int y) { yPos = y; }
private:
int xPos;
int yPos;
};
class Square {
public:
Square(){}
Square(Point pos) : posLeftUp(pos) {}
//Имитация движения квадрата по окну
void Move(const char key) {
switch(key)
{
case 'A': posLeftUp.setX(posLeftUp.x() - 2); break;
case 'D': posLeftUp.setX(posLeftUp.x() + 2); break;
case 'W': posLeftUp.setY(posLeftUp.y() + 2); break;
case 'S': posLeftUp.setY(posLeftUp.y() - 2); break;
}
if(callBackFunc)
{
callBackFunc(posLeftUp);
}
}
void setCallbackFunc(std::function<void(const Point p)> fn) {
callBackFunc = fn;
}
private:
std::function<void(const Point p)> callBackFunc = nullptr;
Point posLeftUp;
};
//Имитация окна
class MainWindow {
private:
static void getPosition(Point point);
Square square;
public:
MainWindow() {
using namespace std::placeholders;
Point pos(10, 10);
square = Square(pos);
square.setCallbackFunc(std::bind(&MainWindow::getPosition, _1));
square.Move('A');
square.Move('D');
square.Move('W');
square.Move('S');
}
};
void MainWindow::getPosition(Point point){
cout << point.x() << " : " << point.y() << endl;
}
int main() {
MainWindow test;
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Написал программку для расставления букв в алфавитном порядке в любом словеПоставил setlocale(0, "rus")
Я создал три класса, в каждом по приватному атрибуту, и с помощью дружественной функции использовал их значенияНо функция дружественна лишь...