Статические поля класса при наследовании
Есть абстрактный класс Figure и производные от него Triangle и Rectangle.
class Figure {
public:
Figure(std::vector<Point2D>& array) {
_array = new Point2D[vertices];
for (int i = 0; i < vertices; ++i) {
_array[i] = array[i];
}
}
...
protected:
static std::string name; // name - имя фигуры (треугольник, прямоугольник)
static int vertices; // количество вершин у фигуры
Point2D* _array; // массив точек, задающих фигуру
};
Мне бы хотелось использовать конструктор класса Figure и в дочерних классах, а также хотелось бы хранить информацию о классах в статических переменных. Так как очевидно, что количество вершин у всех прямоугольников равно четырем, а у треугольника это количество другое.
Однако конструктор всех фигур имеет одинаковый вид - по вектору точек заполняем внутренний массив. Как реализовать эту идею? Очевидно, сейчас код работать не будет, так как при вызове конструктора Figure он будет обращаться к своей статической переменной vertices, а не к количеству вершин, например, прямоугольника.
Ответы (2 шт):
Сюда напрашивается что-то вроде этого:
#include <algorithm>
#include <iostream>
#include <span>
struct Point2D {float x = 0, y = 0;};
class BasicShape
{
public:
virtual ~BasicShape() = default;
virtual std::span<Point2D> GetPoints() = 0;
std::span<const Point2D> GetPoints() const
{
return const_cast<BasicShape &>(*this).GetPoints();
}
// ...
};
template <std::size_t N>
class Shape : public BasicShape
{
Point2D points[N]{};
public:
Shape() {}
Shape(std::span<const Point2D, N> span)
{
std::ranges::copy(span, points);
}
std::span<Point2D> GetPoints() override {return points;}
using BasicShape::GetPoints;
};
class Triangle : public Shape<3>
{
public:
using Shape::Shape;
};
class Square : public Shape<4>
{
public:
using Shape::Shape;
};
int main()
{
// Подбором определил, сколько должно быть пар скобочек. :)
Triangle tri = {{{{1,2},{3,4},{5,6}}}};
for (Point2D point : tri.GetPoints())
std::cout << point.x << ' ' << point.y << '\n';
}
Массив на куче не храним, чтобы быстрее работало.
BasicShape и все виртуальные функции можно спокойно убрать, если полиморфизм не нужен, и сложить все общие методы в шаблон (в этой ситуации часто бывает нужен CRTP, но именно тут не нужен).
#include <algorithm>
#include <iostream>
#include <span>
struct Point2D {float x = 0, y = 0;};
template <std::size_t N>
class Shape
{
Point2D points[N]{};
public:
Shape() {}
Shape(std::span<const Point2D, N> span)
{
std::ranges::copy(span, points);
}
std::span<Point2D> GetPoints() {return points;}
std::span<const Point2D> GetPoints() const
{
return const_cast<Shape &>(*this).GetPoints();
}
};
class Triangle : public Shape<3>
{
public:
using Shape::Shape;
};
class Square : public Shape<4>
{
public:
using Shape::Shape;
};
int main()
{
// Подбором определил, сколько должно быть пар скобочек. :)
Triangle tri = {{{{1,2},{3,4},{5,6}}}};
for (Point2D point : tri.GetPoints())
std::cout << point.x << ' ' << point.y << '\n';
}
Хранение информации о классах в статических переменных - вполне здравая идея. Однако размещая статическое поле в базовом классе оно будет одно на все экземпляры этого класса; входящие и в объекты Triangle, и в объекты Rectangle. Вместо этого поле следует размещать в самих дочерних классах. Хотя сами поля статические, но доступ к ним из базового класса можно будет получить только посредством динамической диспетчеризации, через виртуальные методы, ведь в базовом классе заранее не известно, подобъектом объекта какого из дочерних классов тот является. Однако это влечет другую проблему - вызов виртуальных методов нельзя производить внутри конструкторов и деструкторов. А значит, что передавать в конструктор базового класса размер необходимо явно.
В итоге, после правок получается что-то подобное:
#include <memory>
#include <string_view>
#include <vector>
#include <cstddef>
using
Points = ::std::vector<Point2D>;
class Figure
{
private: Points m_points;
protected: explicit
Figure(Points const & points, ::std::size_t const points_count)
: m_points{points.data(), points.data() + points_count}
{}
public: virtual ::std::string_view
Fetch_Name(void) const noexcept = 0;
public: virtual ::std::size_t
Fetch_PointsCount(void) const noexcept = 0;
...
};
class Triangle final
: public Figure
{
private: static constexpr ::std::string_view const s_name{"Triangle"};
private: static constexpr ::std::size_t const s_points_count{3};
public: explicit
Triangle(Points const & points)
: Figure{points, s_points_count}
{}
public: ::std::string_view
Fetch_Name(void) const noexcept override
{ return s_name; }
public: ::std::size_t
Fetch_PointsCount(void) const noexcept override
{ return s_points_count; }
};
class Rectangle final
: public Figure
{
private: static constexpr ::std::string_view const s_name{"Rectangle"};
private: static constexpr ::std::size_t const s_points_count{4};
public: explicit
Rectangle(Points const & points)
: Figure{points, s_points_count}
{}
public: ::std::string_view
Fetch_Name(void) const noexcept override
{ return s_name; }
public: ::std::size_t
Fetch_PointsCount(void) const noexcept override
{ return s_points_count; }
};