Pointers to Objects in a Program
The pointer is the link between the slot and the shape.
A Drawing Program with Many Shapes
A drawing program rarely works with a single shape. It reads shapes from some input, keeps them while the program works, draws them, transforms them, and finally cleans up. Each of those stages touches the same collection of shapes, so a small mistake in one stage can show up as a wrong result somewhere else. This article follows one short program through its stages so you can practice tracing a result back to the stage that produced it.
The program you will study is a single user function. It reads shapes until input runs out, draws every shape, rotates every shape by 45, and then deletes every shape. Read it as a sequence of stages. The question to keep in mind is: at which stage would the output stop matching what you expected?
What do you think happens?
The program starts with an empty vector of shape pointers and fills it inside a while loop that calls read_shape on the input stream. Once that loop has finished reading everything, what should the vector hold?
Reveal answer
One pointer for each shape read, each pointing to a shape object
Each call to read_shape produces one shape, and push_back appends that shape to the vector. Because the vector is declared as a vector of Shape* pointers, the elements stored are pointers that refer to shapes, not the shapes themselves.
Stage One: Reading Shapes into the Vector
The program uses a shape reader to get shapes from input. The source shows the reader being called as read_shape(cin), and its result being passed directly to push_back on the vector. Each call to read_shape produces one shape, and push_back adds that shape to the collection. The reader is therefore the only place in this program where new shapes come into existence from outside.
- Declares the collection: the asterisk means the vector stores pointers to shapes.
- The reading loop calls
read_shapeonce per pass and appends the result withpush_back. draw_allandrotate_alleach receive the whole vector.- The delete loop is the only place in this program where shapes are released.
Read this diagram once per pass through the loop. Each pass moves exactly one shape from the input into the end of the vector, so the vector grows by one element each time. When the input runs out, the loop stops and the vector holds every shape that was read, in the order they were read.
Why the Vector Holds Pointers
A Shape* is a pointer to a shape. In the declaration std::vector<Shape*> v, the asterisk means the vector stores pointers to shapes rather than the shapes themselves. Each slot in the vector holds a pointer, and that pointer refers to a shape object that exists separately from the slot.
This choice matters for every later stage. Drawing, rotating, and deleting all work through those pointers. When draw_all reaches a slot, it does not find a shape sitting in that slot; it finds a pointer, and the shape it needs is the object that pointer refers to. The pointer is the link between the slot and the shape.
How draw_all and rotate_all Walk the Vector
Once the vector holds the shapes, the program calls draw_all(v) and then rotate_all(v,45). The source's comments describe what each call does to the vector: draw_all calls draw() for each element, and rotate_all calls rotate(45) for each element. Both functions work on the whole vector, so the caller does not write a loop over the shapes for these two stages.
Each function steps through the vector one element at a time. On each iteration, the element is a pointer, and the operation is applied to the shape that pointer refers to. The draw operation therefore runs once for every shape, and the rotation operation runs once for every shape with the argument 45.
Tracing the Stages in Order
The program runs in a fixed order: reading, storing, drawing, rotating, and then deleting. Because the order is fixed, you can rule stages out one at a time when a result is wrong. The diagram below lays out those stages in sequence.
The order matters. Drawing happens before rotating in this program, so the drawing stage reflects the shapes as they were read, and the rotation stage then changes the shapes that remain in the vector. If the result of drawing looks wrong, the rotation stage has not run yet and cannot be the cause. This ordering is a useful way to narrow down a failure.
What the Delete Loop Really Does
The final stage is a loop that deletes each element: for (auto p : v) delete p. The source marks this with the comment that you should remember to delete the elements. Each iteration copies one pointer out of the vector into p and then deletes the shape that p points to. This is the only place in the program where shapes are released.
Deleting the pointed-to shape does not change the vector. The vector still contains the same pointer values, but after this loop those pointers no longer refer to live objects. The vector is not cleared by this loop, so v still reports the same number of elements afterward. Keeping that distinction in mind is essential when you trace the final state of the program.
Worked Trace of a Two-Shape Input
Following two shapes through every stage
The input contains two shapes. Trace what the vector holds and what happens to the shapes at each stage of the program.
- 1Read stage, first pass
read_shape(cin)produces one shape, andpush_backappends a pointer to that shape. The vector now has one element. - 2Read stage, second pass
read_shape(cin)produces a second shape, andpush_backappends its pointer. The vector now has two elements, and input has run out, so the reading loop ends. - 3Draw stage
draw_all(v)callsdraw()for each element. Each pointer leads to one shape, so both shapes are drawn as they were read, before any rotation. - 4Rotate stage
rotate_all(v,45)callsrotate(45)for each element, so both shapes are rotated by 45 after they were drawn. - 5Cleanup stage
The delete loop copies each pointer into p and deletes the shape it refers to. Both shapes are released, but the vector still has two slots holding the same pointer values.
The vector holds two pointers before cleanup and still holds two pointers after cleanup. Only the shape objects were released, so the vector must not be used as a source of valid shapes after the delete loop.
Mistakes Beginners Make with Shape Pointers
Common mistake Assuming the delete loop removes the pointers from the vector
What it looks likeAfter for (auto p : v) delete p, the program assumes v is now empty.
Why it failsThe delete loop releases the shape objects but does not remove the pointers from the vector. v still reports the same number of elements.
Do this insteadAfter cleanup, treat the vector as holding stale pointers. Do not read shapes through them.
Common mistake Blaming the rotation stage for a wrong drawing
What it looks likeThe drawn shapes look wrong, and the programmer edits rotate_all first.
Why it failsDrawing happens before rotating in this program, so rotation has not run yet when the drawing is produced.
Do this insteadCheck the reading stage and the shapes before drawing first. Rotation can only be the cause when the drawn shapes are correct and the rotated result is wrong.
Common mistake Treating the vector slot as the shape itself
What it looks likeAssuming v[0] is a shape and that changing the slot changes the shape.
Why it failsThe slot holds a pointer. The shape is a separate object that the pointer refers to.
Do this insteadFollow the pointer to the object it refers to when reasoning about drawing, rotating, or deleting.
Locating a Wrong Result
When the output of a shape program is wrong, the most useful first question is which stage produced the wrong value. Because the program runs in a fixed order of reading, drawing, rotating, and deleting, you can rule stages out one at a time. If the shapes drawn are already wrong, the problem lies in reading or in the shapes before drawing. If the drawn shapes are right but the rotated result is wrong, the problem lies in the rotation stage or in how it was called.
The drawn output of a two-shape input is correct, but the rotated output is wrong. Which stage do you investigate first, and which stages can you rule out? Explain your reasoning by following the order of the stages.
Hints
- Ask which stages run before the drawing and which run after it.
- Since the drawn shapes are correct, what does that tell you about reading and drawing?
- Consider how
rotate_allis called and what it does to each element of the vector.
Key Takeaways
- The reading stage calls
read_shapeon the input and passes the result topush_back, so the vector gains one element per shape read. - std::vector<Shape*> stores pointers to separately existing shape objects, and
draw_allandrotate_allapply their operations to each pointer's shape in turn. - The delete loop is the only stage that releases shapes, but it does not remove the pointers from the vector, so those pointers must not be used afterward.
- To locate a wrong result, follow the fixed stage order and identify the first stage whose value differs from what you expected.