Search

8.5 — Constructors

When all members of a class (or struct) are public, we can initialize the class (or struct) directly using an initialization list or uniform initialization (in C++11):

However, as soon as we make any member variables private, we’re no longer able to initialize classes in this way. It does make sense: if you can’t directly access a variable (because it’s private), you shouldn’t be able to directly initialize it.

So then how do we initialize a class with private member variables? The answer is through constructors.

Constructors

A constructor is a special kind of class member function that is automatically called when an object of that class is instantiated. Constructors are typically used to initialize member variables of the class to appropriate default or user-provided values, or to do any setup steps necessary for the class to be used (e.g. open a file or database).

Unlike normal member functions, constructors have specific rules for how they must be named:

  1. Constructors must have the same name as the class (with the same capitalization)
  2. Constructors have no return type (not even void)

Default constructors

A constructor that takes no parameters (or has parameters that all have default values) is called a default constructor. The default constructor is called if no user-provided initialization values are provided.

Here is an example of a class that has a default constructor:

This class was designed to hold a fractional value as an integer numerator and denominator. We have defined a default constructor named Fraction (the same as the class).

Because we’re instantiating an object of type Fraction with no arguments, the default constructor will be called immediately after memory is allocated for the object, and our object will be initialized.

This program produces the result:

0/1

Note that our numerator and denominator were initialized with the values we set in our default constructor! This is such a useful feature that almost every class includes a default constructor. Without a default constructor, the numerator and denominator would have garbage values until we explicitly assigned them reasonable values (remember: fundamental variables aren’t initialized by default).

Direct and uniform initialization using constructors with parameters

While the default constructor is great for ensuring our classes are initialized with reasonable default values, often times we want instances of our class to have specific values that we provide. Fortunately, constructors can also be declared with parameters. Here is an example of a constructor that takes two integer parameters that are used to initialize the numerator and denominator:

Note that we now have two constructors: a default constructor that will be called in the default case, and a second constructor that takes two parameters. These two constructors can coexist peacefully in the same class due to function overloading. In fact, you can define as many constructors as you want, so long as each has a unique signature (number and type of parameters).

So how do we use this constructor with parameters? It’s simple! We can use the direct initialization form of initialization:

This particular fraction will be initialized to the fraction 5/3!

In C++11, we prefer uniform initialization:

Note that we have given the second parameter of the constructor with parameters a default value, so the following is also legal:

Default values for constructors work exactly the same way as with any other function, so in the above case where we call six(6), the Fraction(int, int) function is called with the second parameter defaulted to value 1.

Rule: Use uniform initialization to initialize class objects

Copy initialization using equals with classes

Much like with fundamental variables, it’s also possible to initialize classes using copy initialization:

However, we recommend you avoid this form of initialization with classes, as it may be less efficient. Although direct initialization, uniform initialization, and copy initialization all work identically with fundamental types, copy-initialization does not work the same with classes (though the end-result is often the same). We’ll explore the differences in more detail in a future chapter.

Rule: Do not copy initialize your classes

Reducing your constructors

In the above two-constructor declaration of the Fraction class, the default constructor is actually somewhat redundant. We could simplify this class as follows:

Although this constructor is still a default constructor, it has now been defined in a way that it can accept one or two user-provided values as well.

When implementing your constructors, consider how you might keep the number of constructors down through smart defaulting of values.

A reminder about default parameters

The rules around defining and calling functions that have default parameters (described in lesson 7.7 -- Default arguments) apply to constructors too. To recap, when defining a function with default parameters, all default parameters must follow any non-default parameters.

This may produce unexpected results for classes that have multiple default parameters of different types. Consider:

With s4, we’ve attempted to construct a Something by providing only a double. This won’t compile, as the rules for how arguments match with default parameters won’t allow us to skip a non-rightmost parameter (in this case, the leftmost int parameter).

If we want to be able to construct a Something with only a double, we’ll need to add a second (non-default) constructor:

An implicitly generated default constructor

If your class has no other constructors, C++ will automatically generate a public default constructor for you. This is sometimes called an implicit constructor (or implicitly generated constructor).

Consider the following class:

This class has no constructor. Therefore, the compiler will generate a constructor that behaves identically to the following:

This particular implicit constructor allows us to create a Date object with no parameters, but doesn’t initialize any of the members (because all of the members are fundamental types, and those don’t get initialized upon creation).

Although you can’t see the implicitly generated constructor, you can prove it exists:

The above code compiles, because date object will use the implicit constructor (which is public).

If your class has any other constructors, the implicitly generated constructor will not be provided. For example:

Generally speaking, it’s a good idea to always provide at least one constructor in your class. This explicitly allows you to control how objects of your class are allowed to be created, and will prevent your class from potentially breaking later when you add other constructors.

Rule: Provide at least one constructor for your class, even if it’s an empty default constructor.

Classes containing classes

A class may contain other classes as member variables. By default, when the outer class is constructed, the member variables will have their default constructors called. This happens before the body of the constructor executes.

This can be demonstrated thusly:

This prints:

A
B

When variable b is constructed, the B() constructor is called. Before the body of the constructor executes, m_a is initialized, calling the class A default constructor. This prints “A”. Then control returns back to the B constructor, and the body of the B constructor executes.

This makes sense when you think about it, as the B() constructor may want to use variable m_a -- so m_a had better be initialized first!

In the next lesson, we’ll talk about how to initialize these class member variables.

Constructor notes

Many new programmers are confused about whether constructors create the objects or not. They do not -- the compiler sets up the memory allocation for the object prior to the constructor call.

Constructors actually serve two purposes. First, constructors determine who is allowed to create an object. That is, an object of a class can only be created if a matching constructor can be found.

Second, constructors can be used to initialize objects. Whether the constructor actually does an initialization is up to the programmer. It’s syntactically valid to have a constructor that does no initialization at all (the constructor still serves the purpose of allowing the object to be created, as per the above).

However, much like it is a best practice to initialize all local variables, it’s also a best practice to initialize all member variables on creation of the object. This can be done either via a constructor, or via other means we’ll show in future lessons.

Best practice: Always initialize all member variables in your objects.

Finally, constructors are only intended to be used for initialization when the object is created. You should not try to call a constructor to re-initialize an existing object. While it may compile, the results will not be what you intended (instead, the compiler will create a temporary object and then discard it).

Quiz time

1) Write a class named Ball. Ball should have two private member variables with default values: m_color (“black”) and m_radius (10.0). Ball should provide constructors to set only m_color, set only m_radius, set both, or set neither value. For this quiz question, do not use default parameters for your constructors. Also write a function to print out the color and radius of the ball.

The following sample program should compile:

and produce the result:

color: black, radius: 10
color: blue, radius: 10
color: black, radius: 20
color: blue, radius: 20

Show Solution

1b) Update your answer to the previous question to use constructors with default parameters. Use as few constructors as possible.

Show Solution

2) What happens if you don’t declare a default constructor?

Show Solution

8.5a -- Constructor member initializer lists
Index
8.4 -- Access functions and encapsulation

