Pergunta

Essa pergunta já tem resposta aqui:

Dado o exemplo a seguir, por que preciso usar explicitamente a instrução b->A::DoSomething() em vez de apenas b->DoSomething()?

A resolução de sobrecarga do compilador não deveria descobrir de qual método estou falando?

Estou usando o Microsoft VS 2005.(Observação:usar virtual não ajuda neste caso.)

class A
{
  public:
    int DoSomething() {return 0;};
};

class B : public A
{
  public:
    int DoSomething(int x) {return 1;};
};

int main()
{
  B* b = new B();
  b->A::DoSomething();    //Why this?
  //b->DoSomething();    //Why not this? (Gives compiler error.)
  delete b;
  return 0;
}
Foi útil?

Solução

As duas “sobrecargas” não estão no mesmo escopo.Por padrão, o compilador considera apenas o menor escopo de nome possível até encontrar uma correspondência de nome.A correspondência de argumentos é feita após.No seu caso, isso significa que o compilador vê B::DoSomething.Em seguida, ele tenta corresponder à lista de argumentos, o que falha.

Uma solução seria reduzir a sobrecarga do A em Bescopo de:

class B : public A {
public:
    using A::DoSomething;
    // …
}

Outras dicas

A resolução de sobrecarga é uma das partes mais feias do C++

Basicamente, o compilador encontra uma correspondência de nome "DoSomething(int)" no escopo de B, vê que os parâmetros não correspondem e para com um erro.

Isso pode ser superado usando o A::DoSomething na classe B

class A  
{  
  public:  
    int DoSomething() {return 0;}
};  

class B : public A  
{  
  public:  
    using A::DoSomething;
    int DoSomething(int x) {return 1;} 
};   


int main(int argc, char** argv)
{
  B* b = new B();  
  // b->A::DoSomething();    // still works, but...
  b->DoSomething();    // works now too
  delete b;  
  return 0;
}

Não, esse comportamento está presente para garantir que você não seja pego herdando de classes base distantes por engano.

Para contornar isso, você precisa informar ao compilador qual método deseja chamar, colocando um using A::DoSomething na classe B.

Ver Este artigo para uma visão geral rápida e fácil desse comportamento.

A presença de um método em uma classe derivada oculta todos os métodos com o mesmo nome (independentemente dos parâmetros) nas classes base.Isso é feito para evitar problemas como este:

class A {} ;
class B :public A
{
    void DoSomething(long) {...}
}

B b;
b.DoSomething(1);     // calls B::DoSomething((long)1));

do que mais tarde alguém muda de classe A:

class A
{
    void DoSomething(int ) {...}
}

agora de repente:

B b;
b.DoSomething(1);     // calls A::DoSomething(1);

Em outras palavras, se não funcionasse assim, uma alteração não relacionada em uma classe que você não controla (A) poderia afetar silenciosamente o funcionamento do seu código.

Isso tem algo a ver com a forma como a resolução de nomes funciona.Basicamente, primeiro encontramos o escopo de onde vem o nome e, em seguida, coletamos todas as sobrecargas desse nome nesse escopo.Porém, o escopo no seu caso é a classe B, e na classe B, B::DoSomething esconde A::DOAlguma coisa:

3.3.7 Ocultação de nome [basic.scope.hiding]

...[recorte]...

3 Em uma definição de função de membro, a declaração de um nome local esconde a declaração de um membro da classe com o mesmo nome;ver básico.scope.class.A declaração de um membro em uma classe derivada (classe.derivada) oculta a declaração de um membro de uma classe base de mesmo nome;ver classe.membro.procurar.

Por causa da ocultação de nomes, A::DoSomething nem sequer é considerado para resolução de sobrecarga

Quando você define uma função em uma classe derivada, ela oculta todas as funções com esse nome na classe base.Se a função da classe base for virtual e tiver uma assinatura compatível, a função da classe derivada também substituirá a função da classe base.No entanto, isso não afeta a visibilidade.

Você pode tornar a função da classe base visível com uma declaração using:

class B : public A  
{  
  public:  
    int DoSomething(int x) {return 1;};  
    using A::DoSomething;
};  

Isso não é sobrecarga!Isso é ESCONDER!

Ao pesquisar na árvore de herança a função a ser usada, o C++ usa o nome sem argumentos, uma vez encontrada qualquer definição, ele para e examina os argumentos.No exemplo dado, ele para na classe B.Para poder fazer o que você procura, a classe B deve ser definida assim:

class B : public A  
{  
  public:
    using A::DoSomething;
    int DoSomething(int x) {return 1;};  
}; 

A função é ocultada pela função com o mesmo nome na subclasse (mas com assinatura diferente).Você pode exibi-lo usando a instrução using, como em using A::DoSomething();

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top