12.4. Constructors
Constructors (Section 2.3.3, p. 49) are special member functions that are executed whenever we create new objects of a class type. The job of a constructor is to ensure that the data members of each object start out with sensible initial values. Section 7.7.3 (p. 262) showed how we define a constructor:
This constructor uses the constructor initializer list to initialize the units_sold and revenue members. The isbn member is implicitly initialized by the string default constructor as an empty string.Constructors have the same name as the name of the class and may not specify a return type. Like any other function, they may define zero or more parameters.
class Sales_item {
public:
// operations on Sales_itemobjects
// default constructor needed to initialize members of built-in type
Sales_item(): units_sold(0), revenue(0.0) { }
private:
std::string isbn;
unsigned units_sold;
double revenue;
};
Constructors May Be Overloaded
There is no constraint on the number of constructors we may declare for a class, provided that the parameter list of each constructor is unique. How can we know which or how many constructors to define? Ordinarily, constructors differ in ways that allow the user to specify differing ways to initialize the data members.For example, we might logically extend our Sales_item class by providing two additional constructors: one that would let users provide an initial value for the isbn and another that would let them initialize the object by reading an istream object:
class Sales_item;
// other members as before
public:
// added constructors to initialize from a string or an istream
Sales_item(const std::string&);
Sales_item(std::istream&);
Sales_item();
};
Arguments Determine Which Constructor to Use
Our class now defines three constructors. We could use any of these constructors when defining new objects:
The argument type(s) used to initialize an object determines which constructor is used. In the definition of empty, there is no initializer, so the default constructor is run. The constructor that takes a single string argument is used to initialize Primer_3rd_ed; the one that takes a reference to an istream initializes Primer_4th_ed.
// uses the default constructor:
// isbn is the empty string; units_soldand revenue are 0
Sales_item empty;
// specifies an explicit isbn; units_soldand revenue are 0
Sales_item Primer_3rd_Ed("0-201-82470-1");
// reads values from the standard input into isbn, units_sold, and revenue
Sales_item Primer_4th_ed(cin);
Constructors Are Executed Automatically
The compiler runs a constructor whenever an object of the type is created:
In the first case, the constructor that takes a string is run to initialize the variable named Primer_2nd_ed. In the second case, a new Sales_item object is allocated dynamically. Assuming that the allocation succeeds, then the object is initialized by running the default constructor.
// constructor that takes a string used to create and initialize variable
Sales_item Primer_2nd_ed("0-201-54848-8");
// default constructor used to initialize unnamed object on the heap
Sales_item *p = new Sales_item();
Constructors for const Objects
A constructor may not be declared as const (Section 7.7.1, p. 260):
There is no need for a const constructor. When we create a const object of a class type, an ordinary constructor is run to initialize the const object. The job of the constructor is to initialize an object. A constructor is used to initialize an object regardless of whether the object is const.
class Sales_item {
public:
Sales_item() const; // error
};
Exercises Section 12.4
Exercise 12.19:Provide one or more constructors that allows the user of this class to specify initial values for none or all of the data elements of this class:
Explain how you decided how many constructors were needed and what parameters they should take.Exercise 12.20:Choose one of the following abstractions (or an abstraction of your own choosing). Determine what data is needed in the class. Provide an appropriate set of constructors. Explain your decisions.
class NoName {
public:
// constructor(s) go here ...
private:
std::string *pstring;
int ival;
double dval;
};
(a) Book (b) Date (c) Employee
(d) Vehicle (e) Object (f) Tree
12.4.1. The Constructor Initializer
Like any other function, a constructor has a name, a parameter list, and a function body. Unlike other functions, a constructor may also contain a constructor initializer list:
The constructor initializer starts with a colon, which is followed by a comma-separated list of data members each of which is followed by an initializer inside parentheses. This constructor initializes the isbn member to the value of its book parameter and initializes units_sold and revenue to 0. As with any member function, constructors can be defined inside or outside of the class. The constructor initializer is specified only on the constructor definition, not its declaration.
// recommended way to write constructors using a constructor initializer
Sales_item::Sales_item(const string &book):
isbn(book), units_sold(0), revenue(0.0) { }

