Skip to main content

Section 16.6 Constructors

Subsection 16.6.1 What is a Constructor?

We have yet to clearly describe this member of the Point class:
Point(double x, double y) {
    m_x = x;
    m_y = y;
}
This is a special type of member function called a constructor. A constructor for a class is responsible for initializing the member variables of new objects of that type. Any time we make a Point, the constructor will make sure m_x and m_y are set.
A constructor always has the same name as the class and it lacks a return type - the return type is implied to be the class type. When you use a Point constructor, a new Point is always created and it is what gets automatically returned:
Listing 16.6.1.
// A Point constructor
Point(double x, double y) {
    // A new point is created when we enter the constructor
    m_x = x;
    m_y = y;
    // implicit return of the new point
}
Constructors can be called like other functions:
Point p1 = Point(3, 4);
But usually, we call a constructor using this shorter syntax:
Point p1(3, 4);
There is a subtle difference between the two. The first says β€œCreate a Point with 3 and 4. Copy that point into p1.” The second says β€œCreate Point p1 with 3 and 4.” and thus skips making an anonymous (nameless) Point that then gets copied into p1 and then throws away the anonymous Point. Either syntax will produce the same results, but you should recognize that Point(3, 4) creates an anonymous point.
Listing 16.6.2.

Subsection 16.6.2 Multiple Constructors

We can overload the constructor of a class in the same way we can overload other functions. Say we want to be able to easily create points at (0, 0) without saying Point p1(0, 0);. We could write a no argument constructor Point() that automatically set x and y to 0. Or if we wanted (for some unknown reason) to be able to easily make points with the same x and y value we might write Point(double sharedCoordinate);:
Listing 16.6.3.
// A two-argument constructor
Point(double x, double y) {
    m_x = x;
    m_y = y;
}
// A  no-argument constructor
Point() {
    m_x = 0;
    m_y = 0;
}
// A one-argument constructor
Point(double sharedCoordinate) {
    m_x = sharedCoordinate;
    m_y = sharedCoordinate;
}
Now we can create points in a variety of ways:
Point p1(3, 4); // Point at (3, 4)
Point p2; // Point at (0, 0)
Point p3(5); // Point at (5, 5)
In this case, the compiler will automatically pick the constructor that best matches the arguments we give it. If we give it two arguments, it will use the two-argument constructor. If we give it one argument, it will use the one-argument constructor.
The no-argument constructor is a special case. To call it we could say Point p2 = Point();. But to write the short version, we have to leave off the parentheses: Point p2;. This somewhat annoying special case is a quirk of the syntax of C++. Point p2(); looks like it defines a function called p2 that returns a Point.

Note 16.6.1.

Remember that declaring a variable of a class automatically calls the no argument constructor unless we use (...) after the variable name to call a different constructor. Because it is called automatically when we don’t use (...), it is also known as the default constructor.
If you fail to define any constructors, a default one is automatically provided by the compiler, but it doesn’t actually initialize any data - you should always define constructors for every class you write.

Subsection 16.6.3 Default Values

It is possible to use default values for the parameters of a constructor just like any other function:
Point(double x = 0, double y = 0) {...
But if we know there are default values that should always be applied, we might want to specify those directly in the class itself. Try running this Codelens sample and notice that the member variables are both set to 0 automatically at the start of each constructor. This is because the variables themselves are given default values of 0 on lines 15 and 16:
Listing 16.6.4.
Assigning default values at the class level can prevent having to do so in multiple constructors. Say we want to provide many different constructors for a Time class. We could either write this:
class Time {
public:
    // Fully specified time
    Time(int hours, int minutes, int seconds) {
        m_hours = hours;
        m_minutes = minutes;
        m_seconds = seconds;
    }

    // Assume seconds is 0
    Time(int hours, int minutes) {
        m_hours = hours;
        m_minutes = minutes;
        m_seconds = 0;
    }

    // Assume minutes and seconds are 0
    Time(int hours) {
        m_hours = hours;
        m_minutes = 0;
        m_seconds = 0;
    }

    // Default constructor
    Time() {
        m_hours = 0;
        m_minutes = 0;
        m_seconds = 0;
    }
private:
    int m_hours, m_minutes, m_seconds;
};
Or we would use default values for the members and then simplify the code to this:
class Time {
public:
    // Fully specified time
    Time(int hours, int minutes, int seconds) {
        m_hours = hours;
        m_minutes = minutes;
        m_seconds = seconds;
    }

    // Assume seconds is 0
    Time(int hours, int minutes) {
        m_hours = hours;
        m_minutes = minutes;
    }

    // Assume minutes and seconds are 0
    Time(int hours) {
        m_hours = hours;
    }

    // Default constructor
    Time() {}
private:
    int m_hours = 0;
    int m_minutes = 0;
    int m_seconds = 0;
};

Note 16.6.2.

Every constructor should make sure that every member variable has some reasonable starting value. If the constructor does not take a value to use for a given variable, a default value should be assigned either in the constructor or where the member variable is declared .

Subsection 16.6.4 Making Copies

As with structs, we can copy one object into another using the assignment operator =. Doing so results in two distinct objects that happen to have the same data:
Point p1(3, 4);
Point p2 = p1; // copy p1 into p2
p2 is an exact copy of p1
Figure 16.6.5. A memory diagram of p1 and p2
There are ways to customize what the assignment operator does with objects. Those techniques are important for objects that are in charge of managing resources. But for the simple objects we are working with for now, the default behavior of the assignment operator works just fine.

Checkpoint 16.6.1.

Checkpoint 16.6.2.

Which statment is false about constructors?
  • They initialize the instance variables of an object.
  • Incorrect! This statement is true!
  • They have the same name as the class.
  • Incorrect! This statement is true!
  • Every class should only have one constructor.
  • Correct! It is often useful to have multiple constructors.
  • They have the same name as the class, no return type and unchanged parameters.
  • Incorrect! This statement is true!

Checkpoint 16.6.3.

The type Dog has these constructors:
Dog(string name);
Dog(string name, int age);
Dog();
Which are valid ways to construct a Dog using the no-arg constructor?
  • Dog d;
  • Dog d = Dog();
  • This is not a preferred method, but it does work. This calls the no-argument constructor and copies the result into d
  • Dog d();
  • Incorrect! You do not use the parentheses after the variable name when using the no-arg constructor.

Checkpoint 16.6.4.

The type Dog has these constructors:
Dog(string name);
Dog(string name, int age);
Dog();
Which are valid ways to construct a Dog object?
  • Dog d;
  • Dog d = Dog("Spot");
  • This is not a preferred method, but it does work. This calls the no-argument constructor and copies the result into d.
  • Dog d("Spot");
  • This works, but is not preferred.This calls the no-argument constructor and copies the result into d.
  • Dog d(3, "Rover");
  • Incorrect! The order of the parameters is wrong.

Checkpoint 16.6.5.

Which is the most accurate statement?
  • Every constructor should make sure every member variable is initialized.
  • Each constructor should only initialize the member variables it accepts values for.
  • Only one constructor should be provided for each class - one that has values for every member variable.

Checkpoint 16.6.6.

Let’s write two constructors for Student. One with no arguments and one with arguments. Put the necessary blocks of code in the correct order with the no-argument constructor first.
You have attempted of activities on this page.