OOP Advanced: Inheritance and Polymorphism
Read at your own pace. Code execution, quizzes, progress tracking, and the AI tutor are available in the interactive view with JavaScript enabled.
Objects Get Flexible
From classes to reusable families
In OOP Basics: Classes and Objects, you learned to bundle data and behavior into small, purposeful classes. That gave us a way to model things like users, bank accounts, game characters, or AI agents.
Now we’ll make those models more flexible with two major OOP ideas:
- Inheritance: define a new class from an existing class
- Polymorphism: call the same operation on different object types and let each type respond correctly
If classes are like components in a web app, inheritance and polymorphism help you build a shared contract while still allowing specialized behavior.

Inheritance Mental Model
A class can build on another class
Inheritance lets one class reuse and extend another class.
The existing class is commonly called the:
- base class or parent class
The new class is commonly called the:
- derived class or child class
A derived class automatically has the accessible members of its base class, then adds or customizes behavior.
FinancialProduct
|
+-- Stock
+-- Bond
+-- SavingsAccount
This is useful when several things share a common core, but each has extra details. In finance software, many instruments may have a name and market value, but a stock might also have a ticker symbol while a bond has an interest rate.

Extending A Class
Public inheritance in C++
In C++, the most common beginner-friendly inheritance form is:
class Derived : public Base
Read that as: Derived is a kind of Base.
In the example below, Stock reuses the FinancialProduct constructor and method, then adds its own ticker-specific behavior.
```cpp
#include <iostream>
#include <string>
using namespace std;
class FinancialProduct {
private:
string name;
double marketValue;
public:
FinancialProduct(string productName, double value)
: name(productName), marketValue(value) {}
void printSummary() const {
cout << name << " is worth $" << marketValue << endl;
}
};
class Stock : public FinancialProduct {
private:
string ticker;
public:
Stock(string productName, double value, string tickerSymbol)
: FinancialProduct(productName, value), ticker(tickerSymbol) {}
void printTicker() const {
cout << "Ticker: " << ticker << endl;
}
};
int main() {
Stock apple("Apple shares", 189.50, "AAPL");
apple.printSummary(); // inherited behavior
apple.printTicker(); // Stock-specific behavior
return 0;
}
```
Is-A Or Has-A
Which situation is the best fit for inheritance?
Polymorphism In Action
Same request, different behavior
Polymorphism means “many forms.”
In OOP, it often means you can call the same method through a base class reference, and C++ will run the correct derived version.
The key C++ ingredient is the virtual keyword on the base class method.
In this example, the function printRiskReport accepts any Asset. It does not need to know whether the asset is a stock or cryptocurrency. Each object decides how to describe its own risk.
```cpp
#include <iostream>
#include <string>
using namespace std;
class Asset {
public:
virtual string riskLevel() const {
return "Unknown risk";
}
};
class StockAsset : public Asset {
public:
string riskLevel() const override {
return "Medium risk: company and market movement";
}
};
class CryptoAsset : public Asset {
public:
string riskLevel() const override {
return "High risk: volatile market swings";
}
};
void printRiskReport(const Asset& asset) {
cout << asset.riskLevel() << endl;
}
int main() {
StockAsset stock;
CryptoAsset crypto;
printRiskReport(stock);
printRiskReport(crypto);
return 0;
}
```
Virtual Dispatch Check
True or false: If a base class method is marked virtual, C++ can choose a derived class override when the method is called through a base class reference.
Override With Intent
override protects your design
When a derived class replaces a virtual method from the base class, that is called overriding.
C++ lets you write override after the method declaration:
string riskLevel() const override
This tells the compiler: “I intend to override a base class method.”
Why this matters:
- It catches spelling mistakes.
- It catches mismatched parameter lists.
- It makes your intent obvious to future readers.
For a web analogy, think of a base class method like an API route contract. Derived classes can implement the route differently, but the shape of the request must match.

Abstract Base Classes
Sometimes the base class is only a contract
A base class does not always need to provide a full implementation.
A pure virtual function says: “Every derived class must provide this behavior.”
The syntax is:
virtual returnType functionName() = 0;
A class with at least one pure virtual function is an abstract class. You do not create objects directly from it. Instead, you use it as a common contract for derived classes.
This is a common pattern in AI systems: different models may all support predict(), even if each model predicts in a completely different way.
```cpp
#include <iostream>
#include <string>
using namespace std;
class Model {
public:
virtual string predict(double input) const = 0; // pure virtual
};
class FraudModel : public Model {
public:
string predict(double input) const override {
return input > 1000.0 ? "Flag transaction" : "Allow transaction";
}
};
class SpamModel : public Model {
public:
string predict(double input) const override {
return input > 0.75 ? "Likely spam" : "Likely safe";
}
};
void runPrediction(const Model& model, double input) {
cout << model.predict(input) << endl;
}
int main() {
FraudModel fraud;
SpamModel spam;
runPrediction(fraud, 1250.0);
runPrediction(spam, 0.82);
return 0;
}
```
Pure Virtual Syntax
Fill in the blank to make this function pure virtual:
virtual double price() const ___;
Fill in the blank: _____________
Design Tradeoffs
Use inheritance carefully
Inheritance and polymorphism are powerful, but they work best when the relationship is truly stable.
Good uses:
-
Stockis a kind ofFinancialProduct -
FraudModelis a kind ofModel -
Circleis a kind ofShape
Be cautious when:
- You only want to reuse a few lines of code.
- The relationship is really “has-a,” not “is-a.”
- Derived classes would need to ignore most base behavior.
A practical rule: inherit for shared identity and contract, not just convenience.
Next, we’ll shift from object relationships to working with external data through file handling and I/O.
