11. Inheritance

About this Tutorial –

Objectives –

This course is aimed at students who need to get up to speed in C++. The course introduces object-oriented concepts and shows how they are implemented in C++. The course does not require awareness or familiarity with object-oriented programming techniques, but programming experience would be useful but not necessarily required.

Audience

Students who are new to object orientation (or programming) and need to learn C++.

Prerequisites

No previous experience in C++ programming is required. But any experience you do have in programming will help. Also no experience in Visual Studio is required. But again any experience you do have with programming development environments will be a valuable.

Contents

The C++ course covers these topics and more:

  • Introduction to C++: Key features of C++; Defining variables; Formulating expressions and statements; Built-in data types; Console input/output
  • Operators and types: Assignment; Compound Assignment; Increment and decrement operators; Const declarations; Type conversions
  • Going Further with Data Types: Enumerations; Arrays; Using the standard vector class; Using the standard string class; Structures

Download Solutions

Java tutorial


Overview

Estimated Time – 0.5 Hours

Not what you are looking? Try the next tutorial – Operator Overloading

Lab 1: Overview of inheritance

Lab 1: Overview of inheritance
  1. Inheritance and OO
    • Inheritance is a very important part of object-oriented development
      • Allows you to define a new class based on an existing class
      • You just specify how the new class differs from the existing class
    • Terminology:
      • For the “existing class”: Base class, superclass, parent class
      • For the “new class”: Derived class, subclass, child class
    • Potential benefits of inheritance:
      • Improved OO model
      • Faster development
      • Smaller code base
  2. Superclasses and Subclasses
    • The subclass inherits everything from the superclass (except constructors)
      • You can define additional variables and methods
      • You can override existing methods from the superclass
      • You typically have to define constructors too
      • Note: You can’t cherry pick or “blank off” superclass members
  3. Inheritance in C++
    • Inheritance is not a requirement for C++ classes
      • You can define a class that doesn’t inherit from anything
      • There’s no Object class at the root of a global inheritance hierarchy (unlike Java and C#)
    • C++ supports multiple inheritance
      • A class can have any number of parent classes
    • However… single inheritance is generally favoured over multiple inheritance
      • i.e. just one parent class
      • This is the way other OO languages do inheritance (e.g. Java, C#)
Lab
  1. Defining a base class
    • In Visual Studio, create a new C++ project named InheritanceApp in the student folder
    • Define a class named Person with the following members:
      • Data members for the person’s name and age
        string name;
        int age;
      • A constructor, to initialize the name and age
        Person::Person(const string & n, int a)
        : name(n),
         age(a)
        {
         cout << "\nPerson object created\n"; }
      • View code file.
      • A ToString() method, to return a textual representation of the person's info
        string Person::ToString() const
        {
         stringstream stm;
         stm << name << " " << age;  return stm.str(); }
      • A destructor, to output a simple message such as "goodbye from Person dtor"
        Person::~Person()

         cout << "\nGoodbye from Person dtor\n"; }
      • View code file.
    • In main(), write code to test your Person class
      Person * p1 = new Person("Fred Flintstone", 68);
      cout << p1->GetName() << endl; cout << p1->GetAge() << endl; cout << p1->ToString() << endl;
    • View code file.

Lab 2: Defining superclasses and subclasses

Lab 2: Defining superclasses and subclasses
  1. Overview
    • During OOA/OOD you decide how to organize classes in your inheritance hierarchy
    • Generalization
      • This is when you notice semantic similarities between classes in your design
      • Factor the similarities into a common superclass
      • Define subclasses that extend the superclass in appropriate ways
    • Specialization
      • This often occurs quite late during development, or if you are using a pre-provided class library or framework
      • You notice a class that is almost what you want, but not quite
      • Define a new class that extends the existing class as appropriate
  2. Sample Hierarchy
    • We'll see how to implement the following hierarchy in this chapter:
    • T11P1

    • Note:
      • BankAccount defines common state and behaviour that is relevant for all kinds of account
      • SavingsAccount "is a kind of" BankAccount that earns interest
      • CurrentAccount "is a kind of" BankAccount that has cheques
  3. Superclass Considerations
    • To define a superclass, just define a regular class
      • Although there are some issues you must consider...
    • You can allow subclasses special access to some members
      • Define members as "protected"
    • You can allow subclasses to override some methods
      • Define methods as "virtual"
      • (Otherwise the methods cannot be overridden properly)
    • You can defer a method implementation to subclasses
      • Designate the method as "pure virtual"
  4. Access Modifiers
    • C++ supports 3 access levels for members in a class...
    • public
      • Accessible by anyone
      • Methods and constants are often public
    • private
      • Accessible only by class itself
      • Data and helper methods are usually private
    • protected
      • Accessible by class itself, and by subclasses
      • Allow access to members that are hidden from general client code
  5. Defining a Superclass
    • Here are the interesting parts of the BankAccount class
      • This is a simplification for now...
        class BankAccount
        {
        private:
         string accountHolder;
         private int id;
        protected:
         double balance;
         // Class data ...
         // Constructors ...
         // Instance methods, including overridable methods if appropriate.
         // Class methods ...
        };
    • Note:
      • balance is protected, so it can be accessed by subclasses
  6. Defining a Subclass
    • To define a subclass, use this syntax:
      • Note the public keyword - this is significant!
        class SavingsAccount : public BankAccount
        {
         // Additional data and methods ...
         // Constructor(s) ...
         // Overrides for superclass methods, if necessary ...
        };
  7. Adding New Members
    • The subclass inherits everything from the superclass
      • (Except for constructors)
      • The subclass can define additional members if it needs to...
        class SavingsAccount : public BankAccount
        {
        private:
         bool premium, goneOverdrawn;
         static const double BASIC_INTEREST_RATE;
         static const double PREMIUM_INTEREST_RATE;
         static const double GUARANTEED_LIMIT;
        public:
         void applyInterest()
         {
          if (balance < 0)    cout << "Sorry mate, no interest if you're overdrawn" << endl;   else if (premium && !goneOverdrawn)    balance *= (1 + PREMIUM_INTEREST_RATE);   else    balance *= (1 + BASIC_INTEREST_RATE);  }  ... };
  8. Defining Constructors
    • The subclass doesn't inherit constructors from superclass
      • So, define constructor(s) in subclass, to initialize subclass data
    • The subclass constructor must invoke the superclass constructor, to initialize superclass data
      • You do this via the member initialization list
        class SavingsAccount : public BankAccount
        {
        public:
         ...
         SavingsAccount(const string & accountHolder, bool premium)
          : BankAccount(accountHolder),
           premium(premium)
         {
          // Any additional subclass initialization here.
         }
         ...
        };
  9. Virtual Methods
    • The subclass can override superclass instance methods that are defined with the virtual keyword
      • To provide a different (or supplementary) implementation
      • No obligation
    • If a subclass overrides a virtual method:
      • The signature must match the superclass method signature
      • The return type must be the same
      • The access level must be the same, or less restrictive
    • An override can call the original superclass method, to leverage existing functionality
      • Via BaseClassName::methodName(params)
    • Examples of overriding methods:
      class BankAccount
      {
      public:
       virtual double withdraw(double amount) { ... }
       virtual string toString() { ... }
       ...
      class SavingsAccount : public BankAccount
      {
      public:
       double withdraw(double amount)
       {
        BankAccount::withdraw(amount);
        if (balance < 0)    goneOverdrawn = true;   return balance;  }  string toString()  {   string str = BankAccount::toString() + ", " +           (premium ? "Premium, " : "Normal, ") +           (goneOverdrawn ? "gone overdrawn" : "not gone overdrawn");   return str;  }  ...
  10. Virtual Destructors
    • If a superclass defines one or more virtual methods...
      • Then the superclass should also define a virtual destructor
      • Ensures that if derived classes define their own destructor, the correct destructor will always be called when objects are deleted
    • Example:
      class BankAccount
      {
      public:
       virtual ~ BankAccount();
       ...
      };
Lab
  1. Defining a derived class
    • Define a class named Student that inherits from Person (i.e. a student "is a kind of" person). Implement the following additional details in Student:
      • Data members for the student's subject (etc.)
        string subject;
        int grade;
      • View code file.
      • A constructor, to initialize a student (call the base constructor via the initialization list)
        Student::Student(const string & n, int a, const string & s, int g)
         :Person(n, a),
         subject(s),
         grade(g)
        {
         cout << "\nStudent object created\n"; }
      • A ToString() method, to return a textual representation of the student's info. Make use of the ToString() method in the base class, to format inherited data
        string Student::ToString() const
        {
         stringstream stm;
         stm << Person::ToString() << " " << subject << " " << grade;  return stm.str(); }
      • View code file.
      • A destructor, to output a simple message such as "goodbye from Student dtor"
        Student::~Student()

         cout << "\nGoodbye from Student dtor\n"; }
      • View code file.
    • In main(), write some simple code to test your Student class. Observe the order in which constructors and destructors fire.
      Student * s1 = new Student("Bam Bam", 12, "Hunting Mammoths", 9);
      cout << s1->GetName() << endl; cout << s1->GetAge() << endl; cout << s1->ToString() << endl;
    • View code file.

Lab 3: Polymorphism

Lab 3: Polymorphism
  1. What is Polymorphism?
    • Greek for "many forms"
    • In an OO context:
      • You can have many different "kinds" of object (e.g. many different kinds of bank accounts)
      • Your application can treat them all in the same way
      • Your application doesn't need to know which particular kind of object it's using at any given moment
  2. The Principle of Substitutability
    • Polymorphism is facilitated via "substitutability"
      • A superclass pointer/reference can refer to any subclass object
        void demoPolymorphism() {
         BankAccount * sa = new SavingsAccount("Mickey", true);
         BankAccount * ca = new CurrentAccount("Donald", 50);
         processAccount(sa);
         processAccount(ca);
        }
        void processAccount(BankAccount * account) {
         ...
         // The compiler knows "account" points to a BankAccount object or some subclass,
         // but it doesn't know which particular subclass.
         // Therefore the compiler only lets you access members defined in BankAccount. 
         // You can't access SavingsAccount-specific or CreditAccount-specific members.
         // This is good - your code is more "general purpose", so you won't have to modify
         // your code if someone defines a new kind of BankAccount subclass in the future.
         ...
        }
  3. Polymorphism in Action
    • What happens when you invoke an instance method via a superclass pointer/reference?
      • The "correct" version of the method is invoked, depending on the actual type of object currently pointed to
    • Specifically, this is what polymorphism boils down to:
      • If the pointer/reference actually points to a subclass object...
      • And that subclass has overridden the method...
      • The subclass's version of the method is called
        void processAccount(BankAccount * account)
        {
         account->withdraw(200);
         account->deposit(300);
         cout << account->toString() << endl; }
  4. Polymorphic Collections
    • A polymorphic collection (or array) can hold different types of subclass objects
      • Achieved by specifying a superclass-pointer as the generic type parameter (or as the array type)
    • Allows you to insert any type of subclass object into the collection/array
      • When you access items in the collection/array, you are working with superclass pointers
        List<BankAccount*> accounts;
        accounts.push_front(new SavingsAccount("Pluto", true));
        accounts.push_front(new CurrentAccount("Goofy", 50));
        ...
        for (list<BankAccount*>::iterator it = accounts.begin(); it != accounts.end(); it++)
        {
         processAccount(*it);
        }
  5. Run-Time Type Information (RTTI)
    • Classes that have virtual methods support run-time type information (RTTI)
      • Allows client code to determine the exact type of an object
      • Similar to instanceof in Java, and is in C#
    • To use RTTI in your client code:
      • #include the <typeinfo> standard header
      • Use the typeid operator to determine the actual object type
        #include <typeinfo>
        void SomeFunction(BankAccount * account)
        {
         if (typeid(*account) == typeid(SavingsAccount))
         {
          ... we know it's really a SavingsAccount object ...
         }
        }
Lab
  1. Defining another derived class
    • Define an Employee class. An employee is a kind of person, with these additional features:
      • A salary and a job grade
        private:
         int jobgrade;
        protected:
         double salary;
      • View code file.
      • A method to give the employee promotion (increase salary by £1000 per job-grade-rise)
        void Employee::Promote()
        {
         salary += jobgrade * 1000;
        }
      • A method to calculate the number of years to retirement (everyone retires at 65 currently)
        int Employee::YearsToRetire() const
        {
         return 65 - GetAge();
        }
      • View code file.
      • Housekeeping methods (constructor(s), destructor, ToString(), etc.)Employee::Employee(const string & n, int a, double sal, int jg)
         : Person(n, a),
          salary(sal),
          jobgrade(jg)
        {
         cout << "\nEmployee object created\n"; } Employee::~Employee() {   cout << "\nGoodbye from Employee dtor\n"; } string Employee::ToString() const {  stringstream stm;  stm << Person::ToString() << " " << salary << " " << jobgrade;  return stm.str(); }
      • View code file.
    • Test this new class from main()
      Employee * d1 = new Employee("Barney", 32, 34000, 3);
      cout << d1->GetName() << endl; cout << d1->GetSalary() << endl; cout << d1->ToString() << endl; d1->Promote();
      cout << d1->GetSalary() << endl; cout << d1->YearsToRetire() << endl;
    • View code file.

Lab 4: Pure virtual methods

Lab 4: Pure virtual methods
  1. Overview
    • When you define an inheritance hierarchy, the superclass must list all the methods that will be available to the client
      • The problem is, the superclass might not know how to actually implement some of these methods
      • For example, the implementation details might vary completely across all the subclasses
    • In such cases, declare the method as "pure virtual"
      • The superclass does not provide a method body
      • Instead, each subclass is obliged to implement the method
      • Note, this is equivalent to abstract methods in Java and C#
  2. Defining Pure Virtual Methods
    • In the superclass:
      class BankAccount
      {
       ...
       virtual string getTermsAndConditions() = 0;
       virtual double getGuaranteedLimit() = 0;
       ...
      };
    • The subclass is obliged to override pure-virtual methods:
      class SavingsAccount : public BankAccount
      {
       ...
       string getTermsAndConditions() { ... }
       double getGuaranteedLimit() { ... }
       ...
      };
  3. Abstract Classes
    • If a class defines one or more pure-virtual methods...
      • The class is "abstract"
      • You are not allowed to create an instance of that class (because some of its methods are unfulfilled)
    • This also applies for a subclass that hasn't overridden all the pure-virtual methods from the superclass
      • ...because the methods remain unfulfilled
    • Note for Java and C# programmers:

 

Well done. You have completed the tutorial in the C++ course. The next tutorial is

12. Operator Overloading


Back to beginning
Copyright © 2016 TalkIT®






If you liked this post, please comment with your suggestions to help others.
If you would like to see more content like this in the future, please fill-in our quick survey.
Scroll to Top