Método de clase base de llamadas de C++

Abdul Mateen 12 octubre 2023
  1. Herencia en C++
  2. Problemas al llamar al método de clase base
Método de clase base de llamadas de C++

Este tutorial discutirá cómo llamar al método de la clase principal desde una clase secundaria usando C++. Primero, actualizaremos rápidamente la herencia.

Más adelante, analizaremos cómo llamar a métodos de clase base en diferentes escenarios y problemas relacionados.

Herencia en C++

La herencia es una característica poderosa de la programación orientada a objetos. A través de la herencia, las clases secundarias pueden compartir atributos y funciones de la clase principal, lo que facilita la reutilización en OOP.

En la herencia, dos clases entran en una relación conocida como relación padre-hijo, base-derivada o generalizada-especializada; sin embargo, la jerga derivada de la base se adopta con mayor frecuencia en lugar de padre e hijo.

Sintaxis de la herencia en C++:

class Parent {
  ..
};
class Child : InheritanceSpecifier Parent {
  ...
};

Para crear una clase derivada, tenemos que escribir InheritanceSpecifier seguido del nombre de la clase base. Aquí, el Especificador de herencia puede ser público, privado o protegido.

Además, la herencia puede ser múltiple si se especifican los nombres de varias clases principales durante la herencia. Puede encontrar más información sobre los tipos de herencia aquí.

Usaremos la herencia pública a lo largo del artículo para simplificar las cosas. Podemos llamar a los métodos de la clase base (si no son privados) dentro de la clase secundaria y con el objeto de la clase secundaria, como se demuestra en el código a continuación.

class P {
 public:
  void pf() { cout << "Parent class function\n"; }
};
class C : public P {
 public:
  void cf() {
    pf();
    cout << "Child class function\n";
  }
};
int main() {
  C cObj;
  cObj.cf();
  cObj.pf();
  return 0;
}

Creamos una clase principal, P, que se hereda públicamente de la clase secundaria C. La función cf() en la clase secundaria C llama al método base pf().

De manera similar, en el código del controlador (es decir, main()), el objeto secundario cObj llama directamente al método de la clase base.

Producción :

Parent class function
Child class function
Parent class function

El ejemplo anterior fue muy trivial para comprender la llamada de la función de clase base: dentro de la clase secundaria y con el objeto de la clase secundaria.

Sin embargo, las situaciones más complejas pueden requerir precaución adicional al llamar a los métodos base, como se explica en los siguientes pasajes.

Problemas al llamar al método de clase base

Parece que no hay problema para llamar al método de la clase base en el código y la salida anteriores. Sin embargo, surgen algunos problemas relacionados, como si las clases padre e hijo tienen la misma función o si el hijo tiene dos o más padres que tienen la misma función.

Analicemos estos temas uno por uno.

Padre e hijo tienen la misma función

Las clases principal y secundaria pueden tener las mismas funciones y, en algunos casos, es posible que queramos llamar a la función de la clase principal con el objeto de la clase secundaria, o podemos llamar a la función de la clase principal dentro de la clase secundaria.

Primero, veamos el código que demuestra el escenario.

class Parent {
 public:
  void f() { cout << "Parent class function\n"; }
};
class Child : public Parent {
 public:
  void f() { cout << "Child class function\n"; }
};
int main() {
  Child cObj;
  cObj.f();
  return 0;
}

Producción :

Child class function

Aquí, tenga en cuenta que el objeto de la clase secundaria llama a la función f() de la clase secundaria (no del padre). Para llamar a la f() del padre a través del objeto de clase hijo, debemos usar el nombre de la clase padre con el operador de resolución de alcance (::), como se demuestra en el siguiente código.

class Parent {
 public:
  void f() { cout << "Parent class function\n"; }
};
class Child : public Parent {
 public:
  void f() { cout << "Child class function\n"; }
};
int main() {
  Child cObj;
  cObj.Parent::f();  // calling f() of parent class
  cObj.f();
  return 0;
}

Producción :

Parent class function
Child class function

Vea la segunda línea en la función main() donde hemos usado el nombre de la clase principal con el operador de resolución de alcance. En consecuencia, en la primera línea de la salida, podemos ver el mensaje impreso por f() de la clase padre.

Se debe seguir la sintaxis exacta para llamar a la función de clase base dentro de una clase secundaria, como se muestra en el código a continuación.

class Parent {
 public:
  void f() { cout << "Parent class function\n"; }
};
class Child : public Parent {
 public:
  void f() {
    Parent::f();
    cout << "Child class function\n";
  }
};
int main() {
  Child cObj;
  cObj.f();
  return 0;
}

Producción :

Parent class function
Child class function

Ambigüedad de herencia múltiple

El segundo problema de llamar al método de la clase base es que varios padres de la clase (dos o más) pueden tener el mismo método. En este caso, el compilador confunde la selección de una clase base particular para llamar al método porque el mismo método existe en varias clases base.

Veamos un código que generará ambigüedad en tiempo de compilación para el compilador mientras selecciona una clase base particular para llamar al método base.

class P1 {
 public:
  void fun() { cout << "Fun from P1\n"; }
};
class P2 {
 public:
  void fun() { cout << "Fun from P2\n"; }
};
class C : public P1, public P2 {};
int main() {
  C objC;
  objC.fun();
  return 0;
}

Aquí, la clase C se hereda de dos clases base (es decir, P1 y P2) y ambos padres tienen un método fun() con la misma firma.

Ahora, cuando el código del controlador llama a la función fun() del objeto secundario (objC), confunde al compilador al seleccionar la clase base correcta.

Veamos el mensaje del compilador.

base_class_3.cpp: In function int main():
base_class_3.cpp:16:7: error: request for member fun is ambiguous
  objC.fun();

El mensaje muestra claramente que el compilador está confundido. Nuevamente, la solución es la misma que usa el nombre de la clase con el operador de resolución de alcance.

Consulte el código y el resultado relacionado.

class P1 {
 public:
  void fun() { cout << "Fun from P1\n"; }
};
class P2 {
 public:
  void fun() { cout << "Fun from P2\n"; }
};
class C : public P1, public P2 {};
int main() {
  C objC;
  objC.P1::fun();
  objC.P2::fun();
  return 0;
}

Producción :

Fun from P1
Fun from P2

Consulte las líneas 2 y 3 en main(), donde los nombres de clase P1 y P2 se utilizan con el operador de resolución de alcance. En consecuencia, en la salida, puede ver la salida de ambas funciones base.

Artículo relacionado - C++ Inheritance