Initialization Inside Classes, Explained with Code Examples

UPI Study › Courses › C++ Programming › Free lessons › Lesson 9.4 Free with a free account

Initialization Inside Classes

C++ Programming · Chapter 9: Structs, Classes, and File StreamsSection 9.4 · lesson 62 of 84video coming soon

← Classes With Member FunctionsNext lesson: Defining Multiple Constructor Versions →

Earn 3 college credits for C++ ProgrammingRecommended for credit by NCCRS · Computer Science 242 · self-paced · one payment, lifetime access

In this lesson

Giving objects a starting state.

  • Explain what an uninitialized member holds
  • Use default member initializers and constructors
  • Prefer the member initializer list, in declaration order
  • Predict exactly when a destructor runs

Transcript

Everything said in the video, section by section (1,486 words). Read along, or search it.

Why It Matters

Memory Holds No Sensible Starting State

Bringing an object into existence sets aside room for its data. It doesn't put anything sensible in that room. An object whose members hold whatever the memory happened to contain, is a program waiting to produce a wrong answer that no one can trace. So the mechanisms for guaranteeing a sensible starting state aren't a convenience. They're the difference between a type you can rely on, and one you have to remember to set up correctly, every single time.

Worked Example

A Sensor Reading Full of Leftovers

Consider a small type holding one reading from a sensor: an ID, a value, and a valid flag. Declaring one reserves room for three members, and does nothing else. So they hold the leftovers of whatever the program was doing before. Reading them is undefined behavior: any value at all can come out, and different runs can differ. Printed, the ID might be a large negative number, and the value a tiny fraction. Those are meaningless, and would be obvious if anyone looked. But valid might come out false: a perfectly plausible value that happens to be right for no reason at all. It could just as easily be true on the next run. And worst of all, the program doesn't stop, warn, or behave oddly. It prints three numbers with total confidence. So we don't run this one.

Problem Setup

Moving Responsibility Into the Type

Compilers can sometimes see the fault, reporting that each member is used uninitialized. It's one of the more valuable warnings there is. The obvious answer is to assign every member after creating the object. It works, and it puts the responsibility in exactly the wrong place. On every person who ever creates one of these objects, in every place they do it, forever. Miss one member in one place, and the fault appears somewhere else entirely. The whole point of what follows is to move that responsibility into the type, where it's written once, and can't be forgotten.

Technique

Default Member Initializers Beside Declarations

The simplest fix is to give a member a value where it's declared. The ID starts at 0, the value at 0.0, and valid at false. That value is used for every object created, unless something later overrides it. Printed now, all three are sensible. Nothing else changed. No constructor was written. This is called a default member initializer, and it's the least effort for the largest share of the problem. The default sits beside the member it belongs to, so a reader sees both at once. And it applies to every way of creating the object, including ones written later. Choose a default that's obviously a default. Zero, empty and false are good not because they're correct, but because they're recognizably nothing. A plausible value, like a temperature of 25, looks like real data, and isn't.

Compiler Behavior

The Free Constructor Gets Withdrawn

Objects can be created without your writing anything, because the compiler quietly supplies a default constructor, one taking no arguments, whenever a type has none of its own. It stops the moment you write a constructor of your own. Here, book has a constructor taking a title and a page count. Creating Wings of Fire with 180 pages is fine. But creating a book with no arguments is no longer possible. The compiler says so. Writing any constructor is taken as a statement that you intend to control how objects are created, and the free one is withdrawn. That surprises people, and then turns out to be helpful. If a book genuinely needs a title and a page count to be meaningful, the compiler now refuses to make one without them.

Definition

Constructors and the Member Initializer List

A constructor is a special member function that runs automatically when an object is created. Its name is exactly the name of the type, and it states no return type at all, not even void. This constructor assigns the ID and the label in its body. That works, but it's not how it's normally written. By the time the first statement in the body runs, every member already exists, and has already been initialized. The ID holds whatever was in memory, and the label is an empty string. The body replaces them. Two steps, the first wasted. The member initializer list says what each member should start as, so it's created correctly the first time. It goes between the parameter list and the body, after a colon: ID of i, label of l. The body has nothing left to do.

