Re: Is this bad? (How can this be avoided?)
mike3 wrote:
On Nov 28, 3:16 pm, Leigh Johnston <le...@i42.co.uk> wrote:
On 28/11/2011 21:44, mike3 wrote:
Hi.
Is this bad? Suppose you have something like this:
---
class Shape {
... blah ...
};
class Rectangle : public Shape {
... blah ...
};
class Circle : public Shape {
... blah ...
};
... blah ...
class Drawing {
private:
... blah ...
std::vector<Shape *> shapeList;
... blah ...
public:
... blah ...
void addShape(Shape *s)
{
shapeList.push_back(s);
}
... blah ...
void doSmth(); // uses the objects in shapeList somehow
... blah ...
};
---
There could arise a situation like this:
---
void someFunction()
{
Drawing drawing;
... blah ...
{
Circle circle(35.0, 55.0, 25.0); // make a circle& (35, 55)
with
// radius 25
drawing.addShape(&circle); // add it to the drawing
// OOPS! circle goes out of scope...!
}
... blah ...
drawing.doSmth(); // BOOM!
... blah ...
}
---
What to do? Now one could, I suppose, change to Circle* circle = new
Circle(35.0, 55.0, 25.0);, but then we need something to take care
of the garbage. Adding a delete() in, say, the destructor for
Drawing might work, but we make some assumptions that may or may
not be true, i.e. one can break it by misusing it, e.g. if one does
not use a new to get the parameter or one forgets that destroying
drawing also destroys circle, etc. Is it a bad idea to rely on the
user's understanding -- to me it doesn't seem so. What should be
done?
Easy: use std::vector<std::tr1::shared_ptr<Shape> > (pre C++11) or
std::vector<std::unique_ptr<Shape> > (C++11) instead or take a look
at the Boost "ptr" containers.
/Leigh
And should the function addShape() be upgraded to use shared_ptr?
And also, though this solves the "garbage collection" issue, it still
seems
the user needs to understand that they must pass an object that's on
the heap,
and that it will be lost when the Drawing object is gone. Is that OK?
After the benefit of many years of occasionally revisiting ye olde taxonomy
on geometry problem, I offer the following as general advice and specific
comment for this particular question.
For polymorphic heirarchies, the base class defines how the outside world
interacts with the concrete sub-types. Its role and content deserve much
more thought and effort than simply "...blah...", before diving immediately
into the derived types. Doing so places the proverbial cart before the
horse, as it were.
Further, almost all "solutions" miss the key abstractions, focusing instead
on the relatively meaningless distinction between rectangles, circles, and
pentagons, ad nauseum. The vertex-list, or vertex generation, seems implicit
in every naive implementation, but I've seen little effort to bring it
forward as an explicit, exploitable concept.
Last, the more important distinction from my point of view is the difference
between a spline, and a haphazard shape connected by straight line segments.
On the other hand, if ye olde professor truly did intend for you to focus on
the trivialities of four-sided versus five-sided regular geometric shapes,
stick with his program, however much you might feel above this simplistic
learning. The larger danger is in over-engineering the solution and still
missing the simplistic mark that was placed before you.
==========================
class Shape2D {
public:
Shape2D(Vertex2D originXY);
Shape2D(Vertex2D originXY, Vertex2D scaleXY);
Shape2D(Vertex2D originXY, Vertex2D scaleXY, Scalar rotation);
// Shape2D(const Shape2D &); // default copy is appropriate.
void Draw(Canvas2D &) const;
// void SetColor(Color); // as needed
...
private:
virtual DrawTo(Canvas2D &) const = 0; // abstract
Vertex2D origin;
Vertex2D scale;
Scalar rotationXY;
VertexGen & vertices;
};