Class Constructors

Learn about a special type of function we can add to our classes, which we can use to control how our objects get created.
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

3D art showing a blacksmith character
Ryan McCombe
Ryan McCombe
Edited

A constructor is a special type of function on a class. A constructor gets called automatically when an object of that class is created. Because of this, it is the ideal place to put any sort of initialisation.

To let C++ know that a function is a constructor, it needs to have two properties:

  • It has the same name as the class itself. For example, if we have a Monster class, any function that is a constructor is also called Monster
  • Constructors do not specify any return type (not even void)

We can see an example of a constructor below:

class Monster {
public:
  Monster() {
    Health = 150;
  }

  int GetHealth() { return Health; }

private:
  int Health;
};

We should ensure that the constructors we create are in the public: section of our classes.

This will not be enforced by the compiler, as there are advanced scenarios where a non-public constructor is useful, but those are beyond the scope of this course. Therefore, for now, we just have to be mindful to always ensure our constructors are public.

Test your Knowledge

What is a constructor?

Test your Knowledge

Imagine we had this simple class:

class Car {};

What would be a valid prototype for a constructor for this class?

Using Constructors in C++ Classes

Above, we added a constructor to our class. As that constructor does not require any arguments, it is considered the default constructor.

This constructor will be called for us automatically, without making any changes to the code we had previously written. A simple Monster Harry; statement will cause this constructor to be called.

