La sintaxis en la composición es bastante obvia, en cambio en la herencia, la sintaxis es nueva y diferente.
Cuando hereda, realmente se expresa "Esta nueva clase es como esta otra vieja clase". Se comienza el código proporcionando el nombre de la clase, como se realiza normalmente, pero antes de abrir la llave del cuerpo de la clase, se colocan dos puntos y el nombre de la clase base (o de las clases bases, separadas por comas, para herencia múltiple). Una vez realizado, automáticamente se consiguen todos los miembros y las funciones de la clase base. 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, que significa que Y contendrá todos los miembros de X y todas las funciones de X. De hecho, Y contiene un subobjeto X como si se hubiese creado un objeto X dentro de la clase Y en vez de heredar de X. Tanto los miembros objetos y la clase base son conocidos como subobjetos.
Todos los elementos privados de X continúan siendo privados en Y; esto es, aunque Y hereda de X no significa que Y pueda romper el mecanismo de protección. Los elementos privados de X continúan existiendo, ocupando su espacio - sólo que no se puede acceder a ellos directamente.
En main() observamos que los datos de Y están combinados con los datos de X porque sizeof(Y) es el doble de grande que el sizeof(X).
Observará que la clase base es precedida por public. Durante la herencia, por defecto, todo es privado. Si la clase base no estuviese precedida por public, significaría que todos los miembros públicos de la clase base serían privados en la clase derivada. Esto, en la mayoría de ocasiones no es lo deseado [51]; el resultado que se desea es mantener todos los miembros públicos de la clase base en la clase derivada. Para hacer esto, se usa la palabra clave public durante la herencia.
En change(), se utiliza a la función de la clase base permute(). La clase derivada tiene acceso directo a todas las funciones públicas de la clase base.
La función set() en 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, conseguirá las versiones de la clase base (esto es lo que esta ocurriendo dentro de main()). Pero si llamamos a set() en un objeto Y, conseguiremos la versión redefinida. Esto significa que si no deseamos un comportamiento de una función durante la herencia, se puede cambiar. (También se pueden añadir funciones completamente nuevas como change().)
Sin embargo, cuando redefinimos una función, puede ser que desee llamar a la versión de la clase base. Si, dentro de set(), simplemente llama a set(), conseguiremos una versión local de la función - una función recursiva. Para llamar a la versión de la clase base, se debe explícitamente utilizar el nombre de la clase base y el operador de resolución de alcance.