C++ Inheritance and Polymorphism: Practical Implementation Guide

Task 1: Publisher Class Hierarchy

The following code demonstrates inheritance and polymorphism using a publisher class hierarchy:

main.cpp

#include "publisher.hpp"
#include <vector>
#include <typeinfo>

using std::vector;

void demonstrate_polymorphism() {
   vector<Publisher*> media_collection;

   media_collection.push_back(new Book("Harry Potter", "J.K. Rowling"));
   media_collection.push_back(new Film("The Godfather", "Francis Ford Coppola"));
   media_collection.push_back(new Music("Blowing in the wind", "Bob Dylan"));

   for(auto &media_ptr: media_collection) {
        cout << "Pointer type: " << typeid(media_ptr).name() << endl;
        cout << "RTTI type: " << typeid(*media_ptr).name() << endl;
        media_ptr->publish();
        media_ptr->use();
        cout << endl;
   }
}

int main() {
    demonstrate_polymorphism();
}

publisher.hpp

#pragma once

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

// Abstract base class for media publications
class Publisher {
public:
    Publisher(const string &s = "");

public:
    virtual void publish() const = 0;
    virtual void use() const = 0;

protected:
    string title;
};

Publisher::Publisher(const string &s): title {s} {
}

// Book class derived from Publisher
class Book: public Publisher {
public:
    Book(const string &s = "", const string &a = "");

public:
    void publish() const override;
    void use() const override;

private:
    string author;
};

Book::Book(const string &s, const string &a): Publisher{s}, author{a} {
}

void Book::publish() const {
    cout << "Publishing book: 《" << title << "》 by " << author << endl;
}

void Book::use() const {
    cout << "Reading book: " << title << " by " << author << endl;
}

// Film class derived from Publisher
class Film: public Publisher {
public:
    Film(const string &s = "", const string &d = "");

public:
    void publish() const override;
    void use() const override;

private:
    string director;
};

Film::Film(const string &s, const string &d): Publisher{s}, director{d} {
}

void Film::publish() const {
    cout << "Publishing film: <" << title << "> directed by " << director << endl;
}

void Film::use() const {
    cout << "Watching film: " << title << " directed by " << director << endl;
}

// Music class derived from Publisher
class Music: public Publisher {
public:
    Music(const string &s = "", const string &a = "");

public:
    void publish() const override;
    void use() const override;

private:
    string artist;
};

Music::Music(const string &s, const string &a): Publisher{s}, artist{a} {
}

void Music::publish() const {
    cout << "Publishing music <" << title << "> by " << artist << endl;
}

void Music::use() const {
    cout << "Listening to music: " << title << " by " << artist << endl;
}

Task 2: Book Sales System

This task implements a book sales tracking system with sorting capabilities:

main.cpp

#include "booksale.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

// Comparison function for sorting by sales quantity
bool sort_by_quantity(const BookSale &item1, const BookSale &item2) {
    return item1.get_quantity() > item2.get_quantity();
}

void process_sales() {
    using namespace std;

    vector<BookSale> sales_records;

    int book_count;
    cout << "Enter number of books: ";
    cin >> book_count;

    cout << "Enter book sales records" << endl;
    for(int i = 0; i < book_count; ++i) {
        string title, author, translator, isbn;
        float price;
        cout << string(20, '-') << "Book " << i+1 << " Information" << string(20, '-') << endl;
        cout << "Enter title: "; cin >> title;
        cout << "Enter author: "; cin >> author;
        cout << "Enter translator: "; cin >> translator;
        cout << "Enter ISBN: "; cin >> isbn;
        cout << "Enter price: "; cin >> price;

        Book book(title, author, translator, isbn, price);

        float sale_price;
        int quantity_sold;

        cout << "Enter sale price: "; cin >> sale_price;
        cout << "Enter quantity sold: "; cin >> quantity_sold;

        BookSale record(book, sale_price, quantity_sold);
        sales_records.push_back(record);
    }

    // Sort by quantity sold
    sort(sales_records.begin(), sales_records.end(), sort_by_quantity);

    // Display sorted sales information
    cout << string(20, '=') << "Book Sales Statistics" << string(20, '=') << endl;
    for(auto &sale: sales_records) {
        cout << sale << endl;
        cout << string(40, '-') << endl;
    }
}

