¿Cómo llamar a una función de clase primaria desde una función de clase derivada?


604

¿Cómo llamo a la función padre desde una clase derivada usando C ++? Por ejemplo, tengo una clase llamada parent, y una clase llamada childque se deriva de padre. Dentro de cada clase hay una printfunción. En la definición de la función de impresión del niño, me gustaría llamar a la función de impresión de los padres. ¿Cómo haría para hacer esto?


Todas las soluciones anteriores suponen que su función de impresión es un método estático. ¿Es este el caso? Si el método no es estático, las soluciones anteriores no son relevantes.
hhafez

14
hhafez, está equivocado, la sintaxis base :: function () se parece a la sintaxis de llamada a un método estático, pero funciona, por ejemplo, en este contexto.
Motti

2
No usaría el MSVC __super ya que es específico de la plataforma. Aunque es posible que su código no se ejecute en ninguna otra plataforma, usaría las otras sugerencias, ya que lo hacen según el lenguaje previsto.
Avance el


El antipatrón donde las clases derivadas siempre se requieren para llamar a las funciones de clase principal es Call super
Rufus

Respuestas:


775

Me arriesgaré a decir lo obvio: llama a la función, si está definida en la clase base, está automáticamente disponible en la clase derivada (a menos que sea private).

Si hay una función con la misma firma en la clase derivada, puede desambiguarla agregando el nombre de la clase base seguido de dos puntos base_class::foo(...). Debe tener en cuenta que, a diferencia de Java y C #, C ++ no tiene una palabra clave para "la clase base" ( supero base) ya que C ++ admite la herencia múltiple, lo que puede generar ambigüedad.

class left {
public:
    void foo();
};

class right {
public:
    void foo();
};

class bottom : public left, public right {
public:
    void foo()
    {
        //base::foo();// ambiguous
        left::foo();
        right::foo();

        // and when foo() is not called for 'this':
        bottom b;
        b.left::foo();  // calls b.foo() from 'left'
        b.right::foo();  // call b.foo() from 'right'
    }
};

Por cierto, no puede derivar directamente de la misma clase dos veces, ya que no habrá forma de referirse a una de las clases base sobre la otra.

class bottom : public left, public left { // Illegal
};

31
¿Por qué te gustaría heredar de la misma clase dos veces?
Paul Brewczynski

65
@bluesm: en la OOP clásica no tiene mucho sentido, pero en la programación genérica, template<class A, class B> class C: public A, public B {};dos tipos pueden ser iguales por razones que dependen de cómo se usa el código (lo que hace que A y B sean iguales), pueden ser dos o tres abstracción de alguien que no sabe lo que hiciste.
Emilio Garavaglia

8
Creo que es útil agregar, que esto llamará al método de la clase principal, incluso si no se implementa directamente en la clase principal, sino que se implementa en una de las clases principales en la cadena de herencia.
Maxim Lavrov

44
En una nota al margen, me enfureció cuando intenté poner esto en un archivo cpp. Tuve 'usando el espacio de nombres estándar'. 'left' se define en algún lugar de ese espacio de nombres. El ejemplo no compilaría, me volvía loco :). Luego cambié 'izquierda' a 'izquierda'. Gran ejemplo por cierto.
Mathai

72
@Mathai Y es por eso que no debes usar using namespace std.
JAB

193

Dada la clase principal llamada Parenty la clase secundaria nombrada Child, puede hacer algo como esto:

class Parent {
public:
    virtual void print(int x);
}

class Child : public Parent {
    void print(int x) override;
}

void Parent::print(int x) {
    // some default behavior
}

void Child::print(int x) {
    // use Parent's print method; implicitly passes 'this' to Parent::print
    Parent::print(x);
}

Tenga en cuenta que Parentes el nombre real de la clase y no una palabra clave.


Por supuesto, esto solo sería útil si la llamada base se intercalara con otra lógica, de lo contrario no tendría sentido anular la función, por lo que tal vez sea un poco demasiado puntual;)
subrayado_d

1
@underscore_d en realidad, es útil incluso si la llamada base no se intercala con otra lógica. Digamos que la clase padre hace casi todo lo que quieres, pero expone un método foo () que no quieres que usen los usuarios del niño, ya sea porque foo () no tiene sentido en el niño o las llamadas externas al niño arruinarán lo que es el niño haciendo. Por lo tanto, el niño puede usar parent :: foo () en ciertas situaciones, pero proporciona una implementación de foo para que no se llame al foo () de parent.
iheanyi

@iheanyi Suena interesante, pero lo siento, todavía no lo entiendo. ¿Es foo()aquí print()una función análoga o separada? Y qué se refiere con el uso de privatela herencia para ocultar detalles heredado de la base, y proporcionar publicsombra funciones para las cosas que no desea exponer?
underscore_d

@underscore_d Sí, foo()fue análogo a print(). Permítanme volver a usarlo, print()ya que creo que tendría más sentido en este contexto. Digamos que alguien creó una clase que realizó un conjunto de operaciones en un tipo de datos en particular, expuso algunos accesores y tuvo un print(obj&)método. Necesito una nueva clase que funcione, array-of-objpero todo lo demás es igual. La composición da como resultado una gran cantidad de código duplicado. Minimiza la herencia que, en print(array-of-obj&) la llamada bucle print(obj&), pero no desea que los clientes llaman print(obj&), porque no tiene sentido para los que lo hagan
iheanyi