This constructor assigns, but does not explicitly initialize, the members of class Sales_item. Regardless of the lack of an explicit initializer, the isbn member is initialized before the constructor is executed. This constructor implicitly uses the default string constructor to initialize isbn. When the body of the constructor is executed, the isbn member already has a value. That value is overwritten by the assignment inside the constructor body.Conceptually, we can think of a constructor as executing in two phases: (1) the initialization phase and (2) a general computation phase. The computation phase consists of all the statements within the body of the constructor.Section 2.3.4, p. 50). Data members of class type are initialized by running the type's default constructor. The initial value of members of built-in or compound type depend on the scope of the object: At local scope those members are uninitialized, at global scope they are initialized to 0.The two versions of the Sales_item constructor that we wrote in this section have the same effect: Whether we initialized the members in the constructor initializer list or assigned to them inside the constructor body, the end result is the same. After the constructor completes, the three data members hold the same values. The difference is that the version that uses the constructor initializer initializes its data members. The version that does not define a constructor initializer assigns values to the data members in the body of the constructor. How significant this distinction is depends on the type of the data member.
// legal but sloppier way to write the constructor:
// no constructor initializer
Sales_item::Sales_item(const string &book)
{
isbn = book;
units_sold = 0;
revenue = 0.0;
}
Constructor Initializers Are Sometimes Required
If an initializer is not provided for a class member, then the compiler implicitly uses the default constructor for the member's type. If that class does not have a default constructor, then the attempt by the compiler to use it will fail. In such cases, an initializer must be provided in order to initialize the data member.

Remember that we can initialize but not assign to const objects or objects of reference type. By the time the body of the constructor begins executing, initialization is complete. Our only chance to initialize const or reference data members is in the constructor initializer. The correct way to write the constructor is
class ConstRef {
public:
ConstRef(int ii);
private:
int i;
const int ci;
int &ri;
};
// no explicit constructor initializer: error ri is uninitialized
ConstRef::ConstRef(int ii)
{ // assignments:
i = ii; // ok
ci = ii; // error: cannot assign to a const
ri = i; // assigns to ri which was not bound to an object
}
// ok: explicitly initialize reference and const members
ConstRef::ConstRef(int ii): i(ii), ci(i), ri(ii) { }
Advice: Use Constructor Initializers
In many classes, the distinction between initialization and assignment is strictly a matter of low-level efficiency: A data member is initialized and assigned when it could have been initialized directly. More important than the efficiency issue is the fact that some data members must be initialized.

Order of Member Initialization
Not surprisingly, each member may be named only once in the constructor initializer. After all, what might it mean to give a member two initial values? What may be more surprising is that the constructor initializer list specifies only the values used to initialize the members, not the order in which those initializations are performed. The order in which members are initialized is the order in which the members are defined. The first member is initialized first, then the next, and so on.

In this case, the constructor initializer is written to make it appear as if j is initialized with val and then j is used to initialize i. However, i is initialized first. The effect of this initializer is to initialize i with the as yet uninitialized value of j!Some compilers are kind enough to generate a warning if the data members are listed in the constructor initializer in a different order from the order in which the members are declared.
class X {
int i;
int j;
public:
// run-time error: i is initialized before j
X(int val): j(val), i(j) { }
};