int main() {
    process_sales();
}

booksale.hpp

#pragma once

#include "book.hpp"
#include <iostream>
#include <string>
#include <iomanip>

using std::string;
using std::cout;
using std::endl;
using std::setw;

class BookSale {
public:
    BookSale(const Book &b, float price, int quantity);
    int get_quantity() const;
    
    friend std::ostream& operator<<(std::ostream &out, const BookSale &item);

private:
    Book book_info;         
    float sale_price;      // Selling price
    int quantity_sold;     // Number of copies sold
    float total_revenue;   // Revenue from sales
};

BookSale::BookSale(const Book &b, float price, int quantity): 
    book_info{b}, sale_price(price), quantity_sold{quantity} {  
    total_revenue = quantity_sold * sale_price;
}

int BookSale::get_quantity() const {
    return quantity_sold;
}

std::ostream& operator<<(std::ostream &out, const BookSale &item) {
    out << std::left;
    out << item.book_info << endl
        << setw(15) << "Price:" << item.sale_price << endl
        << setw(15) << "Quantity:" << item.quantity_sold << endl
        << setw(15) << "Revenue:" << item.total_revenue;

    return out;
}

book.hpp

#pragma once

#include <string>
#include <iostream>
#include <iomanip>

using std::string;
using std::ostream;
using std::endl;
using std::setw;
using std::left;

class Book {
public:
    Book(const string &title, const string &author, const string &translator, 
         const string &isbn, float price);

    friend ostream& operator<<(ostream &out, const Book &book);

private:
    string title;        // Book title
    string author;      // Author name
    string translator;  // Translator name
    string isbn;        // ISBN number
    float price;        // List price
};

Book::Book(const string &title, const string &author, const string &translator, 
           const string &isbn, float price) {
    this->title = title;
    this->author = author;
    this->translator = translator;
    this->isbn = isbn;
    this->price = price;
}

ostream& operator<<(ostream &out, const Book &book) {
    out << left;
    out << setw(15) << "Title:" << book.title << endl
        << setw(15) << "Author:" << book.author << endl
        << setw(15) << "Translator:" << book.translator << endl
        << setw(15) << "ISBN:" << book.isbn << endl
        << setw(15) << "Price:" << book.price;

    return out;
}

Task 3: Virtual Pet Simulation

This task demonstrates polymorphism through a virtual pet simulation:

virtual_pets.hpp

#pragma once
#include<string>
using std::string;

// Base class for robotic pets
class RoboticPet{
    public:
        RoboticPet(const string &name): pet_name{name} {}
        string get_name() const {
            return pet_name;
        }
        virtual string make_sound() const = 0;
    
    private:
        string pet_name;
};

// Robotic cat class
class RoboticCat: public RoboticPet{
    public:
        RoboticCat(const string &name): RoboticPet{name} {}
        string make_sound() const override {
            return "meow~";
        }
};

// Robotic dog class
class RoboticDog: public RoboticPet{
    public:
        RoboticDog(const string &name): RoboticPet{name} {}
        string make_sound() const override {
            return "woof woof~";
        }
};

main.cpp

#include <iostream>
#include <vector>
#include "virtual_pets.hpp"

void test_pets() {
    using namespace std;

    vector<RoboticPet*> pets;

    pets.push_back(new RoboticCat("Whiskers"));
    pets.push_back(new RoboticDog("Buddy"));

    for(auto &pet: pets)
        cout << pet->get_name() << " says " << pet->make_sound() << endl;
}

int main() {
    test_pets();
}

Task 4: Movie Database

This task implements a movie database with sorting capabilities:

movie.hpp

#pragma once
#include<string>
#include<iostream>
#include<iomanip>
using std::string;
using std::istream;
using std::ostream;
using std::setw;
using std::left;

class Movie {
    public:
        Movie(const string &title = "", const string &director = "", 
              const string &country = "", const int &year = 0);
        
