#c #class
#c #класс
Вопрос:
Я работаю над API, который должен позволять вам рисовать простые геометрические фигуры и вычислять их элементы. Проект основан на библиотеке SFML.
У меня есть этот класс:
#ifndef POINT_H_INCLUDED
#define POINT_H_INCLUDED
#include "stdafx.h"
#include "Helper.h"
class Point : public AbstractShape
{
public:
Point(float x, float y);
Vector getCoords();
sf::VertexArray getShape();
void setLabel(std::string label, int param);
private:
float m_x, m_y, m_R;
std::string m_label;
sf::VertexArray m_shape;
sf::Text m_labelObject;
};
#endif
Он наследуется от абстрактного класса AbstractShape
, как и другие подобные классы Segment
и Triangle
. Мне это нужно, чтобы иметь возможность добавлять разные формы в один контейнер, чтобы потом удобно обрабатывать их в одном месте.
В main
функции я объявляю контейнер, затем создаю экземпляр Point
, а затем push_back
его в контейнер:
std::vector<AbstractShape*> shapes;
Point* p1 = new Point(100, 50);
p1->setLabel("A", 4);
shapes.push_back(p1);
Я думаю, было бы лучше, если бы экземпляр мог добавлять себя в контейнер при создании экземпляра. Для этого Point
класс должен иметь возможность видеть контейнер изнутри самого себя. Каков наилучший способ добиться этого, не вводя слишком много связей с Point
классом?
Ответ №1:
Чтобы добавить себя в контейнер, Point
он должен быть связан с контейнером. Объединение этих двух кажется плохой идеей: зачем Point
им что-то знать std::vector
?
Если вы часто используете этот шаблон в своем коде, лучше определить функцию для создания экземпляра и добавления точки в контейнер:
template<typename T=std::vector<Point*>>
Point *makePoint(int x, int y, T container) {
Point *p = new Point(x, y);
container.push_back(p);
return p; // so we can set other properties easily
}
Или создать другой Context
класс, который инкапсулирует набор точек:
template <typename T=std::vector<Point*>>
class Context {
T container;
public:
Point* addPoint(int x, int y) {
Point *p = new Point(x, y);
container.push_back(p);
return p;
}
};
Также вы можете захотеть использовать shared_ptr
или unique_ptr
избежать утечек памяти, хотя это может немного усложнить наследование.
Вот полностью WME на Ideone со 2-м вариантом:
#include <iostream>
#include <vector>
using namespace std;
class Point {
public:
Point (int x, int y) {}
};
template <typename T=std::vector<Point*>>
class Context {
T container;
public:
Point* addPoint(int x, int y) {
Point *p = new Point(x, y);
container.push_back(p);
return p;
}
};
int main() {
Context<> c;
c.addPoint(1, 2);
return 0;
}
Комментарии:
1. Я получаю «ошибка C2228: слева от ‘.push_back’ должен иметь class / struct / union» для строки
container.push_back(p)
. (Я использую вторую реализацию, которая включает вContext
себя класс).2. @Rokas oups, пожалуйста, посмотрите Мое обновление и MWE на Ideone; TLDR использовать
std::vector<Point*>
вместоstd::vector
Ответ №2:
Я думаю, было бы лучше, если бы экземпляр мог добавлять себя в контейнер при создании экземпляра.
Это ваше решение, но подумайте дважды — в большинстве ситуаций лучше сохранять объекты как можно более простыми. Если вам нужно просто упростить свой код, это можно сделать:
Вы можете создать внешнюю функцию построения, аналогичную std::make_share и std::make_tuple:
Это позволит вам вызывать:
construct<Point>(container, 1, 2);
construct<Line>(container, 1, 2, 3, 4);
И он построит Point / Line и поместит в контейнер в одной строке
Полный код:
#include <iostream>
#include <vector>
using namespace std;
struct AbstractShape
{
virtual std::ostreamamp; dump(std::ostreamamp;) = 0;
};
struct Point : AbstractShape
{
Point(float x, float y) : x(x), y(y) {}
virtual std::ostreamamp; dump(std::ostreamamp; o) override
{
return o << "P[" << x << ":" << y << "]";
}
float x, y;
};
struct Line : AbstractShape
{
Line(float x1, float y1, float x2, float y2) : x1(x1), y1(y1), x2(x2), y2(y2) {}
virtual std::ostreamamp; dump(std::ostreamamp; o) override
{
return o << "L[" << x1 << ":" << y1 << "," << x2 << ":" << y2<< "]";
}
float x1, y1, x2, y2;
};
template<typename Object, typename Container, typename ...Args>
Object* construct(Containeramp; c, Args... args)
{
Object* res = new Object(args...);
c.push_back(res);
return res;
}
int main() {
std::vector<AbstractShape*> container;
construct<Point>(container, 1, 2);
construct<Line>(container, 1, 2, 3, 4);
for (auto s : container)
s->dump(std::cout) << std::endl;
return 0;
}
Вывод:
P[1:2]
L[1:2,3:4]
И я определенно рекомендую использовать std::unique_ptr вместо необработанных указателей