Requirement

Const Members Demand the Initializer List

The initializer list isn't always optional. The clearest case: const members can't be assigned at all. A member declared const is fixed once the object exists. So it must get its value at the moment of creation, and there's no later moment to assign it. Try assigning it in the body, and the build fails twice over. The member was never initialized, and it can't be assigned to now. Here are both messages. Those two errors together tell the whole story. The initializer list is the only way in.

Common Mistake

Declaration Order Beats List Order

The initializer list also hides a trap that catches experienced programmers. Members are initialized in the order they're declared in the type. The order you write them in the initializer list has no effect whatsoever. Here, area is declared before width. The list says width of w, then area of width times 2. Reading left to right suggests width becomes 5, and area 10. What happens is the opposite. Area is initialized first, using a width that doesn't yet hold anything. That's undefined behavior, so the output could be anything, including a deceptively ordinary zero. The compiler can warn about this, as shown. The habit that removes the problem: write the initializer list in declaration order, always. And avoid initializing one member from another where you can.

Definition

Destructors Tidy Up at End of Life

A destructor is the counterpart to a constructor. It runs automatically when an object ceases to exist. Its name is the type's name after a tilde. It takes no arguments, and states no return type. Its purpose is to let an object tidy up whatever it was responsible for: closing something it opened, or releasing something it holds. A type can have as many constructors as are useful, but exactly one destructor. Neither is called by you. Creating the object calls the constructor, and leaving its scope calls the destructor. And the timing is exact.

Worked Example

Tracing Lifetimes as Nested Brackets

Printing from both makes every object's lifetime visible. A and B are constructed in main. Then, entering an inner function, C is constructed. Leaving that function, C is destroyed. Then, as main ends, B is destroyed, and finally A. Read the output as brackets: C's lifetime sits inside B's, and B's inside A's. Lifetimes nest, and never partly overlap, which is what makes the ordering predictable.

Key Rules

Scope-Based and Reverse-Order Destruction

Two rules are visible in that output, and both are guarantees, not tendencies. An object is destroyed when control leaves the block it was declared in. C was destroyed as its function ended, not later. Nothing had to remember to do it. And objects are destroyed in the reverse of the order they were created. A was created first, and destroyed last. That matters because a later object may depend on an earlier one. Reverse order guarantees the thing you depend on is still there when you're cleaned up.

Full Example

A Sensor Type Using Every Mechanism

The finished type uses each mechanism where it fits. The ID is const, fixed at creation, so it must come from the initializer list. The label must be supplied at creation. The last value and the online flag have sensible defaults, and need no argument at all. The constructor fills the ID and the label through the initializer list, in declaration order. And the destructor reports that the sensor is being released. In the output, the destructor's message appears after main is finishing, because the object isn't destroyed until control actually leaves main. Nothing in the program asked for that.

Decision Guide

Choosing the Right Initialization Mechanism

So which mechanism do you reach for? If a member has a sensible starting value that rarely varies, use a default member initializer. If it must be supplied by whoever creates the object, use a constructor parameter, passed through the initializer list. The compiler then enforces that it's given. If it's const, the initializer list is the only option. And if it needs checking before it's fit to store, initialize it in the list, then validate in the body. The body is for what isn't initialization.

Quick Check

Destruction Order of Four Objects

Quick check. In main, X and Y are created. Then a nested block creates Z, and ends. Then W is created. In what order are the four destroyed? Pause and think. Z first, the moment its block ends, before W even exists. Then, as main ends, W, Y, X: the reverse of the order they were created.

Key Takeaways

Summary of Initialization and Cleanup

To summarize. Uninitialized members silently hold leftovers. So give every member a starting value where it's declared. Writing any constructor of your own removes the one the compiler supplies. Use the initializer list, in declaration order. It's required for const members. And destructors run on leaving the block, in reverse order of creation.

About C++ Programming at UPI Study

