Inheritance
→ provides method to create classes from existing classes
→ allow reuse of existing classes
→ allows new classes to modify behaviors of existing classes to make it unique
Accounts - with inheritance - reuse
class Account {
// balance, deposit, withdraw, ...
};
class Savings_Account : public Account {
// interest rate, specialized withdraw, ...
};
class Checking_Account : public Account {
// minimum balance, per check fee, specialized withdraw, ...
};
clas Trust_account : public Account {
// interest rate, specialized withdraw, ...
};
Single inheritance
→ a new class is created from another single class
Multiple Inheritance
→ a new class is created from two or more other classes
Base class (parent class, super class)
→ class beeing extendet or inherited from
Derived class (child class, sub class)
→ the class being created from the Base class
→ Will inherit attributes and operations from Base class
example:
C is derived from A
D is derived from C
(B and C have nothing in common)
“Is-A” relationship
→ public inheritance
→ derived class are sub-types of their base classes, can be used wherever base class objects are used
Generalization
→ combining similar classes into single, more general class based on common attributes
Specialization
→ creating new classes from existing classes proving more specialized attributes or operations
Organisation of inheritance relationships: Class Hierarchies
Public Inheritance vs. Composition
(always check if inheritance is needed instead of using a less complex composition)
(both allow reuse of existing classes)
Public inheritance:
→ employee “is-a” person
→ Checking Account “is-a” person
Composition:
→ Person “has-a” account
→ player “has-a” special attack
class Person {
private:
std::string name; // has-a name
Account account; // has-a account
};
Derivation Syntax
class Base {
// Base class members ...
};
class Derived: acces-specifier Base {
// Derived class members ...
};
Acces-specifier can be: public, private or protected
// Example for public specifier
class Account {
// Account class members ...
};
class Savings_Account: public Account {
// Savings_Account class members ...
};
//Savings_Account "is-a" Account
Account account {};
Account *p_account = new Account();
account.deposit(1000.0);
p_account->withdraw(200.0);
delete p_account;
//->
Savings_Account sav_account {};
Savings_Account *p_sav_account = new Savings_Account();
sav_account.deposit(1000.0);
p_sav_account->withdraw(200.0);
delete p_sav_account;
Protected Members and Class Access
class Base {
protected:
// protected Base class members ...
};
→ accessible from the base class itself
→ accessible from classes derived from Base
→ not accessible by objects of Base or Derived, act like private class members
class Base {
public:
int a; // public Base class members ...
protected:
int b; // protected Base class members ...
private:
int c; // private Base class members ...
};
objects as class members
#include <iostream>
using namespace std;
class Base {
// Note friends of Base have access to all
public:
int a {0};
void display() {std::cout << a << ", " << b << ", " << c << endl; } // member method has access to all
protected:
int b {0};
private:
int c {0};
};
class Derivet: public Base {
// Note friends of Derived have access to only what Derived has access to
// a will be public
// b will be protected
// c will not be accessible
public:
void access_base_members() {
a = 100; // OK
b = 200; // OK
// c = 300; // not accessible
}
};
int main() {
cout << "=== Base member access from base objects ==========" << endl;
Base base;
base.a = 100; // OK
// base.b = 200; // Compiler Error
// base.c = 300; // Compiler Error
cout << "=== Base member access from derived objects ===========" << endl;
Derived d;
d.a = 100; // OK
// d.b = 200; // Error (will still be inhereted, but no access)
// d.c = 300; // Error (will still be inhereted, but no access)
return 0;
}
Constructors and Destructors
→ a derived class inhertis from its Base class
→ Base part of the derived class MUST be initialized BEFORE the derived class is initialized
→ When a derived object is created
- Base class constructor executes then
- derived class constructor executes
class Base {
puvlic:
Base() { cout << "Base constructor" << endl; }
};
class Derived : public Base {
public:
Derived() { cout << "Derived constructor " << endl; }
};
Base base;
Derived derived;
// Output
Base constructor
Base destructor
Base constructor
Derived constructor
Derived destructor
Base destructor
// Output depending on used herarchy!
A Derived class does NOT inherit
- Base class constructors
- Base class destructor
- Base class overloaded assignment operators
- Base class friend functions
→ the derived class consturctors, destructors and overloaded assignment operators can invoke the base-class versions
→ often better to define constructors yourself in case of inheritance
//example
#include <iostream>
using namespace std;
class Base {
private:
int doubled_value;
public:
Base() : value{0} { cout << "Base no-args constructor" << endl; }
Base(int x) : value{x} { cout << "Base (int) overloaded constructor" << endl; }
~Base(){ cout << "Base destructor" << endl; }
};
class Derived : public Base {
using Base::Base;
private:
int doubled_value;
public:
Derived() : doubled_value {0} { cout << "Derived no-args constructor " << endl; }
// Derived(int x) : doubled_value {x*2} { cout << "Derived (int) overloaded constructor" << endl; }
// Derived(int x) : double_value {x*2} { cout << "Derived destructor" << endl;}
};
int main() {
// Base b;
// Base b{100};
// Derived d;
Derived d{1000}; // if not explicitly called, the no args constructor will be called
// -> value = 0
return 0;
}
Passing Arguments to Base Class Constructors
→ the base part of a derived class must be initialized first
→ control which base class constructor is used
// example
class Base {
int value;
public:
Base() : value{0} {
cout << "Base no-args constructor" << endl;
}
Base(int x) : value{x} {
cout << "int Base constructor" << endl;
}
};
class Derived : public Base {
int doubled_value;
public:
Derived(): Base{}, doubled_value{0} {
cout << "Derived no-args constructor " << endl;
}
Derived(int x) : Base{x}, doubled_value {x*2} {
cout << "int Derived constructor " << endl;
}
};
// example
Base base;
// Output:
// Base no-args constructor
Base base{100};
// Output:
// int Base constructor
Derived derived;
// Output:
// Base no-args constructor
// Derived no-args constructor
Derived derived{100};
// Output:
// int Base constructor
// int Derived constructor
Copy and move Constructors and Operators in Derived Classes
→ often you do not need to provide your own
→ if you DO NOT define them in Derived, the compiler will create them and call the base class version
→ if you DO provide Derived versions, the you musk invoke the Base versions explicitly yourself
Careful: especially if Base and Derived each have raw pointers, provide them with deep copy semantics
// example
// invoke Base copy constructor explicitly, Derived object (the Base-part) "other" will be sliced
Derived::Derived(const Derived &other)
: Base(other), {Derived initialiation list}
{
// code
}
// Copy constructor example
class Base {
int value;
public:
// same constructors as previous example
Base(const Base &other) :value{other.value} {
cout << "Base copy constructor" << endl;
}
};
class Derived : public Base {
int doubled_value;
public:
// same constructors as previous example
Derived(const Derived &other)
: Base(other), doubled_value {other.doubled_value } {
cout << "Derived copy constructor " << endl;
}
}; // same for Move constructor
// operator= example
class Base {
int value;
public:
// Same constructors as previous example
Base &operator=(const Base &rhs) {
if (this != &rhs) {
value = rhs.value; // assign
}
return *this;
}
};
class Derived : public Base {
int doubled_value;
public:
// same constructors as previous example
Derived &operator=(const Derived &rhs) {
if (this != &rhs) {
Base::operator=(rhs); // Assign Base part
doubled_value = rhs.doubled_value; // Assign Derived part
}
return *this;
};
Static binding of method calls
Base b;
b.deposit(1000.0); //Base::deposit
Derived d;
d.deposit(1000.0); //Derived::deposit
Base *ptr = new Derived();
ptr->deposit(1000.0);// Base::deposit ????
// By default object call if referred to Base class
// here the pointer is pointing to Base class obejct
Multiple Inheritance
- a derived class inherits from two or more Base classes at the same time
- the base classes may belong to unrelated class herarchies
A Department Chair
- Is-A Faculty and
- Is-A Administrator
class Department_Chair:
public Faculty, public Administrator {
...
};
Example