如何迭代基类对象的向量?

时间:2013-11-29 12:43:19

标签: c++ inheritance stl

我有一个问题,我们需要有许多形状,例如Circle和Square,它们可以放置在平面的二维平面上。所有形状,如Circle和Square,都从一个抽象的基类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

3 个答案:

答案 0 :(得分: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()

调用它

(检测交叉点并不是一项简单的任务。:-))

答案 1 :(得分:1)

你必须使用多态。在Shape类上添加虚拟方法:

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

然后为每个不同的形状实现它。然后,如果使用它像:

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

调用正确的版本,即如果ACircle,则调用Circle::intersects。但在那里,你仍然不知道B实际上是什么形状。您可以通过尝试进行动态演员来找到它:

Circle* circle = dynamic_cast<Circle*>(otherShape);
if (circle)
  intersectsCircle(circle);

答案 2 :(得分:1)

这是另一种方法,它不需要动态转换(或任何显式转换),或者列出子类的丑陋枚举。它基于double dispatch,它基本上通过两个虚方法来确定你想要处理的两个对象的类型。

#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*)),则最终会产生无限的调用循环。

另请注意,在此示例中,我执行了“三重”调度,因此我不必实现逻辑以将CircleSquare相交两次。