Constructors – Deeper Dive

Constructors are special functions that are called when an abject is instantiated. They have the same name as the class and have no return type.

Constructors look like the following.

Cube();
Cube(int length, int width, int height);
One or all of your constructor parameters can be a default parameter. If all the parameters are defaulted, you can’t have a no-args constructor.
Cube(int length = 0, int width = 0, int height = 0);
There are two ways of initializing a class. Member-wise copy and initializer list. Initializer list is preferred because it eliminates unnecessary copies.
Member-wise copy is a two step process, the object is created then the data is copied to members.
ClassName::ClassName(int value) {
    m_member = value;
}

Initializer list is a one step process, the members are initialized when the object is created.

cClassName::ClassName(int value) : m_member {value} {}

Note that the initializer list has an empty body in this case. If there is any specific set-up you need to do after the object has been initialized, it can go in the body.

Problems can occur with implicit conversions. Consider the following code:
#include <iostream>

class Square {
public:
    Square(int side) : m_side {side} {}

    int area() const {
        return m_side * m_side;
    }

private:
    int m_side;
};

bool first_is_greater(const Square & s1, const Square &s2) {
    return s1.area() > s2.area();
}

int main() {

    Square square {10};
    bool result = first_is_greater(square, 20);
    std::cout << "Result: " << result << std::endl;

    return 0;
}
The first_is_greater call is perfectly valid because the compiler can do an implicit conversion from an int to a square as the square constructor takes a single int as a parameter. This can be prevented by marking the constructor as explicit.

explicit Square(int side) : m_side {side} {}
When you have multiple constructors, you can delegate from one to another. Consider the following code where the one-arg constructor calls the two-arg constructor before it goes into its body:
#include <iostream>
#include <string>

class Square {
public:
    Square(int side) : Square {side, "black"} {}
    Square(int side, const std::string &colour) : m_side {side}, m_colour {colour} {}

    int area() const {
        return m_side * m_side;
    }

private:
    int m_side;
    std::string m_colour;
};
You cannot call the other constructor in the body as this creates a local copy.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.