Select Page

Learning how to use constructors is the first and the most crucial step to mastering the object-oriented programming (OOP) paradigm. 

In this article, we’ll cover the ins and outs of constructors in the C++ programming language.

The Object-oriented Programming Paradigm

Object-oriented programming is the most popular programming paradigm. It’s motivated by the “divide and conquer” problem-solving strategy — the breaking down of a complex system into smaller subsystems. Subsystems that are reduced in size become less complex and more easily solvable. However — when combined —  simple, individual units can be used to build incredibly complex programs.

Why Do We Use OOP?

The primary benefit of object-oriented programming is that it simplifies how we build software. OOP emphasizes modularity, as reflected by the elegant and succinct code commonly associated with OOP languages. Code that developers can easily read and understand translates into a program that’s easy to maintain and extend as it grows in size. This allows programmers to think about software development in a more abstract, high-level way, without getting caught up in the nitty-gritty of implementation.

What Are Objects?

Object-oriented programming works by structuring programs as objects. Objects are entities that represent something from real life, such as a dog or a person. Much like dogs or people, the objects representing them have their defining attributes and behaviors. For instance, a dog may have a name and a breed and may be able to bark and wag its tail. We can encode all of this information with code into an object via the object’s class.

Below, we’ll focus on the constructor, a type of method specific to the OOP paradigm. Although we make use of C++ programming to illustrate constructors, the principles underlying our examples are common to most OOP languages. 

C++ Constructors

Constructors are methods that are automatically executed every time you create an object. The purpose of a constructor is to construct an object and assign values to the object’s members. A constructor takes the same name as the class to which it belongs, and does not return any values. Let’s take a look at a C++ constructor example:

#include <iostream>
#include <string>

class Dog {
    public:
        std::string name;
        bool drools;
        void barks() {
        std::cout << "Woof Woof\n";
            };
    Dog(std::string dog_name, bool dog_drools){ // this is the constructor
        name = dog_name;
        drools = dog_drools;
    }
};

The above is an example of a parameterized constructor method. As their name suggests, parameterized constructors accept parameters that they use to initialize an object’s member variables. In our example, the constructor receives std::string dog_name and bool dog_drools. The values of these parameters are used to initialize the class attributes name and drools. 

Let’s see how this works in a concrete example:

int main(int argc, const char * argv[]) {
    Dog spot("Spot", true); // we’re calling the constructor with 2 parameters
    std::cout << "Name: " << spot.name << std::endl;
    return 0;
}

# Running this program produces the following output:
Name: Spot

In the program above, we create an object spot of the class Dog. In the line where we create the object, the constructor accepts the values “Spot” and “true” and assigns them to spot.name and spot.drools.

The way our constructor above is defined requires us to always provide exactly two arguments when we create an object. In case we do not provide any arguments to the constructor, the compiler will print an error because the class does not have instructions for constructing unparameterized objects. However, there’s an easy fix for this.

Default C++ Constructor

CPP constructors can also be unparameterized; that is, they can create objects without being given arguments. Such constructors are called default constructors. The “default” in “default constructor” refers to the fact that the constructor assigns default arguments when none are provided by the user. Default values may be specified by the user or inferred by the compiler. In both cases, we’re talking about default constructors. 

We’ll explain these two types of default constructor below.

Default C++ Constructor with Default Arguments

Like all functions and methods, a constructor can also have default arguments. These are the values that the constructor uses to initialize member values if the user does not provide any custom arguments. 

The following class definition extends our existing constructor to accept default arguments:

class Dog {
    public:
        std::string name;
        bool drools;
        void barks() {
        std::cout << "Woof Woof\n";
            };
   Dog(std::string dog_name = "No name", bool dog_drools = false){
        name = dog_name;
        drools = dog_drools;
    }
};

Note the changes in the highlighted line: dog_name and dog_drools now take up “No name” and “false” as their default values. Now we can still provide custom arguments, but also create an object without them. Let’s make sure that this works as intended by creating another Dog object, this time with an empty constructor:

int main(int argc, const char * argv[]) {
    Dog spot("Spot", true);
    Dog lulu;    // we’re calling the default constructor here
    std::cout << "Name: " << spot.name << std::endl;
    std::cout << "Name: " << lulu.name << std::endl;
    return 0;
}
# Output:
Name: Spot
Name: No name

We created the object lulu without passing any arguments to the class’s constructor. Printing lulu.name shows that the constructor assigned it the default value of “No name.”

Default C++ Constructor with No Parameters

The code above shows an example of a default C++ constructor with default parameter values. There’s also a constructor that does not have any parameters at all. This is the constructor that your compiler automatically creates when the user does not provide one to the class. Here’s how it looks:

class Dog {
    public:
        std::string name;
        bool drools;
        void barks() {
        std::cout << "Woof Woof\n";
            };        // we deleted the constructor method’s body that used to be here
};

int main(int argc, const char * argv[]) {    Dog lulu; // we’re able to create an object despite not having defined a               // constructor for the class    std::cout << "Name: " << lulu.name << std::endl;
    std::cout << "Drools: " << lulu.drools << std::endl;
    return 0;
}

# Output:
Name: Drools: 0 

Notice how we’re still able to create an object despite removing the class’s constructor. This is due to the implicitly defined default constructor, which does not take any parameters and has an empty body. If no user-defined constructor exists, the default constructor is automatically created by the compiler by inferring default values from the members’ types. 

Below, we’ll edit our class with an explicit definition of the same constructor that the compiler would have otherwise created for us, just to show that they’re indeed equal. As we mentioned, this constructor has an empty body and no parameters, so this should be easy.

class Dog {
    public:
        std::string name;
        bool drools;
        void barks() {
        std::cout << "Woof Woof\n";
            };
        Dog::Dog(){} // this is the empty constructor that the compiler would                     // have created for us
};

int main(int argc, const char * argv[]) {
    Dog lulu;
    std::cout << "Name: " << lulu.name << std::endl;
    std::cout << "Drools: " << lulu.drools << std::endl;
    return 0;
}
# Output:
Name: Drools: 0 

When checking the output of the two programs above, you may have noticed that in both cases the default constructor assigned lulu.name an empty string (“”) and lulu.drools a value of 0 (or false). These default values make sense — perhaps more so for the string than for the boolean. However, depending on your compiler, they may differ. The following section will explain why.

Properly Defining a C++ Constructor

When you create an object, the computer allocates a block of memory to where the object’s members are stored. Ideally, we want each member variable to be initialized with its default values. However, rather than initializing a variable with the appropriate default value, an ill-defined constructor can cause your compiler to assign it only a pointer to a location in the memory. This memory block can still contain a random value from a computation that the computer completed in the past.

These values are called “garbage values” and they can cause programs to behave in unintended ways. Because of this, it’s advisable to not rely on the implicitly defined default constructor. Instead, it’s a better idea to define a custom constructor with default arguments, in order for the constructor to fully initialize all of the object’s members. The implicit default constructor may be convenient, but you get what you pay for.

However, a poorly defined custom constructor can likewise jeopardize a program’s success. When it comes to constructors, the single most important rule is that they should always fully initialize an object. This means that every member of an object needs to be initialized as soon as that object is created. The reasoning behind this is simple: An object that’s fully initialized is fully usable upon creation.

For example, if you create a class whose members are objects of other classes, the compiler can easily create a default constructor for the parent class by initializing every member with its corresponding class’s default constructor. Let’s take a look at a concrete example:

class Dog {
    public:
        std::string name;
        bool drools;
        Owner owner;
        Breed breed;
};

The class definition above includes two members, one belonging to the Owner and one to the Breed class. We already know that std::string and bool types have proper default constructors, so if we define default constructors for the two new types, we can be confident that the resulting default constructor of the parent class will not behave unexpectedly.

Externally Defined C++ Class Constructor

In our program, we have been defining constructors inside their class, but constructors are often defined externally. Although our program is too short to showcase the benefits of external constructor definition, it’s often done to improve readability. For example, if your many classes are declared in a headers file, you may want to define the constructors in the source where you can quickly look up their definitions.

Below is an example of our simple class with an externally defined constructor:

class Dog {
    public:
        std::string name;
        bool drools;
        void barks() {
        std::cout << "Woof Woof\n";
            };
        Dog(); // when a constructor is defined externally, it still needs to               // be declared
};

// this is an external definition of a constructor
Dog::Dog(){} 
int main(int argc, const char * argv[]) {
...
}

Note the comments regarding declaration and definition. A constructor must be declared within the class even if it’s defined elsewhere.

As you can see, externally defined constructors can greatly improve a program’s readability. This is especially true for large programs that are split into multiple files. Apart from improved readability, this practice may also shorten compilation time.

Become a C++ Developer

In this article, we showed you how to effectively use a constructor in C++. 
If you’re ready to take your C++ game to the next level, enroll in our expert-taught C++ Nanodegree to get into advanced topics such as memory management, concurrency and the object-oriented programming paradigm.

Start Learning