The following code will log out `"Ready for battle!"

#include <iostream>
using namespace std;

class Monster {
public:
  Monster() {
    cout << "Ready for battle!";
  }
};

int main() {
  Monster Harry;
}

However, because constructors are functions, they can also accept arguments:

class Monster {
public:
  Monster(int InitialHealth = 150) {
    Health = InitialHealth;
    cout << "Ready for battle!";
  }

  int GetHealth() { return Health; }

private:
  int Health;
};

With this constructor's only argument being optional, it will still act as a default constructor. That means creating a Monster using the code Monster Harry; will still call this constructor.

However, we now have the ability to give our Monster objects an initial Health value when creating them.

Just like we were able to pass values to the int data type to set an initial value:

int Level { 5 };

We can now do the same with our Monster data type

// This still works
Monster Harry;
Harry.GetHealth(); // 150

// We can now pass values to the constructor
Monster Julia { 300 };
Julia.GetHealth(); // 300

Class Constructors with Multiple Arguments

Like with any other function, constructors can have multiple arguments. For example:

class Monster {
public:
  Monster(
    int InitialHealth = 150,
    bool isInitiallyEnraged = false
  ) {
    Health = InitialHealth;
    isEnraged = isInitiallyEnraged;
  }

private:
  int Health;
  bool isEnraged;
}

// 150 health, not enraged
Monster SmallCalmGoblin;

// 300 health, enraged
Monster BigAngryGoblin { 300, true };

Adding Multiple Constructors to C++ Classes

We can have multiple constructors in our classes. The only requirement is that each constructor must have sufficiently unique signatures.

For example, we can have a constructor that accepts a boolean, and a different one that accepts an integer.

In this example, we have 4 constructors:

class Monster {
public:
  Monster() {
    cout << "Using default constructor!";
  }
  
  Monster(int InitialHealth) {
    Health = InitialHealth;
  }

  Monster(bool isInitiallyEnraged) {
    isEnraged = isInitiallyEnraged;
  }
  
  Monster(int InitialHealth, bool isInitiallyEnraged) {
    Health = InitialHealth;
    isEnraged = isInitiallyEnraged;
  }

private:
  int Health { 150 };
  bool isEnraged { false };
};

// Uses first constructor
Monster DefaultGoblin;

// Uses second constructor
Monster BigGoblin { 400 };

// Uses third constructor
Monster AngryGoblin { true };

// Uses fourth constructor
Monster BigAngryGoblin { 400, true };

As long as the parameter types are sufficiently unique, we can create as many constructors as we want.

The specific constraint is that, any time we create an object, it must be unambiguously clear which constructor is to be used.

That is, when the compiler receives a statement to create an object, there must be only one constructor that can handle the arguments that were provided in that statement.

This means that the following class would be problematic, as there are some argument combinations that could be handled by either constructor:

class Monster {
public:
  Monster(int Level, int InitialHealth = 150) {
    Level = Level;
    Health = InitialHealth;
  }

  Monster(int InitialHealth) {
    Health = InitialHealth;
  }

private:
  int Level { 1 };
  int Health { 150 };
};

Whilst the above code would compile, issues can arise when we try to construct objects of that class. For example, were we to try to create a Monster in the following way:

Monster Harry { 100 };

It is not clear which constructor to call - either could be used. We might be using the top constructor to set Level to 100, or the bottom constructor to set Health to 100.

Therefore, our code would not compile, with an error message such as "call to constructor of Monster is ambiguous".

What is the maximum number of constructors a class can have?

Test your Knowledge

Consider this code:

class Car {
public:
  Car(int Speed, int HorsePower, int Torque) {}
  Car(int Speed, int HorsePower) {}
  Car(int Speed) {}
  Car(int Speed = 50) {}
};

Car A { 100, 200, 300 };
Car B { 100, 200 };
Car C { 100 };
Car D;

Which of the above Cars cannot be constructed?

Implementing Constructors Separately

In a previous lesson, we gave an example of how functions can be implemented outside of the body of the class, with the Monster::TakeDamage example. This also applies to constructors:

class Monster {
public:
  // The prototype
  Monster(int InitialHealth);

private:
  int Health { 150 };
};

// The implementation
Monster::Monster(int InitialHealth) {
  Health = InitialHealth;
}

Destructors

In addition to constructors, our class can also define a destructor.

A destructor returns nothing, has no parameters, and has the same name as the class, but with a ~ prepended. We’ll see some exceptions later but, in almost all cases, a destructor should be public:

class SomeClass {
public:
  // Destructor
  ~SomeClass(){
    // ...
  }
};

A destructor automatically runs when our objects are destroyed. This makes them the ideal place to do any cleanup we might need.

The object life cycle is something that will become more important as we learn more, and we’ll discover ways to control it. For now, we can note that the objects we’re creating are automatically destroyed when the scope they were created in is destroyed.

This means objects created in the global scope will be destroyed just before our program ends.

For objects created in a block scope, such as between the { and } of a function, the object will be destroyed when execution reaches the end of the block.

#include <iostream>
using namespace std;

class Monster {
public:
  // Constructor
  Monster(){
    cout << "Monster Created" << endl;
  }

  // Destructor
  ~Monster(){
    cout << "Monster Destroyed" << endl;
  }
};

void SomeFunction(){
  Monster Goblin;
}

int main(){
  cout << "Hello World" << endl;
  SomeFunction();
  cout << "Goodbye!";
}
Hello World
Monster Created
Monster Destroyed
Goodbye!

Chapter Summary

In this chapter, we introduced the concept of classes. Classes are the main tool we use to implement the 4 key principles of object oriented programming. We also saw what the first two of those principles are:

Abstraction

Abstraction is the ability to group our objects into categories - conceptual models of what it means to be that type of thing. We grouped all individual monsters under the category, or class, of Monster.

The Monster class is the abstraction - each individual Monster object is an example, or an instance, of a Monster.

Encapsulation

We also looked at how we can use encapsulation to restrict access to parts of our objects, making them easier to use and maintain.

Encapsulation is the ability to hide implementation details under the hood.

Our objects expose only the controls that we intend to be used by external code - code that is not part of our class.

We make these things public. Implementation details - things the outside world doesn't need to know about, are made private.

Inheritance and Polymorphism

In the next chapter, we will finish out our initial exploration of object oriented programming by progressing our design further. We will encounter some new problems, and see how we can use the other two big ideas of object oriented programming.

Those principles are inheritance and polymorphism, and will allow us to create more and more complex software in an effective and organised way.

Was this lesson useful?

Edit History

  • ‚ÄĒ Added section on destructors

  • ‚ÄĒ First Published

Ryan McCombe
Ryan McCombe
Edited
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Classes
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access!

This course includes:

  • 66 Lessons
  • Over 200 Quiz Questions
  • Capstone Project
  • Regularly Updated
  • Help and FAQ
Next Lesson

C++ Inheritance - Why its Useful

An introduction to the problem inheritance is designed to help us solve, and how we can use it to manage future complexity.
3D art depicting a battle scene
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved