纯虚拟类和集合(向量?)

时间:2010-03-07 00:00:26

标签: c++ vector virtual virtual-inheritance

我正在研究一种相当广泛使用虚拟类的图形应用程序。它有:

  • 图片类,基本上是形状的集合。

  • 一个形状类,它纯粹是虚拟的,并且有一些继承它的类:

    • 多边形
    • 矩形
  • 图形,任何图形图形(也是虚拟图形),形状都是从这个图形继承而来的。

基本上,我的问题归结为实现图片类,它基本上用于存储形状集合。我目前正在使用Vector来存储形状,但是,很明显这是错误的决定,因为Vector实例化了这些形状,这并不好,因为它们纯粹是虚拟的。

以下是我目前的代码库(总结一下):

class Figure {
public:
...
  virtual ~Figure();
...
};

class Shape: public Figure
{
public:
...
  virtual ~Shape() {}
  virtual Shape* clone() const = 0;
...
};

class Polygon : public Shape
{
public:
...
virtual Shape* clone() const {return new Polygon(*this);}
... 
private:
std::vector<Point> points;

};

class Picture: public Figure {
public:
...
  Picture(Graphics& gd);
  Picture (const Picture&);
  ~Picture();
  void clear();
  void add (const Shape&);
...
private:
std::vector<Shape> shapes;
Graphics* gfx;
};

//Picture implementation:
...
Picture::Picture(Graphics& gd)
{
gfx = &gd;
}


Picture::Picture(const Picture& a)
{
shapes = a.shapes;
}

Picture::~Picture() 
{
clear();
}

void Picture::clear()
{
shapes.clear();
}

void Picture::add (const Shape& shp)
{
Shape* nshp = shp.clone();
shapes.push_back(*nshp);
}
...

我得到的错误消息只是其中的一小部分:

  

picture.cpp:33:实例化   这里   /opt/local/bin/../lib/gcc/sparc-sun-solaris2.10/4.4.1/../../../../include/c++/4.4.1/ext/new_allocator。 H:105:   错误:无法分配对象   抽象类型'形状'shape.h:12:   注意:因为以下是虚拟的   函数在'Shape'中是纯粹的:   shape.h:58:注意:虚拟无效   Shape :: get(std :: istream&amp;)shape.h:31:   注意:虚拟无效   Shape :: put(std :: ostream&amp;)const   shape.h:36:注意:虚拟无效   Shape :: scale(const Point&amp;,double)   shape.h:40:注意:虚拟无效   Shape :: translate(double,double)   shape.h:45:注意:虚拟无效   外形:: reflectHorizo​​ntally(双)   shape.h:49:注意:虚拟无效   外形:: reflectVertically(双)   shape.h:52:注意:虚拟   RectangularArea Shape :: boundingBox()   const shape.h:21:注意:虚拟   Shape * Shape :: clone()const   shape.h:55:注意:虚拟无效   Shape :: draw(Graphics&amp;)const

那么存储这些形状的理想方式是什么。我应该用什么样的收藏来存放这些东西?

由于

3 个答案:

答案 0 :(得分:16)

当您需要多态时,您需要使用指针或引用。由于容器(或数组)不能存储引用,因此必须使用指针。

基本上将图片类的矢量更改为:

std::vector<Shape*>

并适当修改其他成员函数。

你不能/不应该将它们存储为值类型的原因是因为vector是一个同质的容器,即它只存储一种类型的数据(而一种类型 - 子类是不允许!)。这是因为向量将其数据存储在一个数组中,该数组需要知道它所存储的对象的大小。如果这些对象的大小不同(它们可能是不同的形状),则它不能将它们存储在数组中。

如果将它们存储为指针,那么它们都具有相同的大小(sizeof(Shape*)),并且还可以访问形状的vtable,这是允许多态行为的。

答案 1 :(得分:2)

使用协变返回类型。有关clone方法,请参阅FAQ 20.8。您也可以依赖工厂方法来创建Shape对象。

另外,你不能拥有抽象类对象的容器,抽象类也无法实例化。相反,创建一个指向派生具体对象的指针/引用的容器。请注意,如果您使用指针,则清除它们是您的责任。容器不会正确地取消分配内存。您可以使用智能指针而不是原始指针来更有效地处理此问题。从Boost中查找scoped_ptrshared_ptr

答案 2 :(得分:1)

如另一个答案中所述,多态性需要引用或指针。可以使用智能指针代替原始内存分配。您可以使用

创建一个容器
std::vector<std::unique_ptr<Shape>> shapes;

并使用

添加元素
shapes.push_back(std::make_unique<Circle>());
shapes.push_back(std::make_unique<Polygon>());
shapes.push_back(std::make_unique<Rectangle>());

向量被销毁后,内存将被释放。