Как я могу перебрать вектор объектов базового класса?

0

У меня есть проблема, когда нам нужно иметь несколько форм, таких как круг и квадрат, которые могут быть расположены на плоской двумерной плоскости. Все формы, такие как Circle и Square, наследуются от базового класса abstact Shape; поэтому у меня есть вектор указателей на фигуры.

Тем не менее, мне нужно иметь возможность итерации по плоскости и найти любые фигуры, которые сталкиваются, так что они пересекаются или касаются друг друга. Если я получаю форму от вектора, я не знаю, является ли это квадратом или кругом, потому что он был нарезан базовому классу Shape.

Как мне лучше всего решить эту проблему?

#ifndef Plane_h
#define Plane_h

#include <vector>
#include "Shape.h"

class Plane {
public:
    Plane(std::vector<Shape*>);
    Plane(const Plane&);
    ~Plane();

    void add(Shape*);
    std::vector<Shape*> all() const;

protected:
    std::vector<Shape*> shapes;
};

#endif
  • 0
    В этом коде нет причин для нарезки фигур. Не могли бы вы показать код, где вы добавляете фигуры в вектор.
  • 0
    Я полагаю, возможно, вы не понимаете, что означает «нарезанный». Не могли бы вы показать код, где, по вашему мнению, у вас есть проблема?
Показать ещё 2 комментария
Теги:
inheritance
stl

3 ответа

1
Лучший ответ

Вы должны использовать полиморфизм. Добавьте виртуальный метод в класс Shape:

class Shape {
  ...
  virtual bool intersects(Shape const* otherShape);
  ...
}

Затем вы реализуете его для каждой другой формы. Тогда, если он используется как:

Shape* A = getShapeA();
Shape* B = getShapeB();
if (A->intersects(B))
  doSomething();

Вызывается правильная версия, т.е. Если A является Circle, вызывается Circle Circle::intersects. Но там вы все еще не знаете, какая форма B самом деле. Вы можете найти это, пытаясь сделать динамический бросок:

Circle* circle = dynamic_cast<Circle*>(otherShape);
if (circle)
  intersectsCircle(circle);
  • 0
    Таким образом, вы предлагаете попробовать dynamic_cast для каждой фигуры, пока одна из них не работает? Это далеко не оптимально.
  • 0
    Очень правильное предупреждение, динамическое приведение не очень эффективно. Наличие перечисления типа объекта, как предлагает user1158692, может быть более эффективным, но всегда кажется мне ужасным. На практике я, вероятно, использовал бы что-то вроде шаблона посетителя, но, судя по этапу обучения OP, полиморфизм + динамическое приведение могут быть первыми, что нужно изучить / попробовать.
Показать ещё 2 комментария
2

Ваши классы не были нарезаны. Это приведет к разрезаемому объекту:

vector<Shape> vec;
Circle circ;
vec.push_back(circ);

http://en.wikipedia.org/wiki/Object_slicing

В вашем экземпляре объекты-объекты остаются целыми, а указатели указывают на целые объекты, но почти наверняка верно, что для вычисления пересечений вам нужно будет сделать некоторое понижение. Хотя это должно быть сделано как можно меньше, это не само преступление.

Лучше всего было бы предложить метод в базовом классе, чтобы вернуть значение, указывающее тип объекта - возможно, использовать перечисление, - и использовать его для того, чтобы опустить конкретный указатель Shape или ссылку на указатель/ссылку на правильный производный тип.

Абстрактный метод в базовом классе Shape такой как bool Intersects( const Shape& obj ) может быть переопределен производными классами, переопределяет параметр вниз для правильного производного типа.

В качестве альтернативы вы можете предпочесть глобальный/статический метод, принимающий две формы, или конфиденциально реализовать этот метод и вызвать его из метода экземпляра Intersects()

(Обнаружение пересечений - не совсем тривиальная задача. :-))

  • 0
    Как бы я узнал, что это форма и круг или квадрат? Очевидно, что метод вычисления, если два пересекаются, различен для круга и круга, круга и квадрата и квадрата и квадрата.
  • 0
    @GeorgeRobinson из ответа: «Лучше всего было бы предоставить метод в базовом классе для возврата значения, указывающего тип объекта - возможно, использовать перечисление - и использовать его для понижения определенного указателя Shape или ссылки на указатель / ссылку к правильному производному типу. "
1

Вот еще один метод, который не требует динамических бросков (или любого явного приведения вообще) или уродливого перечисления, перечисляющего подклассы. Он основан на двойной отправке, которая в основном работает, пройдя два виртуальных метода, чтобы определить типы двух объектов, над которыми вы хотите работать.

#include <iostream>
using namespace std;

class Circle;
class Square;

struct Shape
{
  virtual void intersect(Shape* otherShape) = 0;
  virtual void intersect(Circle* otherCircle) = 0;
  virtual void intersect(Square* otherSquare) = 0;
};

struct Circle : public Shape
{
  virtual void intersect(Shape* otherShape)
  {
    otherShape->intersect(this);
  }

  virtual void intersect(Circle* otherCircle)
  {
    cout << "Intersecting Circle with Circle" << endl;
  }

  virtual void intersect(Square* otherSquare)
  {
    cout << "Intersecting Circle with Square" << endl;
  }
};

struct Square : public Shape
{
  virtual void intersect(Shape* otherShape)
  {
    otherShape->intersect(this);
  }

  virtual void intersect(Circle* otherCircle)
  {
    otherCircle->intersect(this);
  }

  virtual void intersect(Square* otherSquare)
  {
    cout << "Intersecting Square with Square" << endl;
  }

};

int main()
{
  Circle circle;
  Square square;

  circle.intersect(&square);

  Shape* shapeA = &circle;
  Shape* shapeB = &square;

  shapeA->intersect(shapeA);
  shapeA->intersect(shapeB);
  shapeB->intersect(shapeA);
  shapeB->intersect(shapeB);
}

Обратите внимание, что здесь вам все равно нужно указать все возможные подклассы в базовом классе, но в этом случае в виде перегрузок intersect для каждого базового класса. Если вы не можете добавить все (скажем, вы создаете class Triangle: public Shape, но не Shape::intersect(Triangle*)), вы получите бесконечные циклы вызовов.

Также обратите внимание, что в этом примере я сделал "тройную" отправку, поэтому мне не нужно реализовывать логику, чтобы пересечь Circle с Square два раза.

  • 0
    Жаль, что они приняли ваш первый ответ и, вероятно, никогда не увидят этого. +1 все тоже самое.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню