Method Overriding
Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 51 of 104.
Method overriding occurs when a derived class provides its own implementation of a method that already exists in the base class. This allows derived classes to customize inherited behavior while keeping the same method signature.
To override a method, simply define a method in the derived class with the exact same name, return type, and parameters:
class Animal {
public:
void speak() {
std::cout << "Some sound" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() {
std::cout << "Woof!" << std::endl;
}
};
Dog d;
d.speak(); // Output: Woof!The derived class version hides the base class version. However, you can still access the base class method using the scope resolution operator:
class Dog : public Animal {
public:
void speak() {
Animal::speak(); // Call base class version
std::cout << "Woof!" << std::endl;
}
};There's an important limitation with this basic form of overriding. When you access a derived object through a base class pointer or reference, the base class version is called instead:
Dog d;
Animal* ptr = &d;
ptr->speak(); // Output: Some sound (not Woof!)This happens because the compiler determines which method to call based on the pointer type, not the actual object type. To achieve true runtime polymorphism where the correct method is called based on the actual object, you'll need virtual functions, which we'll cover in the next lesson.
Challenge
EasyLet's build a notification system that demonstrates method overriding in action. You'll create a base Notifier class and two specialized notifiers that customize how messages are delivered.
You'll organize your code across three files:
Notifier.h: Define a baseNotifierclass with:- A protected
std::string recipientmember - A constructor that takes a recipient name and stores it
- A public
send(const std::string& message)method that prints:Sending to <recipient>: <message>
- A protected
EmailNotifier.h: Define anEmailNotifierclass that publicly inherits fromNotifier:- A constructor that takes a recipient and passes it to the base class
- Override the
send()method to first call the base class version usingNotifier::send(), then print:[Email sent successfully]
main.cpp: Read two inputs (each on a separate line):- Recipient name (string)
- Message content (string)
Define an
SMSNotifierclass directly in main.cpp that publicly inherits fromNotifier:- A constructor that takes a recipient and passes it to the base class
- Override the
send()method to print:SMS to <recipient>: <message>(completely replacing the base behavior without calling it)
Create one object of each notifier type (base
Notifier,EmailNotifier, andSMSNotifier) using the same recipient. Callsend()on each with the input message, printing a separator line between each:--- Base Notifier --- <output> --- Email Notifier --- <output> --- SMS Notifier --- <output>
For example, with inputs Alice and Hello there!:
--- Base Notifier ---
Sending to Alice: Hello there!
--- Email Notifier ---
Sending to Alice: Hello there!
[Email sent successfully]
--- SMS Notifier ---
SMS to Alice: Hello there!Notice how EmailNotifier extends the base behavior by calling the parent method first, while SMSNotifier completely replaces it with its own implementation. Don't forget header guards and to include the appropriate headers in each file.
Cheat sheet
Method overriding allows a derived class to provide its own implementation of a method from the base class, keeping the same method signature (name, return type, and parameters).
Basic override syntax:
class Animal {
public:
void speak() {
std::cout << "Some sound" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() {
std::cout << "Woof!" << std::endl;
}
};
Dog d;
d.speak(); // Output: Woof!The derived class method hides the base class version. To call the base class method from the derived class, use the scope resolution operator:
class Dog : public Animal {
public:
void speak() {
Animal::speak(); // Call base class version
std::cout << "Woof!" << std::endl;
}
};Important limitation: When accessing a derived object through a base class pointer or reference, the base class version is called:
Dog d;
Animal* ptr = &d;
ptr->speak(); // Output: Some sound (not Woof!)This is because the compiler determines which method to call based on the pointer type, not the actual object type. For true runtime polymorphism, use virtual functions.
Try it yourself
#include <iostream>
#include <string>
#include "Notifier.h"
#include "EmailNotifier.h"
using namespace std;
// TODO: Define SMSNotifier class here that publicly inherits from Notifier
// - Constructor that passes recipient to base class
// - Override send() to print: SMS to <recipient>: <message>
// (completely replace base behavior, don't call parent send())
int main() {
string recipient;
string message;
getline(cin, recipient);
getline(cin, message);
// TODO: Create Notifier object with recipient
// Print "--- Base Notifier ---"
// Call send() with message
// TODO: Create EmailNotifier object with recipient
// Print "--- Email Notifier ---"
// Call send() with message
// TODO: Create SMSNotifier object with recipient
// Print "--- SMS Notifier ---"
// Call send() with message
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container