In OOP, inheritance is a fundamental concept that allows
a subclass (aka. derived class or
child class)
to inherit properties and methods from
a superclass (aka. base class or
parent class)
The two major uses for hierarchies are named implementation inheritance and interface inheritance
classDiagram
Parent <|-- Child
Private members of base classes are never directly accessible in subclasses
publicprivateprivate
members in the derived class#include <iostream>
using std::cout, std::endl;
class Parent {
public:
void defined_in_parent() { cout << "method defined in parent" << endl; }
};
class Child : public Parent { // inherit from `Parent`; rec.: always `public`!
};
int main() {
Child c{};
c.defined_in_parent();
return 0;
}
privateprotected to member functions that might need to be
accessed from subclassesThe name literally means "many forms"
(from Greek poly-
"many" and morph- "form").
Polymorphism allows an entity (eg.: a function, a class, or an operator) to take on multiple forms or have multiple implementations.
Resolved by the compiler before the program executes
int add(int i, int j) and
add(double c, double d) are selected at compile time
based on the argument typeTrue polymorphic behavior requires a single method call to invoke different implementations based on an object's runtime type
Allows base interface to be used for actions defined in derived classes
Primary mechanism for run-time polymorphism in C++
C++ determines which non-virtual function to call at compile time
Compile time decisions are based on the declared type of an object or pointer, not the object's dynamic (actual) type
Member functions in subclasses override functions of base classes
virtual means exactly and only
"this is a new virtual function"
Virtual functions are required for achieving runtime polymorphism when dealing with base class pointers to derived class objects
Use override to indicate "this is a non-final overrider"
Use final to indicate "this is a final overrider"
A regular bank account and a children's bank account have a lot in common
Account can be overdrawn while
ChildAccount cannot be overdrawn
classDiagram Account <|-- ChildAccount
#ifndef ACCOUNT_HPP
#define ACCOUNT_HPP
#include <cstdint>
#include <iostream>
#include <string>
namespace bank {
class Account {
public:
explicit Account(std::string owner) : Account(owner, 0l) {}
explicit Account(std::string owner, uint32_t deposit);
void deposit(uint32_t amount);
virtual uint32_t withdraw(uint32_t amount);
int64_t balance() const { return balance_; }
unsigned long number() const { return number_; }
std::string owner() const { return owner_; }
private:
int64_t balance_ = 0;
std::string owner_;
unsigned long number_;
static unsigned long next_number_;
};
std::ostream& operator<<(std::ostream& out, const Account& a);
class ChildAccount : public Account {
public:
ChildAccount(std::string owner) : Account(owner) {}
ChildAccount(std::string owner, uint32_t deposit)
: Account(owner, deposit) {}
uint32_t withdraw(uint32_t amount) override;
};
} // namespace bank
#endif // ACCOUNT_HPP
Download account.hpp
#include "account.hpp"
#include <iostream>
#include <string>
namespace bank {
unsigned long Account::next_number_ { 7483919474 };
Account::Account(std::string owner, unsigned int deposit)
: balance_{deposit}, owner_{owner} {
number_ = next_number_;
next_number_++;
}
void Account::deposit(unsigned int amount) {
balance_ += amount;
}
/* return the amount actually withdrawn */
unsigned int Account::withdraw(unsigned int amount) {
balance_ -= amount;
return amount;
}
std::ostream& operator<<(std::ostream& out, const Account& a) {
out << a.owner() << "'s Account(#" << a.number() << ", ";
out << a.balance() << " Cents)";
return out;
}
} // namespace bank
Download account.cpp
#include "account.hpp"
namespace bank {
unsigned int ChildAccount::withdraw(unsigned int amount) {
if (amount > balance()) {
amount = balance();
}
return Account::withdraw(amount);
}
} // namespace bank
Download childaccount.cpp
#include <iostream>
#include <vector>
#include "account.hpp"
using bank::Account, bank::ChildAccount;
using std::cout, std::endl, std::vector;
int main() {
Account a {"Gerald"};
cout << a << endl;
a.deposit(2000);
cout << a << endl;
cout << "attempting to withdraw 2500 cents\n";
a.withdraw(2500);
cout << a << endl;
ChildAccount ca {"Eric"};
ca.deposit(5000);
cout << ca << endl;
cout << "attempting to withdraw 100000 leads to actually withdrawing ";
cout << ca.withdraw(100000) << endl;
cout << ca << endl;
cout << "\nworking with a `ChildAccount` pointed to by an `Account*`\n";
ChildAccount ca2 {"Mary"};
Account* ap = &ca2;
cout << *ap << endl;
ap->deposit(2000);
cout << *ap << endl;
cout << "attempting to withdraw 2500 leads to actually withdrawing ";
cout << ap->withdraw(2500) << endl; // requires `virtual` in base class
cout << *ap << endl << endl;
vector<Account*> accounts{&a, &ca, &ca2}; // polymorphism ftw!
for (auto& account : accounts) {
cout << "got " << account->withdraw(100) << " from " << *account << endl;
}
}
Download main.cpp