DMarlon Studio

Lección 3 · 3 de 20

Herencia y funciones virtuales · C++

Herede de una clase en C++ y sustituya sus métodos con virtual y override; descubra también el recorte de objetos, con una nómina de empleados.

C++ · Herencia y funciones virtuales en DMarlon Studio: el código del ejemplo y su salida en la terminal

class Manager : public Employee dice que un jefe es un empleado: hereda su nombre, su sueldo base y el método payslip(), y añade lo suyo, bonus_. Los miembros `protected` se ven desde las clases derivadas, como Manager, pero no desde fuera.

El constructor de Manager llama primero al de la clase base en su lista de inicialización: : Employee(name, base), bonus_(bonus). La parte de empleado siempre se construye antes que la de jefe.

Una función `virtual` puede tener otra versión en la clase derivada, marcada con `override` (así el compilador comprueba que de verdad sustituye a una de la base). Manager::pay() reutiliza la de la base con Employee::pay() y le suma la prima: 2800 + 650 = 3450.

Lo interesante es que payslip() solo está escrito en Employee, pero al llamarlo sobre marc usa Manager::pay() y Manager::role(): las llamadas virtuales se deciden al ejecutar, según el objeto real. Lo mismo ocurre con la referencia const Employee& ref = marc. La base lleva un destructor virtual, virtual ~Employee() = default;, para que destruir un jefe a través de la base sea seguro.

En cambio, Employee copy = marc; copia solo la parte de empleado: es el recorte (*slicing*). copy es un Employee de verdad, sin prima, y cobra 2800. Para aprovechar el polimorfismo hay que trabajar con referencias o punteros, como en la lección siguiente.

Código · 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();}

Salida · 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

Para recordar

  • class B : public A hace que B herede los miembros y métodos de A.
  • La lista de inicialización de la derivada construye primero la base.
  • virtual en la base y override en la derivada sustituyen un método.
  • Las llamadas virtuales usan el tipo real del objeto, también desde la base.
  • Copiar una derivada en una variable de la base la recorta.

Ejercicio

Cree una clase Intern que derive de Employee, cobre el 60 % del sueldo base y devuelva "Intern" en role(). Haga la nómina de una becaria con 1500 euros de base.