379 comments to 8.5 — Constructors

  • potterman28wxcv

    Hello!

    I configured CodeBlocks with the -Weffc++ flag as was described in chapter 0.11

    However, the given solution to the exercise triggers the following warning

    "‘Ball::m_color’ should be initialized in the member initialization list [-Werror=effc++]|"

    I rewrote the code using initializer lists in order to remove this warning. I also made it so that only one constructor code is really important, the second constructor is only acting as glue.

    There it is:

    I think this has to be compiled in C++11 or later

    I used auto for `color_default` because both `std::string` and `char *` were giving me errors, and I couldn't find a good type..

    Cheers

    • This can be further simplified after the next lesson by removing the constructor call ing line 10.
      Don't use `auto` because you don't know the type. You should know what your code is doing.

  • Nirbhay

    Hello!

    What is the problem with the following code?

  • IneffableOly

    Hi there,

    I've had a strange issue with the solution for 1a, both with my own solution and the provided one. When trying to compile it I get the error error: 'Ball::m_color' should be initialized in the member initialization list [-Werror=effc++], and the same for m_radius. The fix for this seemed to be to add uniform initialisation to the actual member variables in the class:

    instead of

    Seems very strange, but I'm guessing it is do with best practice being to initialise the member variables in C++11 or later and having pedantic error checking turned on?

    • -Werror=effc++ doesn't want you to create uninitialized members. You already found 1 way to fix it, the compiler's suggestion (constructor initializer lists) are covered in the next lesson.

  • The user of the class only sees the declaration. Default arguments belong at the declaration.

    Recall that no constructors are generated if you define a constructor yourself.

    If the definition now gives a default value to @i, there would be a default constructor. This conflicts with the class declaration and thus is invalid.

  • Yaroslav

    I saw somewhere on this site (can't figure out where) that you can place default parameter value both in function declaration or in function definition (outside the class) and it works.

    but what about constructor? i can place it in a constructor declaration but if i place it in constructor definition it will error with "error: addition of default argument on redeclaration makes this constructor a default constructor"

    i don't understand why if it's erroring in definition it's not erroring in declaration?
    the declaration was in .h file and my logic was to better deal with the actual values in definition part in .cpp file of a class and that's how i discovered this
    why declaration can be both, for default constructor and for constructor with parameter and definition can't ?

    • Please edit your comments instead of deleting and re-posting. My reply is here
      https://www.learncpp.com/cpp-tutorial/85-constructors/comment-page-5/#comment-416042

      • Yaroslav

        so if i understand correctly in theory something like this should work. i doesn't work but i think i get the idea. So the rule is to put default values of parameters only in constructor declaration.

        sorry for deleting the comment, when i edit comments my code tags are bugging out and become into text and not into code.

  • Xueyang

    Hello, in my code for quiz 1(a) below, it seems to work. But when I pass colname by reference, it throws an error saying no matching constructor for the corresponding object instantiated. What would be the problem? Also, in the solution for quiz 1(b), you have two constructors and the second one can handle no parameter, color only, and color plus radius cases. Why do not we need a constructor for each of these cases?  Thanks for helping.

    • Hello,

      Please post the code that's causing the error.

      > Why do not we need a constructor for each of these cases?
      See lesson 7.7 about default arguments.

      • Xueyang Wu

        Hi, the code that is having error is here:

        Also, in the solution of 1(b), why do we need the first constructor since the second constructor already provides the default value for m_radius?

        • There's no error in that code. Whenever you experience an error, post the full code and the error.
          You'll only get an error if you try to construct a ball with a temporary

          To fix it, declare `colname` const.

          The first constructor is required if you want to construct a ball given just its radius

          The second constructor can only override the radius if you also specify a color.

  • Jason

    Hello, I have a question for 1b) solution.

    In the below code,

        Ball(const std::string &color="black", double radius=10.0)
        {
            m_color = color;
            m_radius = radius;
        }

    I am not sure why you used const reference for string but not for double.
    Also, I found that reference without const doesn't work.
    Can you explain why?

    • Alex

      We generally pass fundamental types by value, and non-fundamental types by reference (or pointer). std::string is a non-fundamental type.

      Reference without const doesn't work because "black" is an l-value and non-const references can't bind to l-values (only r-values).

  • ricegf

    "a class without any public constructors can’t be created!"

    Respectfully, this isn't at all true:

    This has practical use, for example, for implementing the Singleton pattern in C++.

    I realize you don't get to static methods until 8.12, but I'd still rather not see incorrect statements in this excellent web book. Perhaps it could be rephrased something like "a class without any public constructors can’t even be instanced without some tricky work-arounds!"?

  • Napalm

    In Q1, is color passed by reference just because it's an object?

  • Nguyen

    Hi,

    I am wondering why we can't do as following:

  • Dimbo1911

    Good morning guys, what do you think about the next code (for Quiz 1)? Thank you for your time.

  • Nguyen

    Hi,

    Question on the solution to quiz 2.

    2) What happens if you don’t declare a default constructor?

    Solution:
    "....Assuming you haven’t provided a default constructor yourself, your objects will not be instantiable with no parameters. "

    I think I should be able to create my objects with no parameters even if I have not provided a default constructor myself.  Why?  because C++ will automatically generate a public default constructor for me. This is sometimes called an implicit constructor (or implicitly generated constructor).

    • Nguyen

      "This particular implicit constructor allows us to create a Date object with no parameters...."
      I only hear "constructors or functions with parameters".  I guess an object with no arguments seems to make more sense?

    • Alex

      > I think I should be able to create my objects with no parameters even if I have not provided a default constructor myself. Why? because C++ will automatically generate a public default constructor for me. This is sometimes called an implicit constructor (or implicitly generated constructor).

      Yes, that's what "If you haven’t defined any other constructors, the compiler will create an empty public default constructor for you. This means your objects will be instantiable with no parameters." is saying.

  • Nguyen

    Hi Alex & nascardriver,

    It really makes sense that foo1 in main() can't be initialized as explained in this lesson.  Why can't foo2 in line 18 be initialized since it is defined in public?

    • A class that has private members is no longer an aggregate. Since you're trying to use aggregate initialization (Lesson B.4) on a non-aggregate, you get an error.

      • Nguyen

        Is it something we have not covered yet?  Lesson B.4 is not there now.  Even if it was there, it must have been one of the last lessons in this tutorial.  Is it safe to jump from this lesson all the way to the last lesson?

        The closest answer I found to my question is from Alex located in the comment section of lesson 8.2.
        Alex replied: "Classes with no constructors or private members (or a few other things we haven't covered yet) are a special kind of class called an aggregate. Aggregates are treated like plain old data structs, and can be initialized via an initializer list. However, as soon as you create a constructor, a private member, a virtual function, or any number of other things, the class is no longer an aggregate, and list initialization is no longer allowed. Allowing list initialization on a class with a constructor or private member would allow you to violate the encapsulation of the class, which is not desirable. And thus it is disallowed"

        Again, this is the closest answer I’ve found from Alex ; therefore, I am not still sure if my example program is a “good case” to show that it violates the encapsulation of the class, which is not desirable. And thus it is disallowed.

        I think Alex’s reply is so important.  I hope Alex will add it with an example to this lesson in the near future.

        Thank you, Have a great day

        • > Lesson B.4 is not there now
          My bad, I was confused, because some of the earlier lessons also start with letters.

          > Is it safe to jump from this lesson all the way to the last lesson?
          Skipping lessons will probably cause more confusion than it is helpful.

          > this is the closest answer I’ve found from Alex
          That's paragraph is what I was referring to.

          > my example program is a “good case” to show that it violates the encapsulation
          The error isn't caused by encapsulation, but by not meeting the preconditions for the type of initialization you're using.

          > I hope Alex will add it with an example to this lesson in the near future
          C++20 will add more features to aggregates, so there will be an update to the lessons at some point. Seeing how far behind the lessons are now (Just parts of C++14, no C++17), this might take a while.

          • Nguyen

            Although I have not found any "formal" explanation that convinces me foo2 can't be initialized inside public in any lessons I've learned so far, I can temporarily use the very top paragraph (with my notes added) in this lesson to convince me as following:

            When all members of a class are public, we can initialize the class directly using an initialization list or uniform initialization inside the main() and in any members inside the class.  However, as soon as we make any member variables private, we’re no longer able to initialize classes in this way (inside main() or anywhere inside the class).

            Please let me know if I can safely assume it as above.

            Thank you, Have a great day

            • There are more restrictions.
              Quoting https://en.cppreference.com/w/cpp/language/aggregate_initialization
              A class (in C++20) is an aggregate, if
              - it has no private/protected non-static members
              - it has no user-declared or inherited constructors
              - it has no virtual, private, or protected base classes (Covered later)
              - it has no virtual functions (Covered later)

              Note that these conditions underwent a lot of changes. See the link for per-version notes.

              • Nguyen

                Hi nascardriver,

                I just modified my program (add a constructor).  It works as expected.  However, the modified program does not seem follow with what Alex's reply. (Alex said Classes with no constructors or private members (or a few other things we haven't covered yet) are a special kind of class called an aggregate. Aggregates are treated like plain old data structs, and can be initialized via an initializer list. However, as soon as you create a constructor, a private member, a virtual function, or any number of other things, the class is no longer an aggregate, and list initialization is no longer allowed. Allowing list initialization on a class with a constructor or private member would allow you to violate the encapsulation of the class, which is not desirable. And thus it is disallowed).  

                As you can see, I can initialize foo2 when there are private members and constructor in class.  foo2 will give me error if there is no constructor.  It really confuses me now.  I hope Alex would give some examples to clarify his reply/statement.

                • Nguyen

                  As you said, there are more restrictions.  However, I am not worried about those restrictions for now because I have not learned them yet.
                  Based on my programs, here are something I have learned about the class objects when working with private member variables and normal constructors:

                  1.  I should not be able to directly initialize the class objects inside main() and/or anywhere inside public members if I have a private member variable & No normal constructor at all.
                  2.  I should be able to initialize the class objects inside main() and/or public members as long as I have a private member variable & a default constructor/constructors with parameters having default value.

                  Please let me know if my assumptions are correct.

                  Thank you very much for your patience and time.

                  • > I can initialize foo2 when there are private members and constructor in class
                    You're no longer using aggregate initialization though. You're using a constructor to perform the initialization. That constructor happens to have its parameters in the same order as the class' members, so it looks the same as aggregate initialization.

                    1. It doesn't matter where you initialize them. The rules are the same. The & should be an or.

                    2. It doesn't matter where you initialize them. The members and constructor parameters also don't matter. If you have a constructor, you can instantiate the class. The constructor _should_ initialize all members, but it doesn't have to.

                    • Nguyen

                      Under the Constructor notes section in this lesson, Alex wrote "Constructors actually serve two purposes. The primary purpose is to initialize objects that have just been created. The secondary purpose is to determine whether creation of an object is allowed. That is, an object of a class can only be created if a matching constructor can be found. This means that a class without any public constructors can’t be created!"

                      So, according to the secondary purpose, in my very first program, foo1 can be created because there is a matching constructor.  This matching constructor is a implicitly generated constructor.  foo2 can't be created & initialized at the same time because there is no matching constructor .  However, in my late program, foo2 can be created & initialized because there is a matching constructor.  That matching constructor is Foo(int m = 111, int n = 222).

                      Well, I am very happy to move forward to the next lesson.  I wish I could read it earlier, so I would not have to ask you too much.

                      Thanks, have a great day.

  • Dimitri

    Hi.

    Why we can't just use uniform initialization for the default values? This code works without garbage on the output.

  • Paulo Filipe

    Hi.
    In exercise 1 b) I can't make the program compile with the following constructor:

    That reference to color just doesn't work for me. Also, I thought you could only reference to variables, not literals, because we're passing literals as arguments...

    I completed the Quizz qith code that compiled without using &color but just color. When I looked into the solution, I tried it and it doesn't compile...

    Regards.

  • In constructor notes section:
    ''the code the compiler creates does that''
    That's sound awful!  )))

  • Helliarc

    These quizzes are phenomenal! I'm at a point now where I'm writing random functions and solving math problems, combing google for examples that I can rebuild...  I have found that speed reading your lessons and going straight to the quiz helps a TON!  I don't speed read the first time through, just when I come back for more clarification and practice, and if I am lost on your quiz, I just scroll back up and learn more.  I'd like to see a "practice" section, one of the difficulties I have when I'm not in the mood to read a lot is "Staring at blank space".  I want to code, I want to code something easy, but I can't seem to figure out what I want to code... and in my search I just get distracted!  My most fun WORKING program so far, is a prime number generator, where the user enters the max number to search to...  We had some PC races up to 100,000. The most I calculated was 1 million but it took so long I moved on haha.  Thanks for the material! I'm learning a TON from you!

  • kineticcrusher

    Hello Alex! I love these tutorials and they've helped me a lot to learn C++ over the course of the past year. However, I was reading through this one and noticed this:

    "Rule: Use direct or uniform initialization with your classes"

    It's never really said why this is the rule. I've personally always been confused by the functionalites of the different ways to initialize things, and in this case it sort of just confused me more.

    • Hi!

      The problem with copy initialization was, and partially is, that a temporary object would be created.

      This used to create 1 @CCrusher object on the right side, which is then copied to the left. Copying data is slow, so this temporary is bad.
      Since C++17, this is no longer the case (Make sure you use the highest standard available in your compiler settings). The object will be constructed directly in @crusher.
      If the right side is not of the same type as the left, or it's not created in-place, a copy is still created.
      Direct- and brace-initialization (Uniform initialization) didn't have this problem, hence the rule (I guess this is the reason, I'm not Alex).

      Brace initialization prevents narrowing casts, ie. a cast that could lead to the loss of data, eg. double to int. It also works almost everywhere, can save you typing, and is easy to tell apart from a function declaration.
      Use brace initialization.

    • Alex

      The lesson was last updated around the time C++11 launched, and not everybody was C++11 capable yet. Now that we can reasonably assume everyone has a C++11 capable compiler, I've updated the practice to note that uniform initialization is preferred.

      • Hey Alex!

        I think the tutorials would benefit from assuming a higher standard (C++14 seems reasonable). This will remove a lot of the exceptions for "do this, when you use this C++ version, otherwise do that", which makes C++ a lot less attractive in my eyes. Readers want to learn C++, not its history. There's not really a need to know when which feature was added.

        • Alex

          The challenge here is that while everyone has a C++11 capable compiler now (and many have C++14 or C++17), the codebases themselves aren't necessary written to those standards. The goal of these tutorials isn't just to teach the latest and greatest, but to help prepare people to understand the kinds of things they may see in a real-life code base, many of which have years (or decades) of history and might include the use of outdated methodologies.

          As an explanative pattern, I tend to favor:
          * Here's the challenge
          * Here's the set of solutions that work, from least good to most good
          * Reiteration of the most good solution as a best practice

          That provides context for why we're talking about something, understanding of what you might see in practice, and understanding of what you should be done according to latest practices.

          But I still take your point to some degree. I'll resweep through the already rewritten lessons to see if there is any unnecessary history that can be deprecated, and be mindful of this as I continue to update and create new content.

          If you have any good counterpoints, let me know.

  • YTXbaiaLrs

    The sample for quiz 1 (and quiz 1 solution) is indented using spaces and tabs. Some of them have a width of 4 and the rest with a width of 8.

  • Brandon

    The answer to the last question confuses me on the last sentence. I thought that if you did not provide a constructor that the compiler would create an empty one for you that would allow you to use no parameters? Then the last sentence says the opposite of that?  I am probably just confused sorry.

    • The answer is pretty clear.
      The compiler will only create a default constructor, if there are no user-defined constructors. As soon as there is one, no default-constructor will be generated.

      • brandon

        I understand that part, but the part about instantiation I dont.  So, the first part says when the compiler creates a constructor for you, they can be instantiated with no parameters, but the last sentence says that if you havent provided a constructor(and assuming the compiler created one) then they would not be able to instantiate with no parameters?   I appreciate the response, and sorry for this probably being a dumb question.

      • brandon

        I had to read it about 100x but it makes sense now. Thanks for your time.  My confusion was in the "default" vs other constructors.

  • lucieon

    What's the difference between:
    1) Note that this calls an implicitly generated constructor.

    and

    2)

    • Alex

      The top one initializes the member variables, and the bottom one does not initialize the member variables but uses assignment to give them values. In the next lesson, we'll show you how to initialize members in your constructors rather than using assignment to give them values.

      • lucieon

        Hmm.. so are these two equivalent?
        (i)

        (ii)

        Also in the following code;

        are the data members initialized or assigned when you don't provide arguments to object like: Date today;

  • Clapfish

    Hi Alex!

    The supplied code in the "Default constructors" section results in the following errors when I try to compile it:

    ||In constructor 'Fraction::Fraction()':|
    |10|error: 'Fraction::m_numerator' should be initialized in the member initialization list [-Werror=effc++]|
    |10|error: 'Fraction::m_denominator' should be initialized in the member initialization list [-Werror=effc++]|

    Perhaps this is due to my 'stringent' error/warning settings, but if so, since they are recommended at the beginning of the tutorial, maybe this should be addressed somehow?

    Update:
    I fixed it by initialising the variables where they are declared (privately) to 0. Is that best practice?

    • Alex

      Yes, it's because we haven't covered member initialization lists yet (that's next lesson).

      Also, yes, it's a best practice to initialize members where they are defined if those values are reasonable defaults (and your compiler supports that feature, as it was introduced in C++11).

Leave a Comment

Put all code inside code tags: [code]your code here[/code]