What a constructor does
A constructor is a special member function that runs automatically when an object is created. Its job is to put the new object into a valid starting state. Without one, you create the object first and fill in its members afterwards:
Dog dog;
dog.name = "Rex";
dog.age = 4;
That works, but nothing forces you to do it, and a forgotten line leaves a member empty or, for an int, holding garbage. A constructor moves the setup into the class, so every object is initialized the moment it exists.
Syntax
A constructor differs from an ordinary member function in two ways. Its name is exactly the class name, and it has no return type, not even void.
class Dog {
public:
string name;
int age;
Dog(string name, int age) {
this->name = name;
this->age = age;
}
};
To call it, put the arguments in parentheses after the variable name:
Dog rex("Rex", 4);
Dog luna("Luna", 7);
By the time each line finishes, rex.name is "Rex" and rex.age is 4. No new is needed for a stack object.
The this pointer
In the constructor above, the parameter name and the member name share an identifier. Inside the constructor, a bare name refers to the parameter, because the closest declaration wins. To reach the member, write this->name. Every non-static member function has a hidden pointer named this, which points at the object the function is working on. Because it is a pointer, you access members through it with the arrow operator -> rather than a dot.
So this->name = name; reads: store the parameter name in this object's member name. If you wrote name = name; instead, you would assign the parameter to itself, and the member would stay uninitialized.
A common alternative: the initializer list
C++ has a second way to write the same constructor, and you will see it in most real code. The members are initialized after a colon, before the body runs:
Dog(string name, int age) : name(name), age(age) {}
Inside the parentheses, the name is the parameter; outside, it is the member, so the shadowing is not a problem here. The difference from assignment in the body is that a member in the initializer list is constructed with its value directly, instead of being default-constructed first and overwritten later. For string and int that rarely matters, but for members that can't be assigned, such as const members and references, the initializer list is the only option. This lesson uses assignments with this->, which behaves the same for these two members.
A side effect to know about
Once you declare any constructor, the compiler stops generating the default one that takes no arguments. After this lesson's change, the line Dog d; no longer compiles, because every Dog must now be created with a name and an age. GCC reports no matching function for call to 'Dog::Dog()'. If you also want objects with no arguments, add a second constructor with no parameters.
The lesson's exercise
The Dog class has the members name and age. There are two steps:
- Under
public:insideDog, add a constructorDog(string name, int age)whose body assigns the parameters to the members withthis->. It has to be public, because code outside the class creates the dogs. - In
mainFunction, createrexandlunawith the arguments"Rex", 4and"Luna", 7. They must come before thecoutlines that use them.
The expected output is:
Rex is 4
Luna is 7
The solution in C++
class Dog {
public:
string name;
int age;
Dog(string name, int age) {
this->name = name;
this->age = age;
}
};
class Solution {
public:
void mainFunction() {
Dog rex("Rex", 4);
Dog luna("Luna", 7);
cout << rex.name << " is " << rex.age << endl;
cout << luna.name << " is " << luna.age << endl;
}
};
The arguments are matched to the parameters by position, so "Rex" becomes name and 4 becomes age. Swapping them is a compile error here, since a number can't become a string that way.
Common mistakes
- Adding a return type.
void Dog(string name, int age)is rejected by the compiler, because a function named like its class is a constructor and can't have a return type. - Writing
name = name;withoutthis->. The parameter is assigned to itself and the member keeps its old value. - Placing the constructor under
private:. Code outside the class then can't create objects. - Using
this.name.thisis a pointer, so usethis->name. - Creating an object without arguments after defining only this constructor.
- Using empty parentheses for a no-argument call.
Dog d();declares a function. WriteDog d;instead.
Key takeaways
- A constructor has the class's name and no return type, and it runs when the object is created.
- Pass the arguments after the variable name:
Dog rex("Rex", 4);. this->nameis the member; a barenameis the parameter when they share a name.- An initializer list (
: name(name), age(age)) is the idiomatic alternative. - Declaring a constructor removes the automatic no-argument one.