In this version, the order in which i and j are initialized doesn't matter.
X(int val): i(val), j(val) { }
Initializers May Be Any Expression
An initializer may be an arbitrarily complex expression. As an example, we could give our Sales_item class a new constructor that takes a string representing the isbn, an unsigned representing the number of books sold, and a double representing the price at which each of these books was sold:
This initializer for revenue uses the parameters representing price and number sold to calculate the object's revenue member.
Sales_item(const std::string &book, int cnt, double price):
isbn(book), units_sold(cnt), revenue(cnt * price) { }
Initializers for Data Members of Class Type
When we initialize a member of class type, we are specifying arguments to be passed to one of the constructors of that member's type. We can use any of that type's constructors. For example, our Sales_item class could initialize isbn using any of the string constructors (Section 9.6.1, p. 338). Instead of using the empty string, we might decide that the default value for isbn should be a value that represents an impossibly high value for an ISBN. We could initialize isbn to a string of ten 9s:
This initializer uses the string constructor that takes a count and a character and generates a string holding that character repeated that number of times.
// alternative definition for Sales_item default constructor
Sales_item(): isbn(10, '9'), units_sold(0), revenue(0.0) {}
Exercises Section 12.4.1
Exercise 12.21:Write the default constructor using a constructor initializer for class that contains the following members: a const string, an int, a double*, and an ifstream&. Initialize the string to hold the name of the class.Exercise 12.22:The following initializer is in error. Identify and fix the problem.
Exercise 12.23:Assume we have a class named NoDefault that has a constructor that takes an int but no default constructor. Define a class C that has a member of type NoDefault. Define the default constructor for C.
struct X {
X (int i, int j): base(i), rem(base % j) { }
int rem, base;
};
12.4.2. Default Arguments and Constructors
Let's look again at our definitions for the default constructor and the constructor that takes a string:
These constructors are almost the same: The only difference is that the constructor that takes a string parameter uses the parameter to initialize isbn. The default constructor (implicitly) uses the string default constructor to initialize isbn.We can combine these constructors by supplying a default argument for the string initializer:
Sales_item(const std::string &book):
isbn(book), units_sold(0), revenue(0.0) { }
Sales_item(): units_sold(0), revenue(0.0) { }
Here we define only two constructors, one of which provides a default argument for its parameter. The constructor that takes a default argument for its single string parameter will be run for either of these definitions:
class Sales_item {
public:
// default argument for book is the empty string
Sales_item(const std::string &book = "):
isbn(book), units_sold(0), revenue(0.0) { }
Sales_item(std::istream &is);
// as before
};
In the case of empty, the default argument is used, whereas Primer_3rd_ed supplies an explicit argument.Each version of our class provides the same interface: They both initialize a Sales_item to the same values given a string or given no initializer.
Sales_item empty;
Sales_item Primer_3rd_Ed("0-201-82470-1");

12.4.3. The Default Constructor
The default constructor is used whenever we define an object but do not supply an initializer. A constructor that supplies default arguments for all its parameters also defines the default constructor.
The Synthesized Default Constructor
If a class defines even one constructor, then the compiler will not generate the default constructor. The basis for this rule is that if a class requires control to initialize an object in one case, then the class is likely to require control in all cases.
Exercises Section 12.4.2
Exercise 12.24:Using the version of Sales_item from page 458 that defined two constructors, one of which has a default argument for its single string parameter, determine which constructor is used to initialize each of the following variables and list the values of the data members in each object:
Exercise 12.25:Logically, we might want to supply cin as a default argument to the constructor that takes an istream&. Write the constructor declaration that uses cin as a default argument.Exercise 12.26:Would it be legal for both the constructor that takes a string and the one that takes an istream& to have default arguments? If not, why not?
Sales_item first_item(cin);
int main() {
Sales_item next;
Sales_item last("9-999-99999-9");
}
synthesized default constructor initializes members using the same rules as those that apply for how variables are initialized. Members that are of class type are initialized by running each member's own default constructor. Members of built-in or compound type, such as pointers and arrays, are initialized only for objects that are defined at global scope. When objects are defined at local scope, then members of built-in or compound type are uninitialized.

Classes Should Usually Define a Default Constructor
In certain cases, the default constructor is applied implicitly by the compiler. If the class has no default constructor, then the class may not be used in these contexts. To illustrate the cases where a default constructor is required, assume we have a class named NoDefault that does not define its own default constructor but does have a constructor that takes a string argument. Because the class defines a constructor, the compiler will not synthesize the default constructor. The fact that NoDefault has no default constructor means:
- Every constructor for every class that has a NoDefault member must explicitly initialize the NoDefault member by passing an initial string value to the NoDefault constructor.The compiler will not synthesize the default constructor for classes that have members of type NoDefault. If such classes want to provide a default, they must define one explicitly, and that constructor must explicitly initialize their NoDefault member.The NoDefault type may not be used as the element type for a dynamically allocated array.Statically allocated arrays of type NoDefault must provide an explicit initializer for each element.If we have a container such as vector that holds NoDefault objects, we cannot use the constructor that takes a size without also supplying an element initializer.

Using the Default Constructor

The declaration of myobj compiles without complaint. However, when we try to use myobj
// oops! declares a function, not an object
Sales_item myobj();
the compiler complains that we cannot apply member access notation to a function! The problem is that our definition of myobj is interpreted by the compiler as a declaration of a function taking no parameters and returning an object of type Sales_itemhardly what we intended! The correct way to define an object using the default constructor is to leave off the trailing, empty parentheses:
Sales_item myobj(); // ok: but defines a function, not an object
if (myobj.same_isbn(Primer_3rd_ed)) // error: myobj is a function
On the other hand, this code is fine:
// ok: defines a class object ...
Sales_item myobj;
Here we create and value-initialize a Sales_item object and to use it to initialize myobj. The compiler value-initializes a Sales_item by running its default constructor.
// ok: create an unnamed, empty Sales_itemand use to initialize myobj
Sales_item myobj = Sales_item();
Exercises Section 12.4.3
Exercise 12.27:Which, if any, of the following statements are untrue? Why?
- A class must provide at least one constructor.A default constructor is a constructor with no parameters for its parameter list.If there are no meaningful default values for a class, the class should not provide a default constructor.If a class does not define a default constructor, the compiler generates one automatically, initializing each data member to the default value of its associated type.
12.4.4. Implicit Class-Type Conversions
As we saw in Section 5.12 (p. 178), the language defines several automatic conversions among the built-in types. We can also define how to implicitly convert an object from another type to our class type or to convert from our class type to another type. We'll see in Section 14.9 (p. 535) how to define conversions from a class type to another type. To define an implicit conversion to a class type, we need to define an appropriate constructor.

Each of these constructors defines an implicit conversion. Accordingly, we can use a string or an istream where an object of type Sales_item is expected:
class Sales_item {
public:
// default argument for book is the empty string
Sales_item(const std::string &book = "):
isbn(book), units_sold(0), revenue(0.0) { }
Sales_item(std::istream &is);
// as before
};
This program uses an object of type string as the argument to the Sales_item same_isbn function. That function expects a Sales_item object as its argument. The compiler uses the Sales_item constructor that takes a string to generate a new Sales_item object from null_book. That newly generated (temporary) Sales_item is passed to same_isbn.Whether this behavior is desired depends on how we think our users will use the conversion. In this case, it might be a good idea. The string in book probably represents a nonexistent ISBN, and the call to same_isbn can detect whether the Sales_item in item represents a null Sales_item. On the other hand, our user might have mistakenly called same_isbn on null_book.More problematic is the conversion from istream to Sales_item:
string null_book = "9-999-99999-9";
// ok: builds a Sales_itemwith 0 units_soldand revenue from
// and isbn equal to null_book
item.same_isbn(null_book);
This code implicitly converts cin to a Sales_item. This conversion executes the Sales_item constructor that takes an istream. That constructor creates a (temporary) Sales_item object by reading the standard input. That object is then passed to same_isbn.This Sales_item object is a temporary (Section 7.3.2, p. 247). We have no access to it once same_isbn finishes. Effectively, we have constructed an object that is discarded after the test is complete. This behavior is almost surely a mistake.
// ok: uses the Sales_item istream constructor to build an object
// to pass to same_isbn
item.same_isbn(cin);
Supressing Implicit Conversions Defined by Constructors
We can prevent the use of a constructor in a context that requries an implicit conversion by declaring the constructor explicit:
The explicit keyword is used only on the constructor declaration inside the class. It is not repeated on a definition made outside the class body:
class Sales_item {
public:
// default argument for book is the empty string
explicit Sales_item(const std::string &book = "):
isbn(book), units_sold(0), revenue(0.0) { }
explicit Sales_item(std::istream &is);
// as before
};
Now, neither constructor can be used to implicitly create a Sales_item object. Neither of our previous uses will compile:
// error: explicit allowed only on constructor declaration in class header
explicit Sales_item::Sales_item(istream& is)
{
is >> *this; // uses Sales_iteminput operator to read the members
}
item.same_isbn(null_book); // error: string constructor is explicit
item.same_isbn(cin); // error: istream constructor is explicit

Explicitly Using Constructors for Conversions
An explicit constructor can be used to generate a conversion as long as we do so explicitly:
In this code, we create a Sales_item from null_book. Even though the constructor is explicit, this usage is allowed. Making a constructor explicit turns off only the use of the constructor implicitly. Any constructor can be used to explicitly create a temporary object.
string null_book = "9-999-99999-9";
// ok: builds a Sales_itemwith 0 units_soldand revenue from
// and isbn equal to null_book
item.same_isbn(Sales_item(null_book));

Exercises Section 12.4.4
Exercise 12.28:Explain whether the Sales_item constructor that takes a string should be explicit. What would be the benefits of making the constructor explicit? What would be the drawbacks?Exercise 12.29:Explain what operations happen during the following definitions:
Exercise 12.30:Compile the following code:
string null_isbn = "9-999-99999-9";
Sales_item null1(null_isbn);
Sales_item null("9-999-99999-9");
What can we infer about the vector constructors based on the error on the second call to f? If the call succeeded, then what would you conclude?
f(const vector<int>&);
int main() {
vector<int> v2;
f(v2); // should be ok
f(42); // should be an error
return 0;
}
12.4.5. Explicit Initialization of Class Members
Although most objects are initialized by running an appropriate constructor, it is possible to initialize the data members of simple nonabstract classes directly. Members of classes that define no constructors and all of whose data members are public may be initialized in the same way that we initialize array elements:
The initializers are used in the declaration order of the data members. The following, for example, is an error because ival is declared before ptr:
struct Data {
int ival;
char *ptr;
};
// val1.ival = 0; val1.ptr = 0
Data val1 = { 0, 0 };
// val2.ival = 1024;
// val2.ptr = "Anna Livia Plurabelle"
Data val2 = { 1024, "Anna Livia Plurabelle" };
This form of initialization is inherited from C and is supported for compatibility with C programs. There are three significant drawbacks to explicitly initializing the members of an object of class type:
// error: can't use "Anna Livia Plurabelle" to initialize the int ival
Data val2 = { "Anna Livia Plurabelle" , 1024 };
- It requires that all the data members of the class be public.It puts the burden on the programmer to initialize every member of every object. Such initialization is tedious and error-prone because it is easy to forget an initializer or to supply an inappropriate initializer.If a member is added or removed, all initializations have to be found and updated correctly.

Exercises Section 12.4.5
Exercise 12.31:The data members of pair are public, yet this code doesn't compile. Why?
pair<int, int> p2 = {0, 42}; // doesn't compile, why?