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 →
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.
Enroll now → Course details Download the syllabus Every course with PRO →
Why take it here
Why students take C++ Programming with UPI Study
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.
Thomas Edison State UniversitySigned partner
University of the PeopleSigned partner
Charter Oak State CollegeSigned partner
Strayer UniversitySigned partner
Columbia Southern UniversitySigned partner
Indiana TechSigned partner
Penn StateWorks with NCCRS
Southern New Hampshire UniversityWorks with NCCRS
SUNYWorks with NCCRS
+many more cooperating universitiesFind my college →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?
How do I give class members default values in C++ without writing a constructor?
Why does my C++ member initializer list run in a different order than I wrote it?
In what order are C++ destructors called for local objects?
About the C++ Programming course
How many college credits is C++ Programming?
How much does C++ Programming cost and what is included?
Which universities accept UPI Study credits?
How long does C++ Programming take?
How are the exams done?
How do my credits reach my college?
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.
3 credits · NCCRSEnroll →