DMarlon Studio

Lesson 3 · 3 of 20

Inheritance and virtual functions · C++

Inherit from a C++ class and replace its methods with virtual and override, and see object slicing in action, with a staff payroll.

C++ · Inheritance and virtual functions in DMarlon Studio: the example's code and its output in the terminal

class Manager : public Employee says a manager is an employee: it inherits the name, the base salary and the payslip() method, and adds its own bonus_. `protected` members are visible to derived classes such as Manager, but not to outside code.

Manager's constructor calls the base constructor first, in its initializer list: : Employee(name, base), bonus_(bonus). The employee part is always built before the manager part.

A `virtual` function can have a different version in a derived class, marked with `override` (so the compiler checks that it really replaces one in the base). Manager::pay() reuses the base version through Employee::pay() and adds the bonus: 2800 + 650 = 3450.

The interesting part is that payslip() is only written in Employee, yet calling it on marc uses Manager::pay() and Manager::role(): virtual calls are resolved at run time, from the real object. The same happens through the reference const Employee& ref = marc. The base has a virtual destructor, virtual ~Employee() = default;, so destroying a manager through the base is safe.

By contrast, Employee copy = marc; copies only the employee part: this is slicing. copy is a real Employee, with no bonus, and earns 2800. To get polymorphism you have to work with references or pointers, as in the next lesson.

Code · main.cpp

#include <iostream>#include <string>class Employee {public:    Employee(std::string name, double base) : name_(name), base_(base) {}    virtual ~Employee() = default;    // virtual: a derived class may replace it with its own version.    virtual double pay() const { return base_; }    virtual std::string role() const { return "Employee"; }    void payslip() const { std::cout << name_ << " (" << role() << "): " << pay() << " EUR\n"; }protected:                         // visible to derived classes, hidden from the rest    std::string name_;    double base_;};class Manager : public Employee {      // a Manager is an Employee    double bonus_;public:    Manager(std::string name, double base, double bonus) : Employee(name, base), bonus_(bonus) {}    double pay() const override { return Employee::pay() + bonus_; }    std::string role() const override { return "Manager"; }};int main() {    Employee eva("Eva", 2100);    Manager marc("Marc", 2800, 650);    eva.payslip();    marc.payslip();                // payslip() is Employee's, but calls Manager::pay    const Employee& ref = marc;    // a base reference to a Manager    std::cout << "Via Employee&: " << ref.pay() << " EUR\n";    Employee copy = marc;          // slicing: only the Employee part is copied    copy.payslip();}

Output · c++ -std=c++17 main.cpp -o main && ./main

Eva (Employee): 2100 EUR
Marc (Manager): 3450 EUR
Via Employee&: 3450 EUR
Marc (Employee): 2800 EUR

Key points

  • class B : public A makes B inherit A's members and methods.
  • A derived class's initializer list builds the base first.
  • virtual in the base and override in the derived class replace a method.
  • Virtual calls use the object's real type, even from inside the base.
  • Copying a derived object into a base variable slices it.

Exercise

Create an Intern class that derives from Employee, earns 60% of the base salary and returns "Intern" from role(). Print the payslip of an intern with a base of 1500 euros.