14.2. Sintaxis de la herencia

La sintaxis de la composición es obvia; en cambio, la herencia tiene una forma nueva y diferente.

Cuando hereda, lo que está diciendo es «Esta nueva clase es como aquella vieja clase». Esto se expresa en el código escribiendo el nombre de la clase como de costumbre, pero antes de la llave de apertura del cuerpo de la clase, se colocan dos puntos y el nombre de la clase base (o de las clases base, separadas por comas, en el caso de la herencia múltiple). Al hacerlo, se obtienen automáticamente todos los atributos y funciones miembro de la clase base. He aquí un ejemplo:

//: C14:Inheritance.cpp
// Simple inheritance
#include "Useful.h"
#include <iostream>
using namespace std;

class Y : public X {
  int i; // Different from X's i
public:
  Y() { i = 0; }
  int change() {
    i = permute(); // Different name call
    return i;
  }
  void set(int ii) {
    i = ii;
    X::set(ii); // Same-name function call
  }
};

int main() {
  cout << "sizeof(X) = " << sizeof(X) << endl;
  cout << "sizeof(Y) = "
       << sizeof(Y) << endl;
  Y D;
  D.change();
  // X function interface comes through:
  D.read();
  D.permute();
  // Redefined functions hide base versions:
  D.set(12);
} ///:~

Listado 14.4. C14/Inheritance.cpp


Como se puede observar, Y hereda de X, lo que significa que Y contendrá todos los atributos de X y todas sus funciones miembro. De hecho, Y contiene un subobjeto de X, como si se hubiese creado un objeto miembro de tipo X dentro de Y en vez de heredar de X. Tanto a los objetos miembro como al espacio que ocupa la clase base se les llama subobjetos.

Todos los elementos privados de X continúan siendo privados en Y; esto es, el hecho de que Y herede de X no significa que Y pueda romper el mecanismo de protección. Los elementos privados de X siguen existiendo y ocupan espacio; sólo que no se puede acceder a ellos directamente.

En main() se puede ver que los atributos de Y están combinados con los de X, porque sizeof(Y) es el doble de sizeof(X).

Observará que la clase base va precedida de public. Durante la herencia, por defecto, todo es privado. Si la clase base no estuviese precedida de public, significaría que todos los miembros públicos de la clase base serían privados en la clase derivada. Esto casi nunca es lo que se desea[75]; el resultado deseado es que todos los miembros públicos de la clase base sigan siendo públicos en la clase derivada. Para ello se usa la palabra reservada public durante la herencia.

En change() se llama a la función permute() de la clase base. La clase derivada tiene acceso directo a todas las funciones públicas de la clase base.

La función set() de la clase derivada redefine la función set() de la clase base. Esto es, si llama a las funciones read() y permute() de un objeto Y, obtendrá las versiones de la clase base (puede ver que eso es lo que ocurre dentro de main()). Pero si llama a set() en un objeto Y, obtendrá la versión redefinida. Esto significa que si no le gusta la versión de una función que obtiene en la herencia, puede cambiar lo que hace. (También se pueden añadir funciones completamente nuevas, como change().)

Sin embargo, cuando redefine una función, puede que aún desee llamar a la versión de la clase base. Si, dentro de set(), simplemente llama a set(), obtendrá la versión local de la función, es decir, una llamada recursiva. Para llamar a la versión de la clase base, debe nombrar explícitamente la clase base mediante el operador de resolución de ámbito.



[75] En Java, el compilador no permite reducir el acceso de un miembro durante la herencia.