C++ Programming (Computer Science 242) is a self-paced online course of 84 video lessons in 11 chapters, from your first program to decisions, loops, arrays, functions, classes, files, pointers and inheritance. It carries a NCCRS credit recommendation for 3 semester credits.

Every lesson transcript is free on pages like this one, and the videos are free with a free UPI Study account. Enrolling adds a practice quiz after each lesson, the graded work and final exam that earn the credit, lifetime access, and an official transcript on completion. UPI Study has helped over 130,000 students with college credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.

Why take it here

Why students take C++ Programming with UPI Study

Your first real programming languageC++ teaches how programs really work: variables, control flow, functions, memory and objects. It is the base for data structures, algorithms, systems and game programming, and for most computer-science degrees.
Credit at a fraction of the priceThe same 3 credits can cost $750 or more at a US college. Here it is one payment, self-paced, with lifetime access.
Finish your degree soonerTransfer credits reduce the courses you still owe, so you can graduate earlier or lighten a heavy semester.
Strengthen an applicationA completed college-level course on an official transcript shows admissions and scholarship committees you can do the work.
Study on your own scheduleNo start dates, no deadlines. Watch the lessons, practice, and take the final when you are ready.
130,000+students helped with college creditsACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.

Where the credit goes

Universities that work with NCCRS

C++ Programming carries a NCCRS credit recommendation. These universities work with NCCRS; some are UPI Study's signed partners. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.

Find my college → Not listed? Ask Athena in the corner of this page, or book a 15-minute call.

FAQ

Questions students ask us

About Initialization Inside Classes

What value does an uninitialized member variable hold in a C++ class?
An uninitialized member holds whatever leftover data was in that memory, and reading it is undefined behavior. Creating an object only reserves room for its members. A plain int might print a huge negative number, while a bool might happen to print false on one run and true on another. The program gives no warning, so wrong results are hard to trace.
How do I give class members default values in C++ without writing a constructor?
Use default member initializers, which put a value right beside the member's declaration. For example, int id = 0; double value = 0.0; bool valid = false; inside the class. Every object gets those values unless something overrides them, including objects created later by other means. Choose obviously blank defaults like zero, empty, or false, not plausible-looking data.
Why does my C++ member initializer list run in a different order than I wrote it?
Members are initialized in the order they are declared in the class, and the order written in the initializer list has no effect. If area is declared before width and the list says width(w), area(width * 2), then area is initialized first from an uninitialized width. That is undefined behavior. Write the list in declaration order and avoid initializing one member from another.
In what order are C++ destructors called for local objects?
A destructor runs when control leaves the block where the object was declared, and objects in the same scope are destroyed in reverse order of creation. If X and Y are created in main, then Z in a nested block, then W, the order is Z, then W, Y, X. Z goes first because its block ends first.

About the C++ Programming course

How many college credits is C++ Programming?
C++ Programming (Computer Science 242) carries a NCCRS credit recommendation for 3 semester credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
How much does C++ Programming cost and what is included?
$250, paid once, with lifetime access: all 84 video lessons, a practice quiz after each, the graded work and final exam, and an official transcript when you finish. Students who want several courses choose UPI Study PRO instead.
Which universities accept UPI Study credits?
UPI Study has signed partner agreements with universities such as Thomas Edison State University, University of the People and Charter Oak State College, and works with universities such as Penn State, SNHU and SUNY. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities. Use "Find my college" on this page to check yours.
How long does C++ Programming take?
It is self-paced with no deadlines. The 84 lessons total about 0 hours of video; most students add reading and practice time and finish in a few weeks to a few months, on their own schedule.
How are the exams done?
Quizzes and assignments are online in the course. The final exam is taken online with proctoring; a person on the UPI Study team checks assignments, usually within two days.
How do my credits reach my college?
When the course shows 100% complete, request the official transcript from your dashboard. It is sent to the college you name, and you can send it to more than one. Check your college first with "Find my college" on this page, or ask Athena.

Ready for the full course? Earn 3 credits in C++ Programming.

84 video lessons, a quiz after each one, and the graded work that earns the credit. Recommended for credit by NCCRS.

C++ Programming
3 credits · NCCRS
Enroll →