Built-in Types in C++
A concrete class is a class designed so that its objects behave just like built-in types.
A Type That Acts Like int
Most of the types you have used so far come with the language itself. An int holds a whole number, and an array holds a fixed run of elements of one kind. These are built-in types. Now imagine a type that a programmer designs and writes as a class, yet that can be used in almost the same way as those built-in types. That idea is the subject of this article: concrete classes.
Keep one question in mind while you read: when a class is written to behave like a built-in type, what exactly is shared with the built-in type, and what is left different? You will trace the answer step by step through an example, then check it against the source's pairings of types.
Built-in Types as Starting Material
Think of a built-in type as the material you start from. When you write int total = 0; you create a value, you combine it with other values using operations the language supplies, and you pass it through your program in familiar ways. Nothing about that usage needs explaining: it is the baseline pattern of working with values in C++.
A concrete class asks a different question. Instead of accepting the language's operations as given, the programmer defines a type whose values can be used with that same familiar pattern. The source's central claim is that the user-defined type can be used in almost the same way as the built-in one, even though the two are not the same thing.
What a Concrete Class Is
A concrete class is a class designed so that its objects behave just like built-in types.
The word concrete here signals that the class is a complete, usable value type: its objects are meant to be created, combined, and passed around the way built-in values are. A concrete class does not have to reproduce every detail of the built-in type it resembles. Its job is to make its objects feel like ordinary values in a program.
Pairing Concrete Classes With Their Built-in Models
The source frames the idea through two comparisons. A complex number type and an infinite-precision integer are each much like the built-in int. A vector and a string are each much like a built-in array. In both comparisons, the user-defined type is close enough to its built-in counterpart that it can be understood through that counterpart, even though it is not the same thing.
| Concrete class | Built-in type it resembles | What is shared in use | Where it differs |
|---|---|---|---|
| Complex number type | int | Values are created, combined with operators, and passed through a program | Its operations are defined by the class, not supplied by the language |
| Infinite-precision integer | int | Integer-like values used in the same familiar way | Its semantics and its own set of operations belong to the class |
| Vector | Built-in array | Collections of elements handled in familiar ways | Described in the source as better behaved than the built-in array |
| String | Built-in array | Text handled as a value in familiar ways | Described in the source as better behaved than the built-in array |
The source pairs each concrete class with the built-in type it is meant to resemble. Every row names a difference as well as a similarity.
The pairs show that the resemblance concerns the role each type plays in a program, not how it is built inside. A complex number and an int play similar roles as quantities you calculate with. A vector and an array play similar roles as containers of elements. The internal workings can differ widely while the usage pattern stays familiar.
Tracing an Addition on a Complex Number
A complex number is a good illustration because it is a quantity that people often wish to add and track just as they would a plain number. The example below is written for this article, not taken from the source. It defines a small complex number class that stores a real part and an imaginary part, and it defines its own addition operation. Read it with the source's idea in mind: the class can be used the way a number is used, and the addition it performs is the class's own definition.
(4, 1)- 1Complex a(1.0, 2.0) creates a value whose real part is 1 and imaginary part is 2.
- 2Complex b(3.0, -1.0) creates a second value with real part 3 and imaginary part -1.
- 3The expression a + b calls the operator+ member function on a, passing b as other.
- 4Inside operator+, the real parts are added (1 + 3 = 4) and the imaginary parts are added (2 + -1 = 1), and a new Complex is returned.
- 5That returned value is stored in c, and printing c's parts produces (4, 1).
- The class stores its two parts as private members
- operator+ defines what the plus sign means for Complex values
- The expression a + b in main looks like ordinary arithmetic
What do you think happens?
Before reading on, what do you expect the program to print?
Reveal answer
(4, 1)
The real parts 1 and 3 sum to 4, and the imaginary parts 2 and -1 sum to 1, so the printed pair is (4, 1).
Following the value of c through the addition
Trace what happens to the values a, b, and c when the statement Complex c = a + b; runs.
- 1Start with a and b
a holds real 1 and imaginary 2. b holds real 3 and imaginary -1. Neither has changed yet.
- 2Dispatch the plus sign
The compiler sees a + b with a Complex on each side and uses the operator+ that the class defined, not an addition the language supplies for int.
- 3Compute the real part
Inside the class, re_ of a (1) plus other.re_ of b (3) gives 4.
- 4Compute the imaginary part
im_ of a (2) plus other.im_ of b (-1) gives 1.
- 5Store the result
The new Complex (4, 1) is the value that initializes c. The originals a and b are unchanged.
c holds real 4 and imaginary 1. a and b still hold their original values.
The plus sign in main looks like the ordinary arithmetic you already know, which is the resemblance to int that the source describes. But the addition itself lives inside the class. Had the programmer defined operator+ differently, the same visible syntax would mean something else. This is the second half of the source's point: the operations belong to the concrete type.
Shared Pattern Versus Own Operations
The source names two things that a concrete class and the built-in type it resembles share. The first is the overall pattern of use: you create values, combine them, and move them through a program in familiar ways. The second is the sense that the type is a sound, well-formed value rather than a loose collection of parts. Beyond that shared pattern, the concrete class is free to define what its own operations mean.
The diagram below sets a built-in int beside a user-defined complex number type. Both are used by writing values and operations on them, but the operations come from different places: the built-in int uses the operations the language supplies, while the complex type supplies its own.
Read the two columns across. Each row of shared pattern matches, so a reader familiar with int can start using the complex type immediately. The bottom row is where they separate: the int's operations are supplied by the language, while the complex type's operations are whatever its class defines.
Three Ingredients That Make a Type Built-in
Built-in types can be built from the fundamental types, the const modifier, and the declarator operators. It helps to read this definition as a recipe with three inputs. The first input is the set of fundamental types. The second is the const modifier, which marks a value as one that should not change. The third is the set of declarator operators, which shape how a declaration names and structures a type. A built-in type is whatever the language lets you form by combining these three inputs.
Read the diagram as a set of three inputs feeding one result. None of the three inputs is a complete built-in type on its own; each one contributes a part, and the combination is what the language recognizes as a built-in type.
- int is a fundamental type
- const marks
speed_limitas a value that should not change - The declaration combines the fundamental type with the const modifier
This declaration does not invent a new kind of type with its own operations. It combines a fundamental type with the const modifier, which is exactly the recipe the definition names. The source does not list every possible combination, so treat this as one illustration of the general principle rather than a complete catalogue.
Why C++ Keeps Built-in Types Low-Level
Imagine you need a language in which a programmer can write a fast program with almost no hidden cost, where every operation maps onto something the machine actually does. C++ begins from that requirement. Its built-in types and operations are rich, but they are deliberately low-level, and they reflect the capabilities of conventional computer hardware directly and efficiently.
A low-level foundation has a consequence. The built-in types do not give you high-level facilities that make advanced applications easy to write. The language therefore adds a second layer: a set of abstraction mechanisms that programmers use to build those higher-level facilities themselves. Keep that two-layer picture in mind, because the rest of this section is about how the layers connect.
Built-in Types Versus User-Defined Types
Built-in types are the starting material. Abstraction mechanisms are the tools that turn that material into something larger. The source describes these mechanisms as a sophisticated set that C++ adds on top of the built-in types and operations. Programmers use them to build high-level facilities out of the low-level base.
The purpose of these mechanisms is specific. They are primarily designed to let programmers design and implement their own types, with suitable representations and operations. They also let programmers use those types simply and elegantly once they exist. So the abstraction mechanisms serve two audiences in one move: the person who designs the type and the person who later uses it.
When a programmer uses the abstraction mechanisms to build a type, the result is called a user-defined type. The source gives two names for this kind of type: classes and enumerations. Concrete classes such as the complex number type above are one kind of user-defined type. Most of the source book is devoted to the design, implementation, and use of user-defined types, which shows how central this category is to working in C++.
Read the diagram from the top down. The built-in layer is the foundation at the top, augmented by abstraction mechanisms. Those mechanisms are the means by which user-defined types are built. User-defined types are also constructed directly out of the built-in types, so the built-in layer stays the foundation even at the top of the stack.
The source also notes that the standard library mainly consists of user-defined types. Those library components show what can be built with the language facilities and programming techniques the early chapters introduce. The standard library is a good place to see user-defined types at work once you understand the built-in foundation.
Mistakes Beginners Make With This Idea
Common mistake Assuming a concrete class works exactly like the built-in type it resembles
What it looks likeBelieving that a complex number type must behave identically to int in every operation
Why it failsThe source says the resemblance is about how the type is used. Concrete classes keep their own semantics and their own sets of operations.
Do this insteadUse the shared pattern to get started, then check the class's own definitions for what each operation actually does.
Common mistake Thinking the same visible syntax always means the same thing
What it looks likeReading a + b as ordinary addition even when a and b are a user-defined Complex type
Why it failsThe meaning of + for Complex comes from the operator+ the class defines, so a different definition would change the result.
Do this insteadLook for the operator definition inside the class when an expression uses a user-defined type.
Common mistake Treating a built-in type as a single unbreakable word
What it looks likeReading const int as one indivisible keyword with one meaning
Why it failsThe definition says built-in types are built by combining fundamental types, the const modifier, and declarator operators.
Do this insteadRead the declaration as a combination of parts and identify which ingredient each part supplies.
Common mistake Confusing low-level with limited: built-in types are rich, but they do not supply high-level facilities by themselves
Checking Your Understanding
A class Vector is written so that its objects can be created, combined, and passed through a program in the same way as built-in arrays. Explain what is shared between Vector and the built-in array, and name one thing that the class is free to define for itself.
Hints
- Look for the phrase the resemblance is about how the type is used.
- The source says concrete classes keep their own semantics and operations.
- Consider what the vector's own operations might mean, separate from the array's usage pattern.
Name the three ingredients that combine to form a built-in type, then decide which one the declaration const double rate = 0.05; relies on for its const part.
Hints
- The three ingredients are fundamental types, the const modifier, and declarator operators.
- Find the word that marks a value as one that should not change.
Key Takeaways
- A concrete class is a class designed so that its objects behave just like built-in types, sharing the familiar pattern of creating, combining, and passing values.
- Complex numbers and infinite-precision integers resemble int, while vectors and strings resemble built-in arrays; the source also describes vectors and strings as better behaved than the arrays they resemble.
- The shared part concerns how a type is used. Each concrete class keeps its own semantics and its own operations, so the same visible syntax can mean something different for a different class.
- A built-in type is built from three ingredients: fundamental types, the const modifier, and declarator operators. C++ keeps these low-level and close to conventional hardware.
- Abstraction mechanisms sit between the two layers, letting programmers build user-defined types (classes and enumerations) out of the built-in foundation.