С++ перегруженные операции с абстрактным базовым классом
Описание кода
Итак, есть набор классов для работы с фигурами.
class Figure;
class Circle;
class Rectangle;
class UnitedFigure;
class ComplementedFigure;
class IntersectedFigure;
Класс Figure - базовый и абстрактный. И выглядит так:
class Figure {
public:
virtual double distance_to (const Point &p) const = 0;
friend UnitedFigure operator + (Figure & f1, Figure & f2);
friend ComplementedFigure operator - (Figure & f1, Figure & f2);
friend IntersectedFigure operator & (Figure & f1, Figure & f2);
bool is_point_into(const Point &p) const {
return distance_to(p) <= 0;
}
};
Все остальные фигуры публично наследуют Figure, имеют разные конструкторы и перегруженный метод distance_to().
class Circle : public Figure {...};
class Rectangle : public Figure {...};
class UnitedFigure : public Figure {...};
class ComplementedFigure : public Figure {...};
class IntersectedFigure : public Figure {...};
UnitedFigure operator + (Figure & f1, Figure & f2) {
return UnitedFigure{f1, f2};
}
ComplementedFigure operator - (Figure & f1, Figure & f2) {
return ComplementedFigure{f1, f2};
}
IntersectedFigure operator & (Figure & f1, Figure & f2) {
return IntersectedFigure{f1, f2};
}
Предмет вопроса
Эти описания позволяют мне задавать фигуры вот таким образом:
// Создаём нужные примитивы
Circle big(Point{0, 0}, 20);
Circle small(Point{0, 0}, 10);
Rectangle quarter_1(Point{0, 0}, Point{10, 10});
Rectangle quarter_3(Point{0, 0}, Point{-10, -10});
// Собираем фигуру
auto quarter_1_and_3 = quarter_1 + quarter_3;
auto inner_empty_space = small - quarter_1_and_3;
auto figure = big - inner_empty_space;
Однако если я попытаюсь описать фигуру без лишних переменных одним выражением, то получу ошибку, так как операции ожидают в качестве операндов ссылки на базовый класс, а не объекты-значения конкретных классов.
// Собираем фигуру
auto figure = big - (small - (quarter_1 + quarter_3)); // Ошибка операндов
Предмет конфликта: абстрактный класс + ссылки VS работа с операндами по значению.
Как модифицировать классы так, чтобы можно было описывать одним выражением, как выше?
UPD1: МВП
#include <iostream>
#include <cmath>
class Point;
class Figure;
class Circle;
class Rectangle;
class UnitedFigure;
class ComplementedFigure;
class IntersectedFigure;
class Point {
public:
double x;
double y;
Point() = default;
Point(double _x, double _y) : x(_x), y(_y) {}
Point operator + (const Point& p) const {
return Point {x + p.x, y + p.y};
}
Point operator - (const Point& p) const {
return Point {x - p.x, y - p.y};
}
static Point max (const Point& p1, const Point& p2) {
return Point {std::max(p1.x, p2.x), std::max(p1.y, p2.y)};
}
double vec_length () const {
return sqrt(x*x + y*y);
}
};
class Figure {
public:
virtual double distance_to (const Point &p) const = 0;
friend UnitedFigure operator + (Figure & f1, Figure & f2);
friend ComplementedFigure operator - (Figure & f1, Figure & f2);
friend IntersectedFigure operator & (Figure & f1, Figure & f2);
bool is_point_into(const Point &p) const {
return distance_to(p) <= 0;
}
};
class Circle : public Figure {
Point o;
double r;
public:
Circle (Point p, double _r) : o(p), r(_r) {}
double distance_to (const Point &p) const override {
return (o - p).vec_length() - r;
}
};
class Rectangle : public Figure {
Point a;
Point b;
public:
Rectangle (Point p1, Point p2) : a(p1), b(p2) {}
double distance_to (const Point &p) const override {
auto d = Point::max(a - p, p - b);
return Point::max(d, Point {0, 0}).vec_length() + std::min(0.0, std::max(d.x, d.y));
}
};
class UnitedFigure : public Figure {
Figure &f1;
Figure &f2;
public:
UnitedFigure (Figure &_f1, Figure &_f2) : f1(_f1), f2(_f2) {}
double distance_to(const Point &p) const override {
return std::min(f1.distance_to(p), f2.distance_to(p));
}
};
class ComplementedFigure : public Figure {
Figure &f1;
Figure &f2;
public:
ComplementedFigure (Figure &_f1, Figure &_f2) : f1(_f1), f2(_f2) {}
double distance_to(const Point &p) const override {
return std::max(f1.distance_to(p), -f2.distance_to(p));
}
};
class IntersectedFigure : public Figure {
Figure &f1;
Figure &f2;
public:
IntersectedFigure (Figure &_f1, Figure &_f2) : f1(_f1), f2(_f2) {}
double distance_to(const Point &p) const override {
return std::max(f1.distance_to(p), f2.distance_to(p));
}
};
UnitedFigure operator + (Figure & f1, Figure & f2) {
return UnitedFigure{f1, f2};
}
ComplementedFigure operator - (Figure & f1, Figure & f2) {
return ComplementedFigure{f1, f2};
}
IntersectedFigure operator & (Figure & f1, Figure & f2) {
return IntersectedFigure{f1, f2};
}
int main() {
Circle big(Point{0, 0}, 20);
Circle small(Point{0, 0}, 10);
Rectangle quarter_1(Point{0, 0}, Point{10, 10});
Rectangle quarter_3(Point{0, 0}, Point{-10, -10});
auto quarter_1_and_3 = quarter_1 + quarter_3;
auto inner_empty_space = small - quarter_1_and_3;
auto figure = big - inner_empty_space;
// auto figure = big - (small - (quarter_1 + quarter_3));
return 0;
}
Ответы (1 шт):
До этого вопроса я думал, что поле класса как ссылочный тип заводить нельзя.
Компилятор ругается на то, что вы передаете rvalue в функцию как non-const ссылку.
Время жизни объекта полученного при помощи (quarter_1 + quarter_3) не выходит за пределы выражения во всей строке.
Вы передаете полученный объект в operator -, который в свою очередь передает его в конструктор ComplementedFigure, который присвоит ссылку в f2 на этот временный объект, так делать не нужно.
В итоге у вас будет недействительная ссылка в поле ComplementedFigure::f2 после перехода к следующему выражению после строки auto figure = big - (small - (quarter_1 + quarter_3));.
Пользуйтесь ссылочными полями класса с осторожностью, а лучше вообще не пользуйтесь.
Рекомендую почитать в чем опасность использования ссылки\указателя на объект из другой области видимости и как это обойти (при помощи умных указателей и RAII)