Come chiamare una funzione di classe genitore dalla funzione di classe derivata?


604

Come posso chiamare la funzione genitore da una classe derivata usando C ++? Ad esempio, ho una classe chiamata parente una classe chiamata childche deriva dal genitore. All'interno di ogni classe c'è una printfunzione. Nella definizione della funzione di stampa del bambino vorrei fare una chiamata alla funzione di stampa dei genitori. Come potrei fare per fare questo?


Tutte le soluzioni di cui sopra presuppongono che la funzione di stampa sia un metodo statico. È questo il caso? Se il metodo non è statico, le soluzioni precedenti non sono pertinenti.
Hhafez,

14
hhafez, ti sbagli la sintassi base :: function () assomiglia alla sintassi delle chiamate ai metodi statici ma funziona per esempio in questo contesto.
Motti,

2
Non userei MSVC __super poiché è specifico per la piattaforma. Anche se il tuo codice potrebbe non essere eseguito su nessun'altra piattaforma, utilizzerei gli altri suggerimenti poiché lo fanno come previsto dalla lingua.
Teaser,


L'antipasto in cui le classi derivate sono sempre tenuti a chiamare le funzioni della classe genitore è Call super
Rufus

Risposte:


775

Prenderò il rischio di affermare l'ovvio: chiamate la funzione, se è definita nella classe base è automaticamente disponibile nella classe derivata (a meno che non sia private).

Se esiste una funzione con la stessa firma nella classe derivata, è possibile chiarirla aggiungendo il nome della classe base seguito da due due punti base_class::foo(...). Dovresti notare che, diversamente da Java e C #, C ++ non ha una parola chiave per "la classe base" ( supero base) poiché C ++ supporta l'ereditarietà multipla che può portare ad ambiguità.

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'
    }
};

Per inciso, non puoi derivare direttamente dalla stessa classe due volte poiché non ci sarà modo di fare riferimento a una delle classi di base sull'altra.

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

31
Perché vorresti ereditare due volte dalla stessa classe?
Paul Brewczynski,

65
@bluesm: nella classica OOP non ha molto senso, ma nella programmazione generica template<class A, class B> class C: public A, public B {};possono arrivare a due tipi uguali per motivi a seconda di come viene utilizzato il codice (che rende A e B uguali), possono essere due o tre strato di astrazione lontano da qualcuno non consapevole di ciò che hai fatto.
Emilio Garavaglia,

8
Penso che sia utile aggiungere che questo chiamerà il metodo della classe genitore anche se non è implementato direttamente nella classe genitore, ma è implementato in una delle classi genitore nella catena ereditaria.
Maxim Lavrov,

4
Su un sidenote, mi ha fatto arrabbiare quando ho provato a metterlo in un file cpp. Ho avuto "usando namespace std". 'left' è definito da qualche parte in quello spazio dei nomi. L'esempio non si sarebbe compilato: mi ha fatto impazzire :) Quindi ho cambiato 'sinistra' in 'Sinistra'. Ottimo esempio a proposito.
Mathai,

72
@Mathai Ed è per questo che non dovresti usare using namespace std.
JAB

193

Data la classe genitore denominata Parente la classe figlio denominata Child, puoi fare qualcosa del genere:

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);
}

Nota che Parentè il nome effettivo della classe e non una parola chiave.


Naturalmente, questo sarebbe utile solo se la chiamata di base fosse intervallata da altra logica, altrimenti non avrebbe senso sovrascrivere la funzione, quindi forse è un po ' troppo puntuale;)
underscore_d

1
@underscore_d in realtà, è utile anche se la chiamata di base non è stata intervallata da altra logica. Diciamo che la classe genitore fa praticamente tutto quello che vuoi, ma espone un metodo foo () che non vuoi che gli utenti di child utilizzino - o perché foo () non ha significato in child o i chiamanti esterni per child rovinano ciò che child è facendo. Quindi il bambino può usare parent :: foo () in determinate situazioni ma fornire un'implementazione di foo in modo da nascondere la chiamata di parent ().
iheanyi

@iheanyi Sembra interessante, ma mi dispiace, non lo sto ancora afferrando. È foo()qui analoga print()o una funzione separata? E cosa si intende per utilizzare privatel'ereditarietà per nascondere i dettagli ereditato dalla base, e di fornire publicfunzioni per le cose che shadowing non desidera esporre?
underscore_d

@underscore_d Sì, foo()era analogo a print(). Vorrei tornare a utilizzare print()come penso che avrebbe più senso in questo contesto. Diciamo che qualcuno ha creato una classe che ha eseguito alcune serie di operazioni su un particolare tipo di dati, ha esposto alcuni accessori e aveva un print(obj&)metodo. Ho bisogno di una nuova classe che funzioni array-of-objma tutto il resto è uguale. La composizione risulta in un sacco di codice duplicato. L'ereditarietà lo minimizza, durante le print(array-of-obj&) chiamate in loop print(obj&), ma non vogliono che i client chiamino print(obj&)perché non ha senso che lo facciano
iheanyi

@underscore_d Questo si basa sul presupposto che non posso refactificare le parti comuni della classe genitore originale o che farlo è incredibilmente costoso. L'eredità privata potrebbe funzionare, ma in tal caso si perdono gli accessi pubblici su cui si faceva affidamento - e quindi sarebbe necessario duplicare il codice.
iheanyi

32

Se viene chiamata la tua classe base Base tua funzione FooBar(), puoi chiamarla direttamente usandoBase::FooBar()

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

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

28

In MSVC esiste una parola chiave specifica per Microsoft: __super


MSDN: consente di dichiarare esplicitamente che si sta chiamando un'implementazione di classe base per una funzione che si sta sovrascrivendo.

// 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)
   }
};


5
Preferirei typdefingaggiare il genitore come qualcosa del genere super.
Thomas Eding,

26
Non proverò a giustificare l'uso di __super; L'ho menzionato qui come suggerimento alternativo. Gli sviluppatori dovrebbero conoscere il proprio compilatore e comprendere i pro ei contro delle sue capacità.
Andrey,

13
Preferirei scoraggiare chiunque dall'utilizzarlo, poiché ostacola gravemente la portabilità del codice.
Erbureth dice di reintegrare Monica il

26
Non sono d'accordo con Andrey: gli sviluppatori dovrebbero conoscere lo standard e non dovrebbero preoccuparsi delle funzionalità del compilatore, se consideriamo la scrittura di software che è principalmente indipendente dal compilatore che penso sia comunque una buona idea perché prima o poi in grandi progetti più compilatori sono comunque utilizzati.
Gabriel,

7
"Gli sviluppatori dovrebbero conoscere il proprio compilatore" questo ragionamento e l'inclusione di funzionalità non standard è ciò che ha portato a IE6 ...
e2-e4

5

Se il modificatore di accesso della funzione membro della classe base è protetto O pubblico, è possibile chiamare la funzione membro della classe base dalla classe derivata. È possibile effettuare la chiamata alla funzione membro non virtuale e virtuale della classe base dalla funzione membro derivata. Si prega di fare riferimento al programma.

#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;
}

Produzione:

$ 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
Grazie per aver portato alcuni esempi con virtual!
M.Ioan,

5

Chiamare il metodo parent con l'operatore di risoluzione dell'ambito parent.

Parent :: metodo ()

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();
}

Esempio di riferimento


2
Potresti modificare in una spiegazione del perché / come questo codice risponde alla domanda? Le risposte solo al codice sono scoraggiate, perché non sono facili da imparare dal codice con una spiegazione. Senza una spiegazione ci vuole molto più tempo e fatica per capire cosa è stato fatto, le modifiche apportate al codice o se il codice è utile. La spiegazione è importante sia per le persone che tentano di apprendere dalla risposta, sia per coloro che valutano la risposta per vedere se è valida o vale la pena votare.
Makyen,

3
Questa risposta riguarda le classi nidificate mentre la domanda riguardava le classi derivate (anche se le parole "parent" e "child" sono un po 'fuorvianti) e quindi non rispondono affatto alla domanda.
Johannes Matokic,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.