When writing a class that has multiple constructors (which is most of them), having to specify default values for all members in each constructor results in redundant code. If you update the default value for a member, you need to touch each constructor.
It’s possible to give normal class member variables (those that don’t use the static keyword) a default initialization value directly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <iostream> class Rectangle { private: double m_length{ 1.0 }; // m_length has a default value of 1.0 double m_width{ 1.0 }; // m_width has a default value of 1.0 public: void print() { std::cout << "length: " << m_length << ", width: " << m_width << '\n'; } }; int main() { Rectangle x{}; // x.m_length = 1.0, x.m_width = 1.0 x.print(); return 0; } |
This program produces the result:
length: 1.0, width: 1.0
Non-static member initialization (also called in-class member initializers) provides default values for your member variables that your constructors will use if the constructors do not provide initialization values for the members themselves (via the member initialization list).
However, note that constructors still determine what kind of objects may be created. Consider the following case:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <iostream> class Rectangle { private: double m_length{ 1.0 }; double m_width{ 1.0 }; public: // note: No default constructor provided in this example Rectangle(double length, double width) : m_length{ length }, m_width{ width } { // m_length and m_width are initialized by the constructor (the default values aren't used) } void print() { std::cout << "length: " << m_length << ", width: " << m_width << '\n'; } }; int main() { Rectangle x{}; // will not compile, no default constructor exists, even though members have default initialization values return 0; } |
Even though we’ve provided default values for all members, no default constructor has been provided, so we are unable to create Rectangle objects with no arguments.
If a default initialization value is provided and the constructor initializes the member via the member initializer list, the member initializer list will take precedence. The following example shows this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#include <iostream> class Rectangle { private: double m_length{ 1.0 }; double m_width{ 1.0 }; public: Rectangle(double length, double width) : m_length{ length }, m_width{ width } { // m_length and m_width are initialized by the constructor (the default values aren't used) } Rectangle(double length) : m_length{ length } { // m_length is initialized by the constructor. // m_width's default value (1.0) is used. } void print() { std::cout << "length: " << m_length << ", width: " << m_width << '\n'; } }; int main() { Rectangle x{ 2.0, 3.0 }; x.print(); Rectangle y{ 4.0 }; y.print(); return 0; } |
This prints:
length: 2.0, width: 3.0 length: 4.0, width: 1.0
Note that initializing members using non-static member initialization requires using either an equals sign, or a brace (uniform) initializer -- the direct initialization form doesn’t work here.
Rule
Favor use of non-static member initialization to give default values for your member variables.
Quiz time
Question #1
Update the following program to use non-static member initialization and member initializer lists.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#include <string> #include <iostream> class Ball { private: std::string m_color; double m_radius; public: // Default constructor with no parameters Ball() { m_color = "black"; m_radius = 10.0; } // Constructor with only color parameter (radius will use default value) Ball(const std::string &color) { m_color = color; m_radius = 10.0; } // Constructor with only radius parameter (color will use default value) Ball(double radius) { m_color = "black"; m_radius = radius; } // Constructor with both color and radius parameters Ball(const std::string &color, double radius) { m_color = color; m_radius = radius; } void print() { std::cout << "color: " << m_color << ", radius: " << m_radius << '\n'; } }; int main() { Ball def; def.print(); Ball blue{ "blue" }; blue.print(); Ball twenty{ 20.0 }; twenty.print(); Ball blueTwenty{ "blue", 20.0 }; blueTwenty.print(); return 0; } |
This program should produce the result:
color: black, radius: 10 color: blue, radius: 10 color: black, radius: 20 color: blue, radius: 20
Question #2
Why don’t we need to declare a constructor with no parameters in the program above, even though we’re constructing
def
without arguments?
![]() |
![]() |
![]() |
Hi,
If the idea for initializing private members is to avoid having to specify a default value for them in multiple constructors, how does assigning that value via non-static member initialization help? Because even though they have a default value initialized, we still need to specify that default value for the constructor parameters to make them optional. This in turn means we still need to provide the default value at multiple places. What am I missing?
Thanks.
The idea for initializing private members is to ensure they have a known, consistent value.
Without non-static member initialization, we'd have to initialize every member in every constructor (or use delegating constructors), which leads to redundancy. This allows us to set our default values in one place, and then the constructors can provide other initialization values if those are preferred.
> Because even though they have a default value initialized, we still need to specify that default value for the constructor parameters to make them optional.
I'm not sure I understand what you mean by this.
so im trying to use the default values as default values in the parameters but it gives me an error
"a nonstatic member reference must be relative to a specific object"
reason im testing if i can do this is because if i wanted to change the default values i was hoping i could change them in the default values section only
and not have to change it on each constructor as well. ALSO it could add a benefit wherein it could work for no parameters and both parameters.
but it seems i cant do this. would like to understand why
The object doesn't yet exist when the constructor get's called. `m_color` doesn't have a value until you initialize it. You can add `static` member variables and use them as default values.
Hi guys, I love the site. I am learning a great deal about C++.
I wanted to say that I appreciated quiz #1 very much for this lesson. I was unable to solve it (I wish that I had) and when I looked at the solution I was happy and impressed with its simplicity. Thank you for making my brain work! It will help me remember the proper way to do things in the future.
Hi all,
please consider a slightly modified example from the learning material:
As I learned in the previous chapters, uniform initialization does type checking for fundamental datatypes, so I cannot initialize a double with an int.
Is there a way to get a similar type checking when a class is created? I assume that implicit type conversions are happening in this example and I would like to catch them.
Thank you very much for your help in advance!
I just realized by now, that uniform initialization will throw an error only if narrowing conversion is being made, that's why I have not got any errors. Sorry for spamming!
Thanks Nascardriver for previous replies,please clarify my doubt.
>In Quiz question #1 what is the use of using non static member intialization,As we did
>When we have multiple constructors,which method should we use for intialization?
1.Member intializer list with delegating constructors
2.constructors using a seperate function as in this example
>is there any standard way among programmers for using constructors as there are many options.
Thanks in advance.
`m_color`'s default value is used by the `Ball(double)` constructor
Use method 1., 2. isn't initialization but assignment. Assignment is more expensive and not always possible.
> is there any standard way among programmers for using constructors as there are many options.
I don't understand. Can you provide an example of some of the options?
Hi there!
Regarding the answer to the second question, isn't the constructor with default arguments for all of its parameters a default constructor?
If i don't remember bad, it was stated this in the lesson about constructors
Yes that's correct! I've rephrased the question, thanks for pointing it out
1. Query
" If you update the default value for a member, you need to touch each constructor."
Is touch a new keyword? Does it mean call?
2. Feedback
For the Rectangle example
length: 2.0, width: 3.0
length: 4.0, width: 1.0
On my laptop, it prints
length: 2, width: 3
length: 4, width: 1
I'm not sure if this is important but I felt you should know.
3. Quiz 1 Ball
I feel like there is a significantly better way to create constructors.
If the input parameters rises to 10 or more, you want to know colour, race, height, weight, age, networth, gender, heart rate, cholesterol level, etc.
You would need a lot of constructors.
"touch" means the same as in real life, just not with your hands but with your cursor. Every constructor needs to be updated.
Assigning values to members after having initialized them has no effect. Re-read lesson 8.5a
"Assigning values to members after having initialized them has no effect. Re-read lesson 8.5a"
Sorry, may I ask which line this is?
Both of your constructors. You're initializing the members in the member-initializer-list, then you assign to them in the constructor body
Oh, Line 31/32 Line 42/43 are redundant.
Once I use a member initializer list, I don't have to assign them in the {} anymore.
Assigning them again is a waste of time.
I got it.
Thanks nascar.
On a side note, why didn't the compiler warn me?
Assigning is redundant code ie garbage, right?
clang-tidy would have warned you
Oh, I see.
Different compilers, different "something".
1. Should I switch to Clang? Or install a Clang add-on?
2. With regards to our conversion regarding passing by value or reference &, it seems Prof Bjarne Stroustrup seems to recommend "moving" instead of copying.
&& means "rvalue reference".
According to the book I'm reading, should we be using "move" constructors and assignments in the tutorials instead?
The site might need a massive rework.
3. Lastly, could you help me with the 8.15 Quiz on part G. My code works until part F. Thank you.
1. VS has support for clang-tidy I think. I've linked you instructions before.
2. You need both. Move semantics are covered later.
3. Post your code in that lesson, explain what you're trying to do and describe you've tried to make that happen and why it didn't work.
1. I turned on clang-tidy and Code Analysis under Code Analysis -> General for the above Ball Quiz. Clang-tidy had no effect when I uncommented.
"m_color = "black"; m_radius = radius;"
You can see I used Clang-Tidy but it has no effect.
No warnings or errors.
1>Target Performance Summary:
1> 0 ms _SelectedFiles 1 calls
1> 0 ms BeforeBuildGenerateSources 1 calls
1> 0 ms PreBuildEvent 1 calls
1> 0 ms MakeDirsForCl 1 calls
1> 0 ms SelectCustomBuild 1 calls
1> 0 ms CustomBuild 1 calls
1> 0 ms _Xsd 1 calls
1> 0 ms MakeDirsForMidl 1 calls
1> 0 ms ComputeMIDLGeneratedCompileInputs 1 calls
1> 0 ms AfterMidl 1 calls
1> 0 ms AfterBuildGenerateSources 1 calls
1> 0 ms AfterBuildGenerateSourcesEvent 1 calls
1> 0 ms _BuildGenerateSourcesAction 1 calls
1> 0 ms BuildGenerateSources 1 calls
1> 0 ms BuildCompileTraverse 1 calls
1> 0 ms BeforeClCompile 1 calls
1> 0 ms ComputeCLInputPDBName 1 calls
1> 0 ms FindReferenceAssembliesForReferences 1 calls
1> 0 ms GetReferencedVCProjectsInfo 1 calls
1> 0 ms ComputeReferenceCLInput 1 calls
1> 0 ms WarnCompileDuplicatedFilename 1 calls
1> 0 ms SelectClCompile 1 calls
1> 0 ms BuildGenerateSourcesTraverse 1 calls
1> 0 ms GetReferenceAssemblyPaths 1 calls
1> 0 ms AfterClangTidy 1 calls
1> 0 ms _PrepareForReferenceResolution 1 calls
1> 0 ms BeforeResolveReferences 1 calls
1> 0 ms AssignProjectConfiguration 1 calls
1> 0 ms _SplitProjectReferencesByFileExistence 1 calls
1> 0 ms _GetProjectReferenceTargetFrameworkProperties 1 calls
1> 0 ms ResolveProjectReferences 1 calls
1> 0 ms GetFrameworkPaths 1 calls
1> 0 ms ResolveReferences 1 calls
1> 0 ms FixupCLCompileOptions 1 calls
1> 0 ms GetResolvedWinMD 1 calls
1> 0 ms _CheckWindowsSDKInstalled 1 calls
1> 0 ms SetCABuildNativeEnvironmentVariables 1 calls
1> 0 ms ResolveSDKReferences 1 calls
1> 0 ms ExpandSDKReferences 1 calls
1> 0 ms ResolveAssemblyReferences 1 calls
1> 0 ms SetUserMacroEnvironmentVariables 1 calls
1> 1 ms SetTelemetryEnvironmentVariables 1 calls
1> 1 ms PrepareProjectReferences 1 calls
1> 1 ms BeforeClangTidy 1 calls
1> 1 ms ComputeCustomBuildOutput 1 calls
1> 1 ms _Midl 1 calls
1> 1 ms CopyFileToFolders 1 calls
1> 1 ms AfterResolveReferences 1 calls
1> 1 ms SetBuildDefaultEnvironmentVariables 1 calls
1> 1 ms _CheckForInvalidConfigurationAndPlatform 1 calls
1> 2 ms InitializeBuildStatus 1 calls
1> 4 ms GetCompileCommands 1 calls
1> 4 ms PrepareForBuild 1 calls
1> 4 ms _PrepareForBuild 1 calls
1> 7 ms ClCompile 1 calls
1> 639 ms ClangTidy 1 calls
1>
1>Task Performance Summary:
1> 0 ms Message 2 calls
1> 0 ms AssignProjectConfiguration 1 calls
1> 0 ms MSBuild 1 calls
1> 0 ms CheckVCToolsetVersion 1 calls
1> 0 ms ReadLinesFromFile 1 calls
1> 0 ms Touch 1 calls
1> 1 ms SetEnv 10 calls
1> 1 ms GetOutOfDateItems 3 calls
1> 1 ms WriteLinesToFile 1 calls
1> 2 ms MakeDir 6 calls
1> 4 ms CLCommandLine 1 calls
1> 7 ms CL 1 calls
1> 639 ms ClangTidy 1 calls
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
According to https://devblogs.microsoft.com/cppblog/code-analysis-with-clang-tidy-in-visual-studio/
there's a "clang-tidy" tab somewhere. I'm not Microsoft Support, you'll have to work through this on your own.
1. Nascar, there's a bug with clang-tidy
color: black, radius: 10
color: blue, radius: 10
color: black, radius: 20
color: blue, radius: 20
When you turn on clang-tidy, the answer can turn into
color: black, radius: 10
color: blue, radius: 10
color: , radius: 20
color: blue, radius: 20
2. Got it. Will wait until that tutorial.
3. Actually, I posted the code a few days ago with all the mistakes I made. Up to part F, it worked. Then, part G. *bazinga*
I was trying to experiment.
This is what my terminal printed out:
color: black, radius: 10
color: blue, radius: 10
color: , radius: 20
color: blue, radius: 20
Why are my radii not double in the terminal?
Why does twenty.print( ) give me: color: , radius: 20</iostream></string>
> Why are my radii not double in the terminal?
`std::cout` doesn't print trailing zeroes.
> Why does twenty.print( ) give me: color: , radius: 20
What did you expect it to do?
I meant to say that twenty.print() gives me "color:, radius:20" because Ball twenty{20.0} uses constructor Ball(double radius): m_radius{radius}{}.
Am i right to say so?
That's right
Why non-static member initialization? What about static members?
Hello,
In the second example you wrote 'No default constructor is provided'.
I remember in one of the previous lessons you said that the constructor from below is generated automatically. Did I get something wrong?
That is only generated if no constructor is provided by the programmer.
For example if you only define
Then the compiler will assume that Ball should only be created with two parameters. No default constructor will be added, and you will not be able to define a Ball variable with no parameters.
Hi nascardriver. I can't get to understand the solution to quiz 1.
Why are not we doing it like
?
I can't understand why we're not giving default non static member initialization to both. Thanks!
If you did
the reader would think that `m_radius` gets this value (10.0). But that's not true. There's no way to make the class use the value provided at `m_radius`' declaration, because it always gets overridden by the constructor.
I have a question about Question 2. Isn't the second Ball constructor the default constructor? In 8.5, a default constructor was defined as "a constructor that takes no parameters (or has parameters that all have default values)." The second constructor has parameters that all have default values, so wouldn't it be the default constructor?
'Note that initializing members using non-static member initialization requires using either an equals sign, or a brace (uniform) initializer -- the direct initialization form doesn’t work here.'
Don't you mean the opposite? All code above uses direct initialization {}?