We have seen initializing structs and classes using aggregate initialization where we provide a list of initialization values and those gets assigned to data members in the order as they are defined.

Aggregate initialization is used when data members are public. But when data members are private, we can’t use this method. For example, compiling following program yields error.

#include <iostream>
 
class Point
{
    int m_x{};
    int m_y{};
 
public:
    void print()
    {
        std::cout << "{ " << m_x << ", " << m_y << "}\n";
    }
};
 
int main(int argc, char const *argv[])
{
    Point point{1, 2};
    return 0;
}

If we notice the error, we should see that there is no matching Function call for Point(1, 2).

Basically, compiler is looking for a constructor.

As an aside

Why aggregate initialization can’t be used for class types with private members?

Firstly, Aggregate initialization requires to know the implementation of the class such that how many data members are there and what order they are defined. This is just opposite of existence of private data members of hiding them.

Secondly, if class has some invariant, we’d be relying on the user to initialize the class without violating the invariant.

With constructors, it becomes classes implementer responsibility to make sure no invariant is violated and data is hidden from outer word as constructor will initialize the data members.

Constructor

Constructors are the special member function which are called automatically when the non-aggregate class type object is created. Constructors are used to initialize the object when the object is created. Objects are created first where compiler allocate memory for them and then calls the constructors to initialize them.

A constructors usually perform two functions:

  1. Initialize member functions (via member initialization list)
  2. Calling setup functions such as opening file, creating network connection, input validations etc.

Note

Aggregators can’t have constructors. If they have, they are not aggregators.

Naming constructors

Naming constructors have following rules:

  1. They should have same name as class.
  2. They don’t have return type. Not even void return type.

Constructors are typically part of public interface, they are usually public.

Example

We can add a simple constructor to our program given above.

#include <iostream>
 
class Point
{
    int m_x{};
    int m_y{};
 
public:
    Point(int x, int y)
    {
        std::cout << "Constructor Point(" << x << ", " << y << ")\n";
    }
 
    void print() const
    {
        std::cout << "{" << m_x << ", " << m_y << "}\n";
    }
};
 
int main(int argc, char const *argv[])
{
    const Point point{1, 2};
 
    point.print();
    return 0;
}

Now, our program should compile and when we run the program, we should see the output below:

Constructor Point(1, 2)
{0, 0}

When the class object is created, compiler looks for constructor Point(int, int) and it gets the constructor.

Constructors are non-const

If we notice, constructor Point(int,int) is a non-const and we have created a const object and calling constructor still works (calling non-const member functions on const objects do not work). This works because const does not get applied to the object under construction, it only comes to effect after the construction ends and constructor is called during the construction.

References

  1. https://www.learncpp.com/cpp-tutorial/introduction-to-constructors/