        friend istream& operator>>(istream &is, Movie &movie) {
            cout << "Enter title: ";
            is >> movie.title;
            cout << "Enter director: ";
            is >> movie.director;
            cout << "Enter production country: ";
            is >> movie.country;
            cout << "Enter release year: ";
            is >> movie.release_year;
            return is;
        }
        
        friend ostream& operator<<(ostream &os, const Movie &movie) {
            os << left << setw(20) << movie.title 
               << setw(20) << movie.director 
               << setw(20) << movie.country 
               << setw(10) << movie.release_year;
            return os;
        }
        
        int get_year() const {
            return release_year;
        }
    
    private:
        string title;
        string director;
        string country;
        int release_year;
};

Movie::Movie(const string &title, const string &director, 
             const string &country, const int &year) {
    this->title = title;
    this->director = director;
    this->country = country;
    this->release_year = year;
}

bool compare_by_year(const Movie &a, const Movie &b) {
    return a.get_year() < b.get_year();
}

main.cpp

#include "movie.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

void process_movies() {
    using namespace std;
    
    int movie_count;
    cout << "Enter number of movies: ";
    cin >> movie_count;

    cout << "Enter " << movie_count << " movie records" << endl;
    vector<Movie> movie_collection;
    for(int i = 0; i < movie_count; ++i) {
        Movie m;
        cout << string(20, '-') << "Movie " << i+1 << " Entry" << string(20, '-') << endl;
        cin >> m;
        movie_collection.push_back(m);
    }

    // Sort by release year
    sort(movie_collection.begin(), movie_collection.end(), compare_by_year);

    cout << string(20, '=') << "Movie Database (by Release Year)" << string(20, '=') << endl;
    cout << left << setw(20) << "Title" << setw(20) << "Director" 
         << setw(20) << "Country" << setw(10) << "Year" << endl;
    cout << string(70, '-') << endl;
    for(auto &m: movie_collection)
        cout << m << endl;
}

int main() {
    process_movies();
}

Task 5: Complex Number Template

This task implements a tempalte class for complex numbers:

complex_number.hpp

#pragma once
#include<iostream>
#include<cmath>
using std::ostream;
using std::istream;

template<typename T> 
class ComplexNumber {
    public:
        ComplexNumber(const T &r = 0, const T &i = 0): real_part{r}, imag_part{i} {}
        
        ComplexNumber operator +=(const ComplexNumber &c) {
            real_part += c.real_part;
            imag_part += c.imag_part;
            return *this;
        }
        
        friend ComplexNumber operator+(const ComplexNumber &c1, const ComplexNumber &c2) {
            return ComplexNumber<T>(c1.real_part + c2.real_part, c1.imag_part + c2.imag_part); 
        }
        
        friend bool operator==(const ComplexNumber c1, const ComplexNumber c2) {
            return c1.real_part == c2.real_part && c2.imag_part == c1.imag_part;
        }
        
        friend ostream &operator<<(ostream &out, const ComplexNumber &c) {
            if(c.imag_part >= 0)
              out << c.real_part << "+" << c.imag_part << "i";
            else
              out << c.real_part << "-" << -c.imag_part << "i";
            return out;
        }
        
        friend istream &operator>>(istream &in, ComplexNumber &c) {
            in >> c.real_part >> c.imag_part;
            return in;
        }
        
        T get_real() const { return real_part; }
        T get_imag() const { return imag_part; }
        
    private:
        T real_part;
        T imag_part;
};

main.cpp

#include "complex_number.hpp"
#include <iostream>

using std::cin;
using std::cout;
using std::endl;
using std::boolalpha;

void test_int_complex() {
    ComplexNumber<int> c1(2, -5), c2(c1);

    cout << "c1 = " << c1 << endl;
    cout << "c2 = " << c2 << endl;
    cout << "c1 + c2 = " << c1 + c2 << endl;
    
    c1 += c2;
    cout << "c1 = " << c1 << endl;
    cout << boolalpha << (c1 == c2) << endl;
}

