Changing an inherited function
A derived class inherits its base class's member functions, but sometimes an inherited function isn't quite right. A Puppy is a Dog, yet it should say "Yip!" instead of "Woof!". Instead of inventing a new function name, the child defines bark() again. That is overriding: the child supplies its own version of a function the parent already has.
The payoff is that callers don't need to know which kind of dog they hold. They call bark(), and the right version runs.
virtual and override
In C++, overriding takes two keywords, one in each class:
class Dog {
public:
virtual void bark() { cout << "Woof!" << endl; }
};
class Puppy : public Dog {
public:
void bark() override { cout << "Yip!" << endl; }
};
virtualin the base class marks the function as one that derived classes may replace. Without it, the child's function doesn't override anything; it only hides the parent's.overridein the derived class is optional, but it asks the compiler to check that a virtual function with exactly this signature exists in a base class. If you misspell the name or change a parameter, the compiler stops you instead of quietly creating a new function.
Once a function is virtual, it stays virtual in every class derived from it, so override alone is enough in the child. The signature must match: the name, the parameter types and the const qualification.
Here is what override catches. In this code the parameter list differs:
class Puppy : public Dog {
public:
void bark(int times) override { cout << "Yip!" << endl; }
};
GCC rejects it with marked 'override', but does not override. Without the keyword, it would compile and bark(int) would be an unrelated new function, while a call to bark() through a Dog pointer still printed "Woof!".
What happens without virtual
If the base function is not virtual, the child's version hides it, and the call depends on the declared type of the variable, not on the real object:
class Base {
public:
void who() { cout << "Base" << endl; }
virtual void v() { cout << "Base v" << endl; }
};
class Der : public Base {
public:
void who() { cout << "Der" << endl; }
void v() override { cout << "Der v" << endl; }
};
Der x;
Base* b = &x;
b->who(); // Base
b->v(); // Der v
x.who(); // Der
Through the Base*, the non-virtual who() runs the base version, while the virtual v() runs the derived one. This is the most surprising rule for people coming from Java or Python, where every method behaves like a virtual one. In C++ you opt in with virtual. The next lesson, on polymorphism, relies on it.
Calling the parent's version
An override often wants to extend the parent's behaviour, not replace it. Name the base class and use the scope operator to call it directly:
void bark() override {
Dog::bark(); // run the parent's version first
cout << "Yip!" << endl;
}
The lesson's exercise
The base class's bark() is already virtual and prints Woof! I am and the name. Puppy has a constructor that forwards the name to Dog. The task is to override bark in Puppy so that the program prints:
Yip! I am Buddy
- Inside
Puppy, underpublic:and below the constructor, declarevoid bark() override { }. The return type and the empty parameter list must match the base version exactly. - In the body, print
"Yip! I am "followed bynameandendl.nameis a public member inherited fromDog, so it can be used directly.
The solution in C++
class Dog {
public:
string name;
Dog(string name) { this->name = name; }
virtual void bark() {
cout << "Woof! I am " << name << endl;
}
};
class Puppy : public Dog {
public:
Puppy(string name) : Dog(name) {}
void bark() override {
cout << "Yip! I am " << name << endl;
}
};
class Solution {
public:
void mainFunction() {
Puppy buddy("Buddy");
buddy.bark();
}
};
The variable buddy is a Puppy, so even a non-virtual function would run the child's version here, because the compiler knows the exact type. The virtual keyword starts to matter when the call goes through a pointer or reference to Dog, as in the next lessons. Writing it correctly now is the habit to build.
Common mistakes
- Forgetting
virtualin the base class. The code compiles with a plain base function, but calls throughDog*orDog&run the parent's version. If you then addoverride, the compiler reports an error, which is a good reason to use it. - A signature that differs slightly. A different parameter type, or a missing
conston one side, makes a new function instead of an override. Useoverrideto catch it. - Putting
overrideon the base class. It belongs on the function that replaces a virtual one. - Copying a
Puppyinto aDogvariable. The copy is a plainDog, sobark()runs the parent's version. Use a pointer or reference to keep the real type.
Key takeaways
- Mark the base function
virtualand the child's replacementoverride. - The signature must match exactly;
overridemakes the compiler check it. - Without
virtual, a function is hidden, not overridden, and calls through base pointers use the base version. - Call the parent's version with
Dog::bark()when you want to extend it.