The is-a relationship
Many things are specialized kinds of other things: a puppy is a dog, a car is a vehicle, a manager is an employee. Inheritance lets a class say so in code. A derived class (the child) is built on a base class (the parent) and automatically gets the base class's members. It can add members of its own, so you don't copy and paste code between related classes.
C++ also supports a class with several bases, but this lesson covers the common case of one.
Syntax
After the new class name, write a colon, an access keyword and the base class:
class Puppy : public Dog {
public:
Puppy(string name) : Dog(name) {}
void whine() { cout << "whimper" << endl; }
};
The keyword public before Dog means the base class's public members stay public in Puppy. It is what you want for an is-a relationship. If you leave it out, a class inherits privately by default, and the base's members become inaccessible from outside. A call such as buddy.bark() then fails with an error saying the function is inaccessible. A struct inherits publicly by default.
What a derived class receives
- Member variables. A
Puppyhas anameeven though onlyDogdeclares it. - Member functions. A
Puppycan callbark()though onlyDogdefines it. - Substitutability. Wherever code expects a
Dogreference or pointer, aPuppycan be passed. The polymorphism lesson builds on this.
Private members of the base are part of the derived object, but the derived class's code can't name them. If a child needs access, make the member protected in the base.
Forwarding the constructor
Constructors are not inherited. Each class has its own, and creating a Puppy builds the Dog part first and the Puppy part after. The base part must be built with arguments if the base's constructor needs them. That call is written in the derived constructor's initializer list, after a colon:
Puppy(string name) : Dog(name) {}
This reads: to build a Puppy from a name, first build its Dog part from that name, then run the body, which is empty here. The call goes before the body because the base part must exist before the derived constructor's own code touches it.
In this lesson, Dog has only Dog(string name), so it has no default constructor, and the compiler can't guess what to pass. Without the forwarding constructor, Puppy buddy("Buddy"); fails with no matching function for call to 'Puppy::Puppy(const char [6])'.
Destruction runs the other way around. If both classes print in their constructors and destructors, creating a puppy shows the Dog constructor, then the Puppy constructor, and at the end of the scope the Puppy destructor and then the Dog destructor.
The lesson's exercise
The Dog class is given, with name, a constructor and bark(). The task is to write a Puppy class below it, so that the code in mainFunction prints:
Woof! I am Buddy
whimper
- Declare
class Puppy : public Dogand open apublic:section. Members of aclassare private by default, andmainFunctionneeds to create aPuppyand call its functions. - Add a one-line constructor
Puppy(string name) : Dog(name) {}. - Add
void whine(), which prints"whimper"followed byendl. Nothing is needed forbark, since it's inherited. - Close the class with
};. The class must sit belowDog, since the base must be fully defined before it is used.
The solution in C++
class Dog {
public:
string name;
Dog(string name) { this->name = name; }
void bark() {
cout << "Woof! I am " << name << endl;
}
};
class Puppy : public Dog {
public:
Puppy(string name) : Dog(name) {}
void whine() {
cout << "whimper" << endl;
}
};
class Solution {
public:
void mainFunction() {
Puppy buddy("Buddy");
buddy.bark();
buddy.whine();
}
};
In Puppy(string name) : Dog(name) {}, the parameter is called name, the same as the base member. In the initializer list, Dog(name) uses the parameter, so the name travels up to the base, which stores it in its own member. The empty braces are the whole body, because the base constructor already did the work.
Common mistakes
- Omitting
publicin: public Dog. The code then fails becausebark()becomes inaccessible from outside. - Not forwarding the arguments. If the base has no default constructor, the derived constructor must call a base constructor explicitly.
- Trying to assign the base members in the body instead of calling
Dog(name). The base part is built before the body runs, and with no way to build it, the code does not compile. - Forgetting the semicolon after the closing brace of
Puppy. - Re-declaring
nameinPuppy. That creates a second, separate member, which is not what you want. - Copying a
Puppyinto aDogvariable.Dog d = buddy;compiles, but only theDogpart is copied. This is called slicing.
Key takeaways
class Child : public Parentgives the child the parent's public members and lets it add its own.- Constructors are not inherited; call the parent's in the initializer list.
- The base part is constructed first and destroyed last.
- Use inheritance for real is-a relationships, not just to reuse code.