¿Puedo llamar a la función virtual de una clase base si la estoy anulando?

340

Digamos que tengo clases Fooy Barconfiguro así:

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
        std::cout << x << std::endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        // I would like to call Foo.printStuff() here...
        std::cout << y << std::endl;
    }
};

Como se anotó en el código, me gustaría poder llamar a la función de la clase base que estoy anulando. En Java existe la super.funcname()sintaxis. ¿Es esto posible en C ++?

Alex
fuente
1
posible duplicado de llamada de función virtual de la clase base
Vladimir F
1
Para Googlers: tenga en cuenta que puede tener problemas como lo hice con el almacenamiento como una variable de miembro de clase que no es un puntero. Vea mi respuesta aquí: stackoverflow.com/questions/4798966/… Involucré nuevo / eliminar para corregir.
Andrew

Respuestas:

449

La sintaxis de C ++ es así:

class Bar : public Foo {
  // ...

  void printStuff() {
    Foo::printStuff(); // calls base class' function
  }
};
algo
fuente
11
¿Hay algún problema posible al hacer esto? ¿Es una mala práctica?
Robben_Ford_Fan_boy
36
@David: No, es perfectamente normal hacer esto, aunque podría depender de tu clase real si es realmente útil. Solo llame al método de clase base si hace algo que desea que suceda;).
algo
20
¡cuidado, este es un olor a código cuando tus clientes se REQUIEREN para hacerlo! (llamado call super requirement, échale un vistazo aquí: en.wikipedia.org/wiki/Call_super )
v.oddou
8
@ v.oddou: No hay nada de malo en llamar a la superclase cuando es útil. Esa página que ha vinculado tiene que ver con algún problema potencial específico relacionado con la herencia. Incluso dice que no hay nada de malo en llamar a la superclase en general: "Tenga en cuenta que es el requisito de llamar al padre el antipatrón. Hay muchos ejemplos en código real donde el método en la subclase aún puede desea la funcionalidad de la superclase, generalmente donde solo aumenta la funcionalidad principal ".
algo
26
Puede ser obvio para la mayoría, pero para completar, recuerde nunca hacer esto en constructores y destructores.
TigerCoding
123

Si,

class Bar : public Foo
{
    ...

    void printStuff()
    {
        Foo::printStuff();
    }
};

Es lo mismo que superen Java, excepto que permite llamar a implementaciones desde diferentes bases cuando tiene herencia múltiple.

class Foo {
public:
    virtual void foo() {
        ...
    }
};

class Baz {
public:
    virtual void foo() {
        ...
    }
};

class Bar : public Foo, public Baz {
public:
    virtual void foo() {
        // Choose one, or even call both if you need to.
        Foo::foo();
        Baz::foo();
    }
};
Alex B
fuente
77
Esta es una mejor respuesta que la seleccionada. Gracias.
Físico loco
Herencia múltiple? ¡Oh, Dios!
lamino
69

A veces necesitas llamar a la implementación de la clase base, cuando no estás en la función derivada ... Todavía funciona:

struct Base
{
    virtual int Foo()
    {
        return -1;
    }
};

struct Derived : public Base
{
    virtual int Foo()
    {
        return -2;
    }
};

int main(int argc, char* argv[])
{
    Base *x = new Derived;

    ASSERT(-2 == x->Foo());

    //syntax is trippy but it works
    ASSERT(-1 == x->Base::Foo());

    return 0;
}
Siempre entrenando
fuente
2
Tenga en cuenta que debe xser de tipo Base*y usar la Base::Foosintaxis para que esto funcione
TTimo
27

En caso de que haga esto para muchas funciones en su clase:

class Foo {
public:
  virtual void f1() {
    // ...
  }
  virtual void f2() {
    // ...
  }
  //...
};

class Bar : public Foo {
private:
  typedef Foo super;
public:
  void f1() {
    super::f1();
  }
};

Esto podría ahorrar un poco de escritura si desea cambiar el nombre de Foo.

MartinStettner
fuente
1
Una forma divertida de hacerlo, pero no funcionará con herencia múltiple.
Físico loco
55
¿Y qué pasa si tienes más de 1 clase base? De todos modos, es tonto y, a menudo, inútil tratar de doblar C ++ para parecerse a otro lenguaje. Solo recuerda el nombre de la base y llámalo así.
underscore_d
6

Si desea llamar a una función de la clase base desde su clase derivada, simplemente puede llamar dentro de la función anulada mencionando el nombre de la clase base (como Foo :: printStuff () ).

el código va aquí

#include <iostream>
using namespace std;

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
         cout<<"Base Foo printStuff called"<<endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        cout<<"derived Bar printStuff called"<<endl;
        Foo::printStuff();/////also called the base class method
    }
};

int main()
{
    Bar *b=new Bar;
    b->printStuff();
}

Una vez más, puede determinar en el tiempo de ejecución qué función llamar utilizando el objeto de esa clase (derivada o base). Pero esto requiere que su función en la clase base se marque como virtual.

código a continuación

#include <iostream>
using namespace std;

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
         cout<<"Base Foo printStuff called"<<endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        cout<<"derived Bar printStuff called"<<endl;
    }
};

int main()
{

    Foo *foo=new Foo;
    foo->printStuff();/////this call the base function
    foo=new Bar;
    foo->printStuff();
}
Tunvir Rahman Tusher
fuente
0

Mira esto...

#include <stdio.h>

class Base {
public:
   virtual void gogo(int a) { printf(" Base :: gogo (int) \n"); };    
   virtual void gogo1(int a) { printf(" Base :: gogo1 (int) \n"); };
   void gogo2(int a) { printf(" Base :: gogo2 (int) \n"); };    
   void gogo3(int a) { printf(" Base :: gogo3 (int) \n"); };
};

class Derived : protected Base {
public:
   virtual void gogo(int a) { printf(" Derived :: gogo (int) \n"); };
   void gogo1(int a) { printf(" Derived :: gogo1 (int) \n"); };
   virtual void gogo2(int a) { printf(" Derived :: gogo2 (int) \n"); };
   void gogo3(int a) { printf(" Derived :: gogo3 (int) \n"); };       
};

int main() {
   std::cout << "Derived" << std::endl;
   auto obj = new Derived ;
   obj->gogo(7);
   obj->gogo1(7);
   obj->gogo2(7);
   obj->gogo3(7);
   std::cout << "Base" << std::endl;
   auto base = (Base*)obj;
   base->gogo(7);
   base->gogo1(7);
   base->gogo2(7);
   base->gogo3(7);

   std::string s;
   std::cout << "press any key to exit" << std::endl;
   std::cin >> s;
   return 0;
}

salida

Derived
 Derived :: gogo (int)
 Derived :: gogo1 (int)
 Derived :: gogo2 (int)
 Derived :: gogo3 (int)
Base
 Derived :: gogo (int)
 Derived :: gogo1 (int)
 Base :: gogo2 (int)
 Base :: gogo3 (int)
press any key to exit

la mejor manera es usar la función base :: como say @sth

joseAndresGomezTovar
fuente
Como se explica en esta pregunta , esto no debería funcionar debido a la protectedherencia. Para emitir un puntero de clase base, debe usar la herencia pública.
Darkproduct
onlinegdb.com/r1PGIhep8
joseAndresGomezTovar
Interesante. Después de leer esta respuesta , pensé que con herencia protegida, el hecho de que Derivado se deriva de Base solo sería visible para la clase en sí y no para el exterior también.
Darkproduct
0

Sí, puedes llamarlo. La sintaxis de C ++ para llamar a la función de clase primaria en la clase secundaria es

class child: public parent {
  // ...

  void methodName() {
    parent::methodName(); // calls Parent class' function
  }
};

Lea más sobre la anulación de funciones .

Atif
fuente