Tabla de contenidos
Una de las características más atractivas de C++ es la reutilización de código. Pero para ser revolucionario, hay que ser capaz de hacer mucho más que copiar código y modificarlo.
Ése es el enfoque de C y no ha funcionado demasiado bien. Como casi todo en C++, la solución gira alrededor de la clase. Se reutiliza código creando nuevas clases, pero en vez de crearlas desde la nada, se utilizan clases existentes que alguien ya ha construido y depurado.
El truco consiste en utilizar las clases sin ensuciar el código existente. En este capítulo verá dos modos de hacerlo. El primero es bastante directo: simplemente se crean objetos de la clase existente dentro de la nueva clase. A esto se le llama composición porque la nueva clase está compuesta por objetos de clases ya existentes.
La segunda forma es más sutil. Se crea la nueva clase como un tipo de una clase existente. Literalmente se toma la forma de la clase existente y se le añade código, pero sin modificar la clase ya existente. A este acto mágico se le llama herencia, y la mayor parte del trabajo la realiza el compilador. La herencia es uno de los pilares de la programación orientada a objetos y tiene implicaciones adicionales que se explorarán en el Capítulo 15.
Resulta que gran parte de la sintaxis y el comportamiento son similares tanto en la composición como en la herencia (lo cual tiene sentido; ambas son formas de crear nuevos tipos a partir de tipos ya existentes). En este capítulo aprenderá estos mecanismos de reutilización de código.
En realidad, ha estado utilizando la composición todo el tiempo para crear clases. Simplemente ha estado componiendo clases principalmente con tipos predefinidos (y en ocasiones con objetos string). Resulta casi igual de fácil usar la composición con tipos definidos por el usuario.
Considere una clase que resulta útil por algún motivo:
//: C14:Useful.h // A class to reuse #ifndef USEFUL_H #define USEFUL_H class X { int i; public: X() { i = 0; } void set(int ii) { i = ii; } int read() const { return i; } int permute() { return i = i * 47; } }; #endif // USEFUL_H ///:~
Listado 14.1. C14/Useful.h
En esta clase los atributos son privados, de modo que es completamente seguro alojar un objeto de tipo X como objeto público en una nueva clase, lo que hace que la interfaz sea directa:
//: C14:Composition.cpp // Reuse code with composition #include "Useful.h" class Y { int i; public: X x; // Embedded object Y() { i = 0; } void f(int ii) { i = ii; } int g() const { return i; } }; int main() { Y y; y.f(47); y.x.set(37); // Access the embedded object } ///:~
Listado 14.2. C14/Composition.cpp
Para acceder a las funciones miembro del objeto alojado (al que se llama subobjeto) basta con otra selección de miembro.
Es más habitual hacer privados los objetos alojados, de modo que pasan a formar parte de la implementación subyacente (lo que significa que es posible cambiar la implementación si se desea). Las funciones de la interfaz pública de la nueva clase implican entonces el uso del objeto alojado, pero no imitan necesariamente la interfaz del objeto:
//: C14:Composition2.cpp // Private embedded objects #include "Useful.h" class Y { int i; X x; // Embedded object public: Y() { i = 0; } void f(int ii) { i = ii; x.set(ii); } int g() const { return i * x.read(); } void permute() { x.permute(); } }; int main() { Y y; y.f(47); y.permute(); } ///:~
Listado 14.3. C14/Composition2.cpp
Aquí, la función permute() se traslada a la interfaz de la nueva clase, pero el resto de funciones miembro de X se utilizan dentro de los miembros de Y.