El cuadernillo

Herencia virtual en c++, consideraciones sobre los constructores de las clases bases virtuales

Introduction

Pedro Valiente Verde

Pedro Valiente Verde


LATEST POSTS

Programando ESP8266 (NodeMcu) como si fuera Arduino en Eclipse con Platformio 12th February, 2018

Sistemas de recomendación basados en contenido (Content-Based) 26th July, 2016

C++

Herencia virtual en c++, consideraciones sobre los constructores de las clases bases virtuales

Posted on .

La herencia virtual es genial, nos soluciona el problema del diamante pero esta medida se puede volver un inconveniente cuando la incorporamos por inercia, cuando heredamos virtualmente sin preocuparnos si estamos heredando interfaces o bien una clase con sus métodos y atributos.

En este post veremos las implicaciones que tiene heredar virtualmente una clase que no sea una interfaz .

El problema del diamante

Cuando creo una clase suelo componerlo por interfaces (que no es lo mismo que clases abstractas que pueden tener estado), de esta manera mato tres pájaros de un tiro:

  1. En tiempo de compilación se lanzará un error si me olvido de implementar algún método virtual puro.
  2. Utilizar el polimorfismo y abstraer de esta manera la implementación, pudiendo utilizar patrones de diseño como la Factoría o Estrategia en nuestras soluciones.
  3. Una implicación de lo anterior, si creamos nuestra clase por composición, donde la firma son interfaces, la etapa de los tests no será una pesadilla.

Pero hay veces que algunas interfaces heredan de la misma interfaz y luego componemos nuestra clase de dichas interfaces. Si no hemos tenido la precaución de añadirlo como herencia virtual, al compilar se quejará diciendo que la resolución del método es ambigua.

error: request for member ‘Tocar’ is ambiguous

Problema del diamante

La herencia múltiple se presenta muchas veces cuando creamos la interfaz de una clase a partir de interfaces más pequeñas. Incorporando la clave virtual en la herencia de la clase base de Der1 y Der2 se elimina la ambigüedad al crear sólamente un subobjeto de Base cuando instanciamos Instrumento

Incorporando la herencia virtual a lo loco

Imaginemos que empezamos a crear una clase a partir de una interfaz, y por inercia especificamos que todas nuestras herencias son virtuales como el ejemplo de abajo, en este caso, se llamará al constructor por defecto ya que la invocación de los constructores de las clases bases virtuales se deben realizar en la lista de inicialización de la clase más derivada.

Abuso de la herencia virtual

El propósito de la herencia virtual es asegurar que solamente creamos un objeto, para ello el compilador tiene unos mecanismos determinados como instanciar las clases bases virtuales antes de las demás. Por ello, si no tenemos precaución en instanciar Der1 en Der3 antes de Der2, el compilador utilizará el constructor por defecto.

Las clases virtuales base son especiales porque se inicializan en la última clase derivada y no por ninguna otra clase base intermedia que hereda de la clase base

De hecho solamente podemos invocar al constructor de clases intermedias si se heredan virtualmente, de otro modo,  el compilar daría un error parecido a este:

error: type ‘Der1’ is not a direct or virtual base of ‘Der3’

Conclusión

Se puede realizar herencia virtual de clases con atributos pero tiene un gran inconveniente:

  • La última clase derivada tiene que invocar al constructor de las clases bases virtuales que tienen atributos, por ende, tiene que conocer como se invoca y además si no queremos problemas, siempre se debe invocar de la misma forma (si estamos en el problema del diamante).

Sin embargo, esto no es un problema si seguimos las recomendaciones de heredar virtualmente solo interfaces, que al no tener atributos no necesitan un constructor por parámetros.

 


Un buen FAQ de la herencia multiple y virtual c++
Ploblema del diamente
Consideraciones de los constructores de las clases bases virtuales

 

 

Pedro Valiente Verde

Pedro Valiente Verde

http://p.valienteverde.com

There are no comments.
View Comments (0) ...
Navigation