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

→ 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 Department Chair

class Department_Chair:
		public Faculty, public Administrator {
		...
};

Example