Class constructor

What is the class constructor?

The constructor is a special method that is automatically called when an object of a class is created. It is where we assign the member variables with default values and initialize the object.

Let us consider we define a class named Character that possesses a pointer of type Character, named _target, as a member. Now, if we create an object of that class, its member _target will, by default, contain some random address, because it is not assigned with any value. Later, if we did not previously change the value of the member _target, because it does not point to an invalid address (0), the object might think it actually points to an existing object. It may then try to access the pointed object, which does not exist, and the program will likely crash. We can avoid that kind of situation by setting the member pointers to 0 at the creation of the objects of a class, by doing it inside its constructor.

Defining the constructor

The constructor is declared and defined the same way a method is, except that it must be named after the class (Same name as the class) and it does not have a return type (Not even void). It may, however, take arguments. When no constructor is declared, the class is assigned with a default constructor which does nothing. The default constructor takes no argument and exist only if no constructor has been defined.

Character.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef CHARACTER_HPP #define CHARACTER_HPP #include <iostream> class Character { public: // Declares the constructor, taking a constant reference to a string as argument. Character(const std::string & name); const std::string & getName() const; private: std::string _name; }; #endif

Character.cpp

1
2
3
4
5
6
7
8
9
10
11
12
#include "Character.hpp" // Assigns the string received in argument to the member _name. Character::Character(std::string const& name) { _name = name; } const std::string & Character::getName() const { return _name; }

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
#include "Character.hpp" int main() { Character character; /* Error: The constructor of the class Character requires an argument. */ return 0; }

