You can write a default constructor, which is a constructor that accepts parameters.
When all members of a class (or struct) are public, we can use aggregate initialization to initialize the class (or struct) directly using list-initialization: Show
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 created. Constructors are typically used to initialize member variables of the class to appropriate user-provided values, or to do any setup steps necessary for the class to be used (e.g. open a file or database). After a constructor executes, the object should be in a well-defined, usable state. Unlike normal member functions, constructors have specific rules for how they must be named:
Default constructors and default initialization 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). When the line This program produces the result: 0/1 Value-initialization In the above program, we initialized our class object using value-initialization:
We can also initialize class objects using default-initialization:
For the most part, default- and value-initialization of a class object results in the same outcome: the default constructor is called. Many programmers favor default-initialization over value-initialization for class objects. This is because when using value-initialization, the compiler may zero-initialize the class members before calling the default constructor in certain cases, which is slightly inefficient (C++ programmers don’t like paying for features they’re not using). However, favoring default-initialization also comes with a downside: you have to know whether a type will initialize itself, i.e. it is a class-type and all members have an initializer, or there is a default-constructor that initializes all member variables. If you see a defined variable without an initializer, you have to think about whether that’s a mistake or not (depending on what type the object is). For example, the following code causes undefined behavior
While you might be able to initialize all members in the classes you write, it’s not feasible to read the definitions of all classes you use to make sure they do the same. Favoring value initialization for class objects is simple, consistent, and can help you catch errors, particularly while you are learning. Best practice Favor value-initialization over default-initialization for class objects. Direct- and list-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 list or direct initialization:
As always, we prefer list initialization. We’ll discover reasons (templates and std::initializer_list) to use direct initialization when calling constructors later in the tutorials. There is another special constructor that might make brace initialization do something different, in that case we have to use direct initialization. We’ll talk about these constructors later. 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 functions, so in the above case where we call Best practice Favor brace 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, list-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. 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 8.12 -- Default arguments) apply to constructors too. To recap, when defining a function with default parameters, all default parameters must follow any non-default parameters, i.e. there cannot be non-defaulted parameters after a defaulted parameter. This may produce unexpected results for classes that have multiple default parameters of different types. Consider:
With If we want to be able to construct a
An implicitly generated default constructor If your class has no 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:
The Date class has no constructors. Therefore, the compiler will generate a default constructor that allows us to create a When the generated default constructor is called, members will still be initialized if they have non-static member initializers (covered in lesson 10.7 -- Default member initialization and 13.7 -- Non-static member initialization). If your class has any other constructors, the implicitly generated constructor will not be provided. For example:
If your class has another constructor and you want to allow default construction, you can either add default arguments to every parameter of a constructor with parameters, or explicitly define a default constructor. There’s a third option as well: you can use the default keyword to tell the compiler to create a default constructor for us anyway:
Using Best practice If you have constructors in your Classes containing class members A This can be demonstrated thusly:
This prints: A B When variable This makes sense when you think about it, as the The difference to the last example in the previous section is that 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.
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 via a constructor or via non-static member initialization. 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 a) Write a
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 b) Update your answer to the previous question to use constructors with default parameters. Use as few constructors as possible. Show Solution What happens if you don’t declare a default constructor? Show Solution Does a default constructor accept parameters?A default constructor is a constructor that either has no parameters, or if it has parameters, all the parameters have default values.
Can a parameterized constructor be a default constructor?You can't call a default constructor once you've created a constructor that takes arguments. You'll have to create the no argument constructor yourself in order to make a call from the parameterized constructor. Yes, this will work.
Can we use constructors with parameters what kind of parameters can be given?A parameterized constructor accepts parameters with which you can initialize the instance variables. Using parameterized constructor, you can initialize the class variables dynamically at the time of instantiating the class with distinct values.
What is a constructor with parameters called?Parameterized Constructor – A constructor is called Parameterized Constructor when it accepts a specific number of parameters. To initialize data members of a class with distinct values. Example illustrating Parameterized Constructor: 1. 2.
|