Concrete Classes and Their Uses
Concrete classes, especially those with small representations, are used much like built-in types: as local variables, accessed by name, and copied around.
Two Questions Every New Object Raises
Whenever a program creates an object, two questions matter in practice. The first is where the object lives in memory, because that determines how long it stays available and how it is created. The second is how the rest of the program reaches it once it exists. The answers differ depending on whether the object belongs to a concrete class or to a class that takes part in a class hierarchy.
This article follows that difference step by step. By the end, you should be able to predict how a given object will be created and reached before you read the code that does it. Keep the two questions in mind as you go: where does the object live, and how does the program get to it?
Concrete Classes Behave Like Built-in Types
Concrete classes, especially those with small representations, are much like built-in types. You define an object of such a class as a local variable, you access it directly through its name, and you can copy it around the program. Nothing about the object requires you to go through an intermediate reference to reach it. The object is simply there under the name you gave it.
The key habit to notice is that a concrete object is handled the way you handle an int. You do not think about where it is stored. You simply name it and use it.
- Point is a small concrete type with no inheritance involved
- a is defined as a local variable and used directly by name
- b is a copy, so changing b does not change a
What do you think happens?
After b.x is set to 10, what is the value of a.x?
Reveal answer
1
b is a separate copy of a. Assigning to b.x changes only the copy, so a.x keeps its original value of 1.
Where Hierarchy Objects Live
Classes in class hierarchies follow a different pattern. Their objects are typically allocated on the free store using new. The object itself is created by new, and the name you hold is a handle that leads to the object rather than the object itself. The local name is not where the object's data sits.
The diagram below shows this split. On one side is a local pointer variable that belongs to the function. On the other side is the object it refers to, which lives on the free store. The variable and the object are two separate things, connected by the pointer.
Why a Name Gives Only a Handle
Notice what this changes. The object is not reached by the name of a local variable sitting next to it. It is reached by following the pointer. Objects in class hierarchies are typically accessed through pointers or references rather than by their names alone.
A reference works in the same spirit. It gives you another way to refer to the object without making a copy of it. So when several names refer to the same hierarchy object, they all lead to one object on the free store, and no duplicate is created.
Comparing the Two Usage Patterns
The source puts the contrast in a single sentence: concrete classes are much like built-in types, whereas classes in hierarchies follow a different pattern. The table lays the two patterns side by side. Each row names one aspect of use, so you can read down either column to see the full pattern.
| Aspect | Concrete class (small representation) | Class in a hierarchy |
|---|---|---|
| How the object is defined | As a local variable | Created with new on the free store |
| How it is reached | Directly by its name | Through a pointer or reference |
| Copying | Copied around the program like a built-in value | Reached through the handle rather than copied by name |
| Where the name sits | The name holds the object | The name is a handle leading to the object |
Typical usage of concrete classes compared with classes in hierarchies
Tracing a Shape Reader Function
The source introduces the pattern with a function that reads data describing shapes from an input stream and constructs the appropriate Shape objects. The point of the example is that the program does not know in advance which concrete kind of shape each piece of data describes. It decides while reading, and it creates the matching object.
Because these objects belong to a class hierarchy, each one is allocated on the free store with new, and the function keeps a pointer to it so the object can be reached later. The program below is an original illustration of that pattern, written to show the flow clearly.
12.5664
9- 1
read_shapesreads one kind and one size at a time from the input stream. - 2When the kind is circle, new creates a Circle on the free store and its pointer is stored in shapes.
- 3When the kind is square, new creates a Square and its pointer is stored the same way.
- 4The function returns the vector of pointers, so the objects remain available after the function ends.
- 5main loops over the pointers and calls area through each one, reaching every object by following its pointer.
read_shapesdecides the concrete kind from each piece of input- new creates each Circle or Square on the free store
- the vector keeps pointers, so the function can return handles to the objects
- main reaches each object through its pointer p
The sequence diagram below traces the order of these steps. It shows which part of the program holds the pointer to each created shape and when the object is reached afterward.
Common Mistakes With Hierarchy Objects
Common mistake Treating a hierarchy object like a concrete local variable
What it looks likeShape s; defined as a plain local variable and then used as if it were a Circle
Why it failsHierarchy objects are typically allocated on the free store with new and reached through pointers or references, not used directly by name alone.
Do this insteadCreate the object with new and keep a pointer to it, as in Shape* p = new Circle(2.0);Common mistake Assuming a copy is made when a second name is introduced
What it looks likeShape* q = p; and then expecting q to be a separate object
Why it failsA pointer or reference gives another way to refer to the same object. It does not make a copy of the object.
Do this insteadThink of q and p as two routes to one object; a change made through either is seen through both.
Common mistake Losing the pointer to an object created with new
What it looks likeCalling new inside a function and never storing the returned pointer anywhere
Why it failsWithout a stored pointer, the program has no way to reach the object afterward, which is why the source keeps a pointer to each created shape.
Do this insteadStore the pointer in a container or variable that lives as long as the object is needed.
Common mistake Expecting a concrete class and a hierarchy class to follow the same usage rules without checking which pattern applies
Practice Tracing the Shape Reader
Using the read_shapes program above, suppose the input stream contains the text: square 4.0 circle 1.0. Predict the order in which new is called and the order in which area is called on the stored pointers. Then state which part of the program holds the pointer to each object after read_shapes returns.
Hints
- Read the input one kind and size pair at a time, from left to right.
- A square triggers new Square and a circle triggers new Circle.
- After
read_shapesreturns, the vector in main is the only thing holding the pointers.
Key Takeaways
- Concrete classes, especially those with small representations, are used much like built-in types: you define them as local variables, access them directly by name, and copy them.
- Objects in class hierarchies are typically allocated on the free store using new, and the name you hold is a handle that leads to the object.
- Hierarchy objects are typically accessed through pointers or references rather than by their names alone; a reference gives another way to refer to the object without copying it.
- A function that reads shape data can decide each concrete kind while reading, construct the matching object with new, and keep a pointer so the object can be reached later.