In the example above, there is an error, because we gave no argument to the constructor of Character while it expected to receive a string as parameter. We give arguments to a constructor by putting them between parentheses after the name of the object when we define it (As we would do with a function).

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "Character.hpp" int main() { // Gives the string "Lancelot" to the constructor of the object 'character'. Character character("Lancelot"); std::cout << character.getName () << std::endl; /* "Lancelot" is outputted in the console. */ return 0; }

Constructor overloading

Like with functions and methods, we can define many overloads of the constructor of a class.

In the example below, two overloads of the constructor of the class Character are defined. One that takes a string in argument and one that takes no parameter. If the constructor receives a string in argument, it sets the member _name with it. Otherwise, it does nothing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream> class Character { public: Character(const std::string & name) { _name = name; } Character() { } const std::string & getName() const { return _name; } private: std::string _name; }; int main() { // Uses the overload of the constructor that takes a string as argument. Character character("Pamela"); std::cout << "Name: " << character.getName () << std::endl; // Uses the overload of the constructor with no argument. Character character2; std::cout << "Name: " << character2.getName () << std::endl; return 0; }

The constructor of member objects

It is not actually true that empty constructors do absolutely nothing. All the constructors, before executing the code inside their definition, call the constructor of the member objects of the class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream> class A{ public: A() { std::cout << "Constructor of A\n"; } }; class B{ public: B() { std::cout << "Constructor of B\n"; } private: A _a; }; int main() { B b; return 0; }

In the example above, an object of type B is created. Its constructor is called, which in turn, call the constructor of the member object of type A, _a. The constructor of _a is called and prints "Constructor of A\n" to the console. Then, the constructor of b executes the code inside its definition and prints "Constructor of B\n" into the console.

Constructor calling order

When there are many member objects inside a class, their constructor is called in the same order they are defined.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream> class A{ public: A() { std::cout << "Constructor of A\n"; } }; class B{ public: B() { std::cout << "Constructor of B\n"; } }; class C{ public: C() { std::cout << "Constructor of C\n"; } private: A _a; B _b; }; int main() { C c; return 0; }

In the example above, if we wanted the constructor of _b to be called before the constructor of _a, we would simply have to define it first, like that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream> class A{ public: A() { std::cout << "Constructor of A\n"; } }; class B{ public: B() { std::cout << "Constructor of B\n"; } }; class C{ public: C() { std::cout << "Constructor of C\n"; } private: B _b; A _a; }; int main() { C c; return 0; }

Explicitly calling the constructor of the member objects

Explicitly calling the constructors

Until then, the constructor of the member objects have been implicitly (automatically) called by the constructor of the class that contains them. It is, however, possible to explicitly call them. This is done by following the header of the definition of the constructor by a colon and then the name of the objects followed by parentheses (), separated by commas if there are many.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream> class A{ public: A() { std::cout << "Constructor of A\n"; } }; class B{ public: B() { std::cout << "Constructor of B\n"; } }; class C{ public: // The constructors of the members _a and _b are explicitly called. C() : _a(), _b() { std::cout << "Constructor of C\n"; } private: A _a; B _b; }; int main() { C c; return 0; }

The calling order of the constructors

Note that, in the example above, even if we explicitly called the constructor of _b before the one of _a, the constructor of _a would still be called first. Only the definition order of the member objects matters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream> class A{ public: A() { std::cout << "Constructor of A\n"; } }; class B{ public: B() { std::cout << "Constructor of B\n"; } }; class C{ public: // The constructors of the members _a and _b are explicitly called. C() : _b(), _a() { std::cout << "Constructor of C\n"; } private: A _a; B _b; }; int main() { C c; return 0; }

The code from above may make your compiler give you a warning to tell you that even though the constructor of _b is explicitly called before _a, it will be executed after.

Why calling the constructor explicitly

When a constructor call the constructors of the member objects of the class it belongs to, it gives them no argument. By calling the constructor of a member object explicitly, we can give it arguments by putting them between the parentheses of the explicit constructor call. Also, if the class possesses an object of a class that possesses no constructor overload which takes no argument, then calling its constructor explicitly is required, otherwise a compilation error will happen.

Giving arguments to the constructor of member objects

Let us have a look at the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream> class Character { public: Character(const std::string & name) { _name = name; } const std::string & getName() const { return _name; } private: std::string _name; }; int main() { Character character("Michael"); std::cout << character.getName() << std::endl; return 0; }

What happens in the code above is that the constructor of the object _name is called, without argument, so it is set to contain an empty string. Then, using the assignment operator, it assigned with the string received in argument. The second step could have been avoided by explicitly calling the constructor of the member _name and giving it, as parameter, the string received in argument:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream> class Character { public: Character(const std::string & name) : _name(name) { } const std::string & getName() const { return _name; } private: std::string _name; }; int main() { Character character("Lisa"); std::cout << character.getName() << std::endl; return 0; }

Obviously, when we give arguments to a constructor, they must match one of its overloads.

The primitive types are not classes and therefore do not have constructors. It is, however, possible to define the value of member variables of primitive type using the same notation as if we gave arguments to their constructors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream> class A{ public: A(int v) : _a(v), _b(43) { } int getA() const { return _a; } int getB() const { return _b; } private: int _a; int _b; }; int main() { A obj(57); std::cout << obj.getA() << " " << obj.getB() << std::endl; return 0; }

Default values for constructor arguments

Like with functions and methods, it is possible to provide default values for the arguments of constructors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream> class A{ public: A(int value=5) : _var(value) { } int getVar() const { return _var; } private: int _var; }; int main() { A obj1, obj2(171); std::cout << "Value of the member '_var' of 'obj1': " << obj1.getVar() << ".\n"; std::cout << "Value of the member '_var' of 'obj2': " << obj2.getVar() << ".\n"; return 0; }

Complete example

Character.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef CHARACTER_HPP #define CHARACTER_HPP #include <iostream> class Character { public: Character(const std::string name, int life=100, int damage=25); void printInfo() const; const std::string & getName() const; void setTarget(Character *target); void attackTarget(); void receiveDamage(int damage); private: std::string _name; int _life; int _damage; Character *_target; }; #endif

Character.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "Character.hpp" Character::Character(const std::string name, int life, int damage) : _name(name), _life(life), _damage(damage), _target(0) { } void Character::printInfo() const { std::cout << "Character information ------\n"; std::cout << "Name: " << _name << ".\n"; if(_life > 0) std::cout << "Life: " << _life << ".\n"; else std::cout << "The character is dead.\n"; std::cout << "Damage: " << _damage << ".\n"; if(_target != 0) std::cout << "Target: " << _target->getName() << ".\n"; else std::cout << "No target.\n"; std::cout << "----------------------------\n"; } const std::string & Character::getName() const { return _name; } void Character::setTarget(Character *target) { _target = target; } void Character::attackTarget() { // If the character has a target. if(_target != 0) _target->receiveDamage(_damage); } void Character::receiveDamage(int damage) { _life -= damage; // The life can not be lower than 0. if(_life < 0) _life = 0; }

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "Character.hpp" int main() { Character player1("Lancelot"); Character player2("Angela", 80, 100); player1.printInfo(); player2.printInfo(); player1.setTarget(&player2); player2.setTarget(&player1); player1.attackTarget(); player2.printInfo(); player2.attackTarget(); player1.printInfo(); return 0; }

Protected and private methods

Like other class members, constructors can be declared/defined as protected of private. When this is the case, objects of that class can not be constructed, outside of the class, using those constructors.

In the following example, there would be a compilation error, because the private constructor overload A::A(int a) would be used from outside the class A:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream> class A { public: A() : _a(0) { } private: A(int a) { _a = a; } int _a; }; int main() { A a(34); // Error: The constructor overload A::A(int a) is private. return 0; }

The following main would compile without any error:

int main() { A a; return 0; }

Objects of type A can be created using the constructor overload A::A(int a), as long as they are defined inside the class A:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream> class A { public: A() : _a(0) { } void method() { /* Since that method belongs to the class A, we can use the private constructor overload A::A(int a) to create an object. */ A a(89); // Prints the value of the member '_a' of the object 'a'. std::cout << a._a << std::endl; } private: A(int a) { _a = a; } int _a; }; int main() { A a; a.method(); return 0; }