14.3.3. Combinación de composición y herencia

Por supuesto, puede usar la composición y la herencia a la vez. El siguiente ejemplo muestra la creación de una clase más compleja utilizando ambas.

//: C14:Combined.cpp
// Inheritance & composition

class A {
  int i;
public:
  A(int ii) : i(ii) {}
  ~A() {}
  void f() const {}
};

class B {
  int i;
public:
  B(int ii) : i(ii) {}
  ~B() {}
  void f() const {}
};

class C : public B {
  A a;
public:
  C(int ii) : B(ii), a(ii) {}
  ~C() {} // Calls ~A() and ~B()
  void f() const {  // Redefinition
    a.f();
    B::f();
  }
};

int main() {
  C c(47);
} ///:~

Listado 14.6. C14/Combined.cpp


C hereda de B y tiene un objeto miembro («está compuesto de») de tipo A. Puede ver que la lista de inicialización del constructor contiene llamadas tanto al constructor de la clase base como al constructor del objeto miembro.

La función C::f() redefine B::f(), que hereda, y también llama a la versión de la clase base. Además, llama a a.f(). Fíjese en que sólo se puede hablar de redefinición de funciones en la herencia; con un objeto miembro sólo se puede manipular su interfaz pública, no redefinirla. Además, si no se hubiera definido C::f(), llamar a f() sobre un objeto de la clase C no llamaría a a.f(), mientras que sí llamaría a B::f().

Llamadas automáticas al destructor

Aunque a menudo sea necesario realizar llamadas explícitas a los constructores en la lista de inicialización, nunca será necesario realizar llamadas explícitas a los destructores porque sólo existe un destructor para cada clase y éste no tiene argumentos. Sin embargo, el compilador asegura que se llama a todos los destructores, y eso significa a todos los destructores de la jerarquía completa, empezando por el de la clase más derivada y retrocediendo hasta la raíz.

Vale la pena destacar que los constructores y destructores son bastante peculiares en el sentido de que se llama a todos los de la jerarquía, mientras que en una función miembro normal sólo se llama a esa función, y no a ninguna de las versiones de la clase base. Si también desea llamar a la versión de la clase base de una función miembro normal que está redefiniendo, debe hacerlo explícitamente.