@underscore_d Esto se basa en el supuesto de que no puedo refactorizar las partes comunes de la clase principal original o que hacerlo es increíblemente costoso. La herencia privada podría funcionar, pero luego pierde los accesos públicos en los que confiaba y, por lo tanto, necesitaría duplicar el código.
iheanyi

32

Si se llama a su clase base y se llama a Basesu función FooBar(), puede llamarla directamente usandoBase::FooBar()

void Base::FooBar()
{
   printf("in Base\n");
}

void ChildOfBase::FooBar()
{
  Base::FooBar();
}

28

En MSVC hay una palabra clave específica de Microsoft para eso: __super


MSDN: le permite declarar explícitamente que está llamando a una implementación de clase base para una función que está anulando.

// deriv_super.cpp
// compile with: /c
struct B1 {
   void mf(int) {}
};

struct B2 {
   void mf(short) {}

   void mf(char) {}
};

struct D : B1, B2 {
   void mf(short) {
      __super::mf(1);   // Calls B1::mf(int)
      __super::mf('s');   // Calls B2::mf(char)
   }
};


55
Eh, preferiría que typdefel padre sea algo así super.
Thomas Eding

26
No intentaré justificar el uso de __super; Lo mencioné aquí como una sugerencia alternativa. Los desarrolladores deben conocer su compilador y comprender los pros y los contras de sus capacidades.
Andrey

13
Prefiero desalentar a cualquiera de usarlo, ya que dificulta severamente la portabilidad del código.
Erbureth dice Reinstate Monica

26
No estoy de acuerdo con Andrey: los desarrolladores deberían conocer el estándar y no deberían tener que preocuparse por las características del compilador, si consideramos escribir un software que sea principalmente independiente del compilador, lo cual creo que es una buena idea de todos modos porque tarde o temprano en proyectos grandes, compiladores múltiples De todos modos se utilizan.
Gabriel

77
"Los desarrolladores deben conocer su compilador" este razonamiento, y la inclusión de características no estándar, es lo que llevó a IE6 ...
e2-e4

5

Si el modificador de acceso de la función miembro de la clase base está protegido O público, puede llamar a la función miembro de la clase base desde la clase derivada. Se puede realizar una llamada a la función miembro no virtual y virtual de la clase base desde la función miembro derivada. Por favor, consulte el programa.

#include<iostream>
using namespace std;

class Parent
{
  protected:
    virtual void fun(int i)
    {
      cout<<"Parent::fun functionality write here"<<endl;
    }
    void fun1(int i)
    {
      cout<<"Parent::fun1 functionality write here"<<endl;
    }
    void fun2()
    {

      cout<<"Parent::fun3 functionality write here"<<endl;
    }

};

class Child:public Parent
{
  public:
    virtual void fun(int i)
    {
      cout<<"Child::fun partial functionality write here"<<endl;
      Parent::fun(++i);
      Parent::fun2();
    }
    void fun1(int i)
    {
      cout<<"Child::fun1 partial functionality write here"<<endl;
      Parent::fun1(++i);
    }

};
int main()
{
   Child d1;
   d1.fun(1);
   d1.fun1(2);
   return 0;
}

Salida:

$ g++ base_function_call_from_derived.cpp
$ ./a.out 
Child::fun partial functionality write here
Parent::fun functionality write here
Parent::fun3 functionality write here
Child::fun1 partial functionality write here
Parent::fun1 functionality write here

1
Gracias por traer algunos ejemplos con virtual!
M.Ioan

5

Llame al método principal con el operador de resolución de alcance principal.

Parent :: method ()

class Primate {
public:
    void whatAmI(){
        cout << "I am of Primate order";
    }
};

class Human : public Primate{
public:
    void whatAmI(){
        cout << "I am of Human species";
    }
    void whatIsMyOrder(){
        Primate::whatAmI(); // <-- SCOPE RESOLUTION OPERATOR
    }
};

-15
struct a{
 int x;

 struct son{
  a* _parent;
  void test(){
   _parent->x=1; //success
  }
 }_son;

 }_a;

int main(){
 _a._son._parent=&_a;
 _a._son.test();
}

Ejemplo de referencia


2
¿Podría editar una explicación de por qué / cómo este código responde a la pregunta? Se desaconsejan las respuestas de solo código, porque no son tan fáciles de aprender como el código con una explicación. Sin una explicación, lleva mucho más tiempo y esfuerzo comprender lo que se estaba haciendo, los cambios realizados en el código o si el código es útil. La explicación es importante tanto para las personas que intentan aprender de la respuesta como para quienes evalúan la respuesta para ver si es válida o si merece la pena votar.
Makyen

3
Esta respuesta es sobre clases anidadas mientras que la pregunta era sobre clases derivadas (aunque las palabras 'padre' y 'hijo' son un poco confusas) y, por lo tanto, no responde a la pregunta en absoluto.
Johannes Matokic
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.