void test_double_complex() {
    ComplexNumber<double> c1, c2;
    cout << "Enter c1 and c2 (real and imaginary parts): ";
    cin >> c1 >> c2;
    cout << "c1 = " << c1 << endl;
    cout << "c2 = " << c2 << endl;

    cout << "c1.real = " << c1.get_real() << endl;
    cout << "c1.imag = " << c1.get_imag() << endl;
}

int main() {
    cout << "ComplexNumber template test with integers: " << endl;
    test_int_complex();

    cout << endl;

    cout << "ComplexNumber template test with doubles: " << endl;
    test_double_complex();
}

Task 6: Banking System

This task implements a banking system with inheritance and polymorphism:

banking_system.cpp

#include "account.h"
#include <cmath>
#include <iostream>
using namespace std;

double Account::total_balance = 0;

Account::Account(const Date& date, const string& id) : account_id(id), balance(0) {
    date.show();
    cout << "\t#" << account_id << " created" << endl;
}

void Account::record(const Date& date, double amount, const string& desc) {
    amount = floor(amount * 100 + 0.5) / 100;
    balance += amount;
    total_balance += amount;
    date.show();
    cout << "\t#" << account_id << "\t" << amount << "\t" << balance << "\t" << desc << endl;
}

void Account::display() const { 
    cout << account_id << "\tBalance: " << balance; 
}

void Account::error(const string& msg) const {
    cout << "Error(#" << account_id << "): " << msg << endl;
}

SavingsAccount::SavingsAccount(const Date& date, const string& id, double rate) 
    : Account(date, id), interest_rate(rate), acc(date, 0) {}

void SavingsAccount::deposit(const Date& date, double amount, const string& desc) {
    record(date, amount, desc);
    acc.change(date, get_balance());
}

void SavingsAccount::withdraw(const Date& date, double amount, const string& desc) {
    if (amount > get_balance()) {
        error("insufficient funds");
    }
    else {
        record(date, -amount, desc);
        acc.change(date, get_balance());
    }
}

void SavingsAccount::settle(const Date& date) {
    if (date.getMonth() == 1) {
        double interest = acc.get_sum(date) * interest_rate / (date - Date(date.getYear() - 1, 1, 1));
        if (interest != 0) record(date, interest, "interest");
        acc.reset(date, get_balance());
    }
}

CreditAccount::CreditAccount(const Date& date, const string& id, double credit_limit, 
                           double rate, double annual_fee) 
    : Account(date, id), credit_limit(credit_limit), interest_rate(rate), 
      annual_fee(annual_fee), acc(date, 0) {}

void CreditAccount::deposit(const Date& date, double amount, const string& desc) {
    record(date, amount, desc);
    acc.change(date, get_debt());
}

void CreditAccount::withdraw(const Date& date, double amount, const string& desc) {
    if (amount - get_balance() > credit_limit) {
        error("exceeds credit limit");
    }
    else {
        record(date, -amount, desc);
        acc.change(date, get_debt());
    }
}

void CreditAccount::settle(const Date& date) {
    double interest = acc.get_sum(date) * interest_rate;
    if (interest != 0) record(date, interest, "interest");
    if (date.getMonth() == 1) record(date, -annual_fee, "annual fee");
    acc.reset(date, get_debt());
}

void CreditAccount::display() const {
    Account::display();
    cout << "\tAvailable credit: " << get_available_credit();
}

account.h

#pragma once
#include "date.h"
#include "accumulator.h"
#include <string>
using std::string;

class Account {
private:
    string account_id;
    double balance;
    static double total_balance;
protected:
    Account(const Date& date, const string& id);
    void record(const Date& date, double amount, const string& desc);
    void error(const string& msg) const;
public:
    const string& get_id() { return account_id; }
    double get_balance() const { return balance; }
    static double get_total() { return total_balance; }
    virtual void deposit(const Date& date, double amount, const string& desc) = 0;
    virtual void withdraw(const Date& date, double amount, const string& desc) = 0;
    virtual void settle(const Date& date) = 0;
    virtual void display() const;
};

class SavingsAccount : public Account {
private:
    Accumulator acc;
    double interest_rate;
public:
    SavingsAccount(const Date& date, const string& id, double rate);
    double get_rate() const { return interest_rate; }
    void deposit(const Date& date, double amount, const string& desc);
    void withdraw(const Date& date, double amount, const string& desc);
    void settle(const Date& date);
};

