Tabla de contenidos
El concepto básico de la herencia múltiple (HM) suena bastante simple: puede crear un nuevo tipo heredando de más una una clase base. La sintaxis es exactamente la que espera, y en la medida en que los diagramas de herencia sean simples, la HM puede ser simple también.
Sin embargo, la HM puede presentar un buen número de situaciones ambiguas y extrañas, que se cubren en este capítulo. Pero primero, es útil tener algo de perspectiva sobre el asunto.
Antes de C++, el lenguaje orientado a objetos más popular era
Smaltalk. Smaltalk fue creado desde cero como un lenguaje
orientado a objetos. A menudo se dice que es puro, mientras que a
C++ se le llama lenguaje híbrido porque soporta múltiples
paradigmas de programación, no sólo el paradigma orientado a
objeto. Uno de las decisiones de diseño de Smalltalk fue que
todas las clases tendrían solo herencia simple, empezando en una
clase base (llamada Object
- ese es el
modelo para la jerarquía basada en objetos)
[18]
En Smalltalk no puede crear una nueva clase sin derivar de un
clase existente, que es la razón por la que lleva cierto tiempo
ser productivo con Smalltalk: debe aprender la librería de
clases antes de empezar a hacer clases nuevas. La jerarquía de
clases de Smalltalk es por tanto un único árbol monolítico.
Las clases de Smalltalk normalmente tienen ciertas cosas en
común, y siempre tienen algunas cosas en común (las
características y el comportamiento de
Object
), de modo que no suelen aparecer
situaciones en las que se necesite heredad de más de una clase
base. Sin embargo, con C++ puede crear tantos árboles de
herencia distintos como quiera. Por completitud lógica el
lenguaje debe ser capaz de combinar más de una clase a la vez -
por eso la necesidad de herencia múltiple.
No fue obvio, sin embargo, que los programadores requiriesen herencia múltiple, y había (y sigue habiendo) mucha discrepancia sobre si es algo esencial en C++. La HM fue añadida en cfront release 2.0 de AT&T en 1989 y fue el primer cambio significativo en el lenguaje desde la versión 1.0. [19] Desde entonces, se han añadido muchas características al Estándar C++ (las plantillas son dignas de mención) que cambian la manera de pensar al programar y le dan a la HM un papel mucho menos importante. Puede pensar en la HM como una prestación menor del lenguaje que raramente está involucrada en las decisiones de diseño diarias.
Uno de los argumentos más convincentes para la HM involucra a
los contenedores. Suponga que quiere crear un contenedor que
todo el mundo pueda usar fácilmente. Una propuesta es usar
void*
como tipo para el contenido. La propuesta de
Smalltalk, sin embargo, es hacer un contenedor que aloja
Object
, dado que
Object
es el tipo base de la jerarquía de
Smalltalk. Como todo en Smalltalk está derivado de
Object
, un contenedor que aloja
Object
s puede contener cualquier cosa.
Ahora considere la situación en C++. Suponga que el fabricante A
crea una jerarquía basada-en-objetos que incluye un conjunto de
contenedores incluye uno que desea usar llamado
Holder
. Después, se da cuenta de que la
jerarquía de clases del fabricante B contiene alguna clase que
también es importante para usted, una clase
BitImage
, por ejemplo, que contiene
imágenes. La única forma de hacer que un
Holder
de BitImage
es derivar de una nueva clase que derive también de
Object
, y así poder almacenarlos en el
Holder
, y
BitImage
:
Éste fue un motivo importante para la HM, y muchas librerías de clases están hechas con este model. Sin embargo, tal como se vio en el Capítulo 5, la aportación de las plantillas ha cambiado la forma de crear contenedores, y por eso esta situación ya no es un asunto crucial en favor de la HM.
El otro motivo por el que se necesita la HM está relacionado con
el diseño. Puede usar la HM intencionadamente para hacer un
diseño más flexible y útil (o al menos aparentarlo). Un ejemplo
de esto es el diseño de la librería original iostream
(que persiste hoy día, como
vio en el Capítulo 4.
Tanto iostream
como
ostream
son clases útiles por si mismas,
pero se pueden derivar simultáneamente por una clase que combina
sus características y comportamientos. La clase
ios
proporciona una combinación de las
dos clases, y por eso en este caso la HM es un mecanismo de
FIXME:code-factoring.
Sin importar lo que le motive a usar HM, debe saber que es más difícil de usar de lo que podría parecer.