Concepts / Free Store and Object Allocation

Free Store and Object Allocation

An abstract object cannot be handled that way, because its layout is deliberately kept out of view.

  • Programming

Two Things Every Type Offers

When you use a type, you usually care about two different things: what you can ask it to do, and how it stores its data internally. Most of the time those two things are tangled together. The type exposes its layout, and the code you write depends on that layout.

An abstract type is built to pull these two things apart. It is designed so that a user can work with it without knowing anything about how it is put together inside. This article introduces that idea, shows how it differs from the more familiar kind of type, and explains the practical consequences that follow from it.

What do you think happens?

Does a user of an abstract type need to know how big its objects are?

  • Yes, the user's code must know the size before it can create an object

  • No, the representation of an abstract type is unknown to the user, not even its size

Reveal answer
Answer:

No. The representation of an abstract type is unknown to the user, and that includes its size.

This one fact drives the rest of the article. If the size is unknown, the language cannot place the object in a plain local variable whose layout the surrounding code understands, so the object has to live on the free store and be reached through a reference or pointer.

Tracing What a Variable Holds

Start by asking a simple question about memory. When the user writes a variable of an abstract type, does that variable contain the object itself? According to the source, it does not. The variable holds a way of reaching the object, and the object holds the representation. The diagram below traces this arrangement from the user's variable down to the hidden layout.

User variableholds a way of reaching theobjectAbstract objectallocated on the free storeRepresentationhidden from the userrefers through reference or pointerholds
What does the user's variable hold instead of the object itself, and where does the representation sit?

Compare this with the picture for a concrete type. A local variable of a concrete type is handled as a value: the value lives in the scope where it is declared, and the surrounding code understands its layout. An abstract object cannot be handled that way, because its layout is deliberately kept out of view. The variable therefore becomes a handle on the object rather than the object itself.

Concrete Types Show Their Layout

The familiar kind of type is called a concrete type. Types such as complex and Vector are examples. Their representation is part of their definition: the way the data is laid out is written directly into the type's definition, and users can see it. Because of this, concrete types resemble the built-in types that come with the language.

Definition

A concrete type is a type whose representation is part of its definition. Its layout is visible to users, and it resembles the built-in types of the language.

The key test for telling the two kinds apart is a single question: is the representation part of the type's definition? If the answer is yes, the type is concrete. If the representation has been deliberately removed from the definition as seen by the user, the type is abstract. The table below lines up the two kinds against this test.

FeatureConcrete typeAbstract type
Representation in the definitionPart of the definition and visible to the userDeliberately removed from the user's view
Examples named in the sourcecomplex and VectorA Container class, intended as a more abstract version of Vector
What a user can depend onThe layout, because it is visibleOnly the interface, because the representation and even its size are unknown
Local variablesGenuine local variables are availableGenuine local variables are given up
How objects are heldHandled like built-in value typesAllocated on the free store and accessed through references or pointers

Concrete and abstract types differ in whether the representation belongs to the type's definition.

Interface and Representation

The source describes the mechanism for insulation in one short phrase: the design decouples the interface from the representation. These two parts need to be separated clearly before the idea makes sense.

Definition

The interface of a type is the set of operations a user can perform on it. The representation is the way the data is actually stored.

Decoupling means these two parts are kept separate. The user's code is written against the interface alone, and the representation can be chosen or changed without disturbing that code. The diagram shows the user's code reaching the interface, while the representation sits behind it and is never reached directly.

User codewritten against theinterfaceInterfaceoperations the user canperformRepresentationhow the data is storeduses onlyhides
Which parts of the code does the user touch directly, and which parts are hidden behind the interface?

The source develops this with a class called Container, intended as a more abstract version of the Vector type seen earlier. Notice the order of work. The interface is designed first, and at that stage only the interface is fixed. Questions about how the elements are stored come later, and they do not leak into the description of what the user may do.

What a User Can Rely On

Decoupling changes what a user is entitled to depend on. Before the representation is hidden, user code can depend on how the data is laid out. After it is hidden, user code can depend only on the operations in the interface. The diagram compares the two situations side by side.

User codereads the layout directlyRepresentationvisible to the userUser codedepends on the interfaceonlyInterfacethe only thing relied onRepresentationhidden and replaceabledepends on layoutdepends on operationshides
What can a user depend on before versus after the representation is hidden, and what can change without breaking their code?

The practical payoff follows directly. Because the user's code relies only on the operations, the internal storage can be replaced without the user having to rewrite anything that depends only on those operations. The user's dependency is on what the type can do, not on how the type stores its data.

Why Abstract Objects Live on the Free Store

Since the representation of an abstract type is unknown to the user, not even its size is known. The language design therefore has to avoid any situation in which the compiler or the user's code needs to know that size in advance. The source draws the consequence clearly: objects of an abstract type must be allocated on the free store and accessed through references or pointers.

Definition

The free store is the region used for objects whose lifetime is managed explicitly rather than tied to a single scope. References and pointers are the means by which the user's code refers to such objects.

This explains the whole arrangement. A variable of an abstract type cannot be a container for the object, because the object's size is not known where the variable is declared. The variable instead holds a reference or pointer, and the object lives on the free store, where it holds the representation.

Worked example

Working With a Container Through a Reference

A user wants to work with a Container, an abstract type whose interface was designed before its storage was decided. Trace what the user's code must hold, and what it must not assume.

  1. 1
    Start from the interface

    The user knows what Container can do, because the interface was fixed first. The user does not know how the elements are stored.

  2. 2
    Check whether the size is needed

    The size of a Container is part of the unknown representation. So the user's code cannot reserve room for a Container object in a local variable.

  3. 3
    Hold a reference or pointer

    The variable holds a way of reaching an object allocated on the free store. The user calls the interface operations through that reference.

  4. 4
    Avoid assuming the layout

    The user never reads the internal storage. If the storage is later changed, the code that calls only the interface still works.

Result

The user works with Container entirely through its interface, holding a reference to an object on the free store, and never depends on the representation or its size.

The Cost of Hiding the Representation

Hiding the representation has a cost, and the source names it directly: the approach gives up genuine local variables. A local variable is a value that lives in the scope where it is declared and is handled as a value. To keep the representation out of view, the design cannot treat an abstract object as a plain value whose layout the surrounding code understands.

The trade is deliberate. The source accepts these restrictions so that the user can work with the type without seeing its internal layout. Losing the convenience of local variables is the price of the insulation the design provides.

Mistakes Beginners Make

  • Common mistake

    Assuming an abstract type's size is part of what the user can rely on

    Why it fails

    The representation of an abstract type is unknown to the user, and that includes its size.

    Do this instead

    Rely only on the interface operations, and never plan memory around a size the user has not been given.

  • Common mistake

    Treating an abstract object like a concrete type such as complex, and holding it in a plain local variable

    Why it fails

    A concrete type's representation is part of its definition, but an abstract object gives up genuine local variables.

    Do this instead

    Allocate the object on the free store and hold it through a reference or pointer.

  • Common mistake

    Reading the internal storage to decide what operations are available

    Why it fails

    The operations are the interface, which is designed first and kept separate from how elements are stored.

    Do this instead

    Decide what to do from the interface alone, so the code keeps working if the storage changes.

  • Common mistake

    Believing that the free store and a single scope manage object lifetime in the same way

Checking Your Understanding

EASY

A type is described as having its layout written directly into its definition, with users able to see that layout. Another type keeps its representation hidden, and users know only its operations. Which of the two is concrete, which is abstract, and why does the answer depend on whether the representation is part of the definition?

Hints
  • Ask whether the representation is part of the type's definition.
  • A concrete type resembles the built-in types, such as complex and Vector.
  • An abstract type's objects must be reached through references or pointers.

Key Takeaways

  • An abstract type insulates the user from implementation details by decoupling the interface from the representation, so the user depends on what the type can do rather than how it stores data.
  • A concrete type has its representation as part of its definition, as with complex and Vector, and resembles the built-in types. An abstract type removes the representation from the user's view.
  • Because the representation of an abstract type is unknown, not even its size, its objects must be allocated on the free store and accessed through references or pointers.
  • The cost of this design is that genuine local variables of abstract types are given up, and the internal storage can then be changed without breaking code that relies only on the interface.

Related concepts

More concepts starting with F