class CreditAccount : public Account {
private:
    Accumulator acc;
    double credit_limit;
    double interest_rate;
    double annual_fee;
    
    double get_debt() const {
        double balance = get_balance();
        return (balance < 0 ? balance : 0);
    }
public:
    CreditAccount(const Date& date, const string& id, double credit, double rate, double fee);
    double get_credit_limit() const { return credit_limit; }
    double get_interest_rate() const { return interest_rate; }
    double get_annual_fee() const { return annual_fee; }
    double get_available_credit() const {
        if (get_balance() < 0) return credit_limit + get_balance();
        else return credit_limit;
    }
    void deposit(const Date& date, double amount, const string& desc);
    void withdraw(const Date& date, double amount, const string& desc);
    void settle(const Date& date);
    void display() const;
};

accumulator.h

#pragma once
#include "date.h"

class Accumulator {
private:
    Date last_date;
    double value;
    double sum;
public:
    Accumulator(const Date& date, double value) 
        : last_date(date), value(value), sum{ 0 } {}
    
    double get_sum(const Date& date) const {
        return sum + value * (date - last_date);
    }
    
    void change(const Date& date, double value) {
        sum = get_sum(date);
        last_date = date;
        this->value = value;
    }
    
    void reset(const Date& date, double value) {
        last_date = date;
        this->value = value;
        sum = 0;
    }
};

date.h

#pragma once
class Date {
private:
    int year;
    int month;
    int day;
    int total_days;
public:
    Date(int year, int month, int day);
    int getYear() const { return year; }
    int getMonth() const { return month; }
    int getDay() const { return day; }
    int getMaxDay() const;
    bool isLeapYear() const {
        return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
    }
    void show() const;
    int operator-(const Date& date) const {
        return total_days - date.total_days;
    }
};

main.cpp

#include "account.h"
#include <iostream>
using namespace std;

int main() {
    Date current_date(2008, 11, 1);
    SavingsAccount savings1(current_date, "S3755217", 0.015);
    SavingsAccount savings2(current_date, "02342342", 0.015);
    CreditAccount credit(current_date, "C5392394", 10000, 0.0005, 50);
    
    Account* accounts[] = { &savings1, &savings2, &credit };
    const int account_count = sizeof(accounts) / sizeof(Account*);
    
    cout << "(d)eposit (w)ithdraw (s)how (c)hange day (n)ext month (e)xit" << endl;
    char command;
    
    do {
        current_date.show();
        cout << "\tTotal: " << Account::get_total() << "\tcommand> ";
        
        int index, day;
        double amount;
        string description;
        
        cin >> command;
        
        switch (command) {
            case 'd':
                cin >> index >> amount;
                getline(cin, description);
                accounts[index]->deposit(current_date, amount, description);
                break;
                
            case 'w':
                cin >> index >> amount;
                getline(cin, description);
                accounts[index]->withdraw(current_date, amount, description);
                break;
                
            case 's':
                for (int i = 0; i < account_count; i++) {
                    cout << "[" << i << "]";
                    accounts[i]->display();
                    cout << endl;
                }
                break;
                
            case 'c':
                cin >> day;
                if (day < current_date.getDay()) {
                    cout << "You cannot specify a previous day";
                }
                else if (day > current_date.getMaxDay()) {
                    cout << "Invalid day";
                }
                else {
                    current_date = Date(current_date.getYear(), current_date.getMonth(), day);
                }
                break;
                
            case 'n':
                if (current_date.getMonth() == 12) {
                    current_date = Date(current_date.getYear() + 1, 1, 1);
                }
                else {
                    current_date = Date(current_date.getYear(), current_date.getMonth() + 1, 1);
                }
                
                for (int i = 0; i < account_count; i++) {
                    accounts[i]->settle(current_date);
                }
                break;
        }
    } while (command != 'e');
    
    return 0;
}

Tags: C++ Inheritance Polymorphism Operator Overloading Templates

Posted on Fri, 11 Sep 2026 16:33:25 +0000 by 9mm