Como chamar uma função de classe pai da função de classe derivada?

604

Como chamo a função pai de uma classe derivada usando C ++? Por exemplo, eu tenho uma classe chamada parente uma classe chamada childderivada do pai. Dentro de cada classe há uma printfunção. Na definição da função de impressão da criança, gostaria de fazer uma chamada para a função de impressão dos pais. Como eu faria isso?

IaCoder
fonte
Todas as soluções acima estão assumindo que sua função de impressão é um método estático. É esse o caso? Se o método não for estático, as soluções acima não são relevantes.
hhafez
14
hhafez, você está enganado: a sintaxe base :: function () se parece com sintaxe de chamada de método estático, mas funciona por exemplo, métodos neste contexto.
Motti
2
Eu não usaria o MSVC __super, pois é específico da plataforma. Embora seu código possa não ser executado em qualquer outra plataforma, eu usaria as outras sugestões, pois elas fazem isso como o idioma pretendido.
Teaser
O antipattern onde as classes derivadas são sempre necessárias para chamar funções classe pai é chamada de super
Rufus

Respostas:

775

Eu correrei o risco de declarar o óbvio: você chama a função, se definida na classe base, está automaticamente disponível na classe derivada (a menos que seja private).

Se houver uma função com a mesma assinatura na classe derivada, você pode desambiguá-la adicionando o nome da classe base seguido por dois pontos base_class::foo(...). Observe que, diferentemente de Java e C #, o C ++ não possui uma palavra-chave para "a classe base" ( superou base), pois o C ++ suporta herança múltipla, o que pode levar à ambiguidade.

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

Aliás, você não pode derivar diretamente da mesma classe duas vezes, pois não haverá como se referir a uma das classes base sobre a outra.

class bottom : public left, public left { // Illegal
};
Motti
fonte
31
Por que você gostaria de herdar da mesma classe duas vezes?
Paul Brewczynski
65
@ bluesm: no POO clássico, não faz muito sentido, mas na programação genérica template<class A, class B> class C: public A, public B {};pode chegar a dois tipos, sendo o mesmo por razões, dependendo de como o seu código é usado (que faz com que A e B sejam iguais), pode ser dois ou três camada de abstração de alguém que não está ciente do que você fez.
Emilio Garavaglia
8
Acho útil acrescentar que isso chamará o método da classe pai, mesmo que não seja implementado diretamente na classe pai, mas seja implementado em uma das classes pai na cadeia de herança.
Maxim Lavrov
4
Em uma nota lateral, fiquei com raiva quando tentei colocar isso em um arquivo cpp. Eu tinha 'usando o namespace std'. 'left' é definido em algum lugar nesse espaço para nome. O exemplo não seria compilado - me deixou louco :). Então eu mudei 'esquerda' para 'Esquerda'. Ótimo exemplo, a propósito.
27515 Mathai
72
@ Mathai E é por isso que você não deve usar using namespace std.
JAB
193

Dada a classe pai nomeada Parente a classe filho nomeada Child, você pode fazer algo assim:

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

Observe que Parenté o nome real da classe e não uma palavra-chave.

Greg Hewgill
fonte
Obviamente, isso só seria útil se a chamada de base fosse intercalada com outra lógica, caso contrário não haveria sentido em substituir a função; portanto, talvez seja um pouco demais ;)
underscore_d
1
@underscore_d, na verdade, é útil mesmo que a chamada base não tenha sido intercalada com outra lógica. Digamos que a classe pai praticamente faça tudo o que você deseja, mas expõe um método foo () que você não deseja que os usuários do filho usem - porque foo () não faz sentido no filho ou os chamadores externos do filho irão estragar o que o filho é fazendo. Portanto, child pode usar parent :: foo () em certas situações, mas fornece uma implementação de foo para que eles ocultem o nome de foo () dos pais.
Iheanyi
@iheanyi Parece interessante, mas desculpe, ainda não estou entendendo. foo()Aqui é análogo print()ou é uma função separada? E você quer dizer usando privateherança para ocultar detalhes herdado da base, e fornecer publicfunções para coisas que você sombreamento que deseja expor?
underscore_d
@underscore_d Sim, foo()era análogo a print(). Deixe-me voltar a usar print()como acho que faria mais sentido nesse contexto. Digamos que alguém criou uma classe que executou algum conjunto de operações em um tipo de dados específico, expôs alguns acessadores e tinha um print(obj&)método. Eu preciso de uma nova classe que funcione, array-of-objmas todo o resto é o mesmo. A composição resulta em muitos códigos duplicados. Minimiza herança que, no print(array-of-obj&) circuito de chamada print(obj&), mas não querem clientes para chamar print(obj&), porque não faz sentido para eles a fazê-lo
iheanyi
@underscore_d Isso se baseia na suposição de que não posso refatorar as partes comuns da classe pai original ou que isso é incrivelmente caro. A herança privada pode funcionar, mas você perde os acessadores públicos nos quais confiava - e, portanto, precisaria duplicar o código.
Iheanyi
32

Se sua classe base é chamada Basee sua função é chamada, FooBar()você pode chamá-la diretamente usandoBase::FooBar()

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

void ChildOfBase::FooBar()
{
  Base::FooBar();
}
Andrew Rollings
fonte
28

No MSVC, existe uma palavra-chave específica da Microsoft para isso: __super


MSDN: permite declarar explicitamente que você está chamando uma implementação de classe base para uma função que você está substituindo.

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

Andrey
fonte
5
Eh, eu preferiria typdefos pais como algo parecido super.
Thomas Eding
26
Não tentarei justificar o uso de __super; Eu mencionei aqui como uma sugestão alternativa. Os desenvolvedores devem conhecer seu compilador e entender os prós e os contras de seus recursos.
Andrey
13
Prefiro desencorajar alguém de usá-lo, pois isso prejudica gravemente a portabilidade do código.
Erbureth diz Reinstate Monica
26
Não concordo com Andrey: os desenvolvedores devem conhecer o padrão e não precisam se preocupar com os recursos do compilador, se considerarmos escrever um software que seja principalmente independente do compilador, o que eu acho que é uma boa ideia, porque mais cedo ou mais tarde em grandes projetos, vários compiladores são de qualquer maneira usados.
Gabriel
7
"Os desenvolvedores devem conhecer o seu compilador" esse raciocínio, ea inclusão de recursos não padrão, é o que levou a IE6 ...
e2-e4
5

Se o modificador de acesso da função de membro da classe base estiver protegido OU público, você poderá chamar a função de membro da classe base da classe derivada. É possível chamar a função de membro não virtual e virtual de classe base da função de membro derivada. Por favor, consulte o 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;
}

Resultado:

$ 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
Ajay yadav
fonte
1
Obrigado por trazer alguns exemplos com virtual!
M.Ioan 5/04
5

Chame o método pai com o operador de resolução de escopo pai.

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

Exemplo de referência.

superbem
fonte
2
Você poderia editar uma explicação de por que / como esse código responde à pergunta? As respostas somente de código são desencorajadas, porque não são tão fáceis de aprender quanto o código com uma explicação. Sem uma explicação, leva muito mais tempo e esforço para entender o que estava sendo feito, as alterações feitas no código ou se o código é útil. A explicação é importante tanto para as pessoas que tentam aprender com a resposta quanto para as que avaliam a resposta para ver se ela é válida ou se vale a pena votar.
Makyen 23/02
3
Esta resposta é sobre classes aninhadas enquanto a pergunta era sobre classes derivadas (mesmo que as palavras 'pai' e 'filho' sejam um pouco enganadoras) e, portanto, não responde à pergunta.
Johannes Matokic