Lei Mao bio photo

Lei Mao

Machine Learning, Artificial Intelligence, Computer Science.

Twitter Facebook LinkedIn GitHub   G. Scholar E-Mail RSS

Introduction

In C programming, we used to initialize objects using assignment operator = and passing arguments at the right side of the assignment operator = to invoke the constructor.


In C++ programming, this feature has been kept. However, it is not recommended to use assignment operator = to initialize objects, especially for the objects whose constructor only takes one single argument, because it is often confusing to the readers who first read the code. For example, we initialize A typed variable a using A a = 1. At first glance, the reader would wonder why an integer could be “assigned” to an A typed variable. But actually it is calling the constructor.


Instead of using the confusing assignment operator =, we use parentheses () and the more recommended curly braces {} to. Especially for curly braces {}, it unambiguously means that it will invoke the constructor.


In C++ programming, initialization using assignment operator = is called implicit initialization, and initialization using parentheses () and curly braces {} is called explicit initialization.


In this blog post, I will give some concrete examples of implicit initialization and explicit initialization.

Examples

We use explicit specifier for constructor to specify that the constructor would only be invoked explicitly using parentheses () and curly braces {}. The compiler will raise errors if the user tries to invoke an explicit constructor using an assignment operator =.

/*
 * explicit_constructor.cpp
 */
#include <iostream>

class Implicit
{
public:
    Implicit(int v) : mV{v}
    {
        std::cout << "Implicit constructor called." << std::endl;
    }
private:
    int mV;
};

class Explicit
{
public:
    // Use explicit specifier for explicit constructor
    explicit Explicit(int v) : mV{v}
    {
        std::cout << "Explicit constructor called." << std::endl;
    }
private:
    int mV;
};

int main()
{
    // Explicit initialization
    Implicit implicit1{1};
    Implicit implicit2(1);
    // Non-explicit initializations might be confusing to readers
    Implicit implicit3 = {1};
    Implicit implicit4 = 1;
    // Default copy constructor called
    Implicit implicit5 = implicit1;

    // Explicit initialization
    Explicit explicit1{1};
    Explicit explicit2(1);
    // explicit specifier prevents non-explicit initialization
    // Explicit explicit3 = {1}; // Invalid
    // Explicit explicit4 = 1; // Invalid
    // Default copy constructor called
    Explicit explicit5 = explicit1;
}

To compile the program, please run the following command in the terminal.

$ g++ explicit_constructor.cpp -o explicit_constructor --std=c++11

The expected output of the program would be as follows.

$ ./explicit_constructor 
Implicit constructor called.
Implicit constructor called.
Implicit constructor called.
Implicit constructor called.
Explicit constructor called.
Explicit constructor called.

Conclusions

Using explicit specifier and {} as much as possible for constructor and initialization is always the best practice.