2008-09-16 15 views
35

Dado el siguiente ejemplo, ¿por qué tengo que utilizar explícitamente la instrucción b->A::DoSomething() en lugar de solo b->DoSomething()?Resolución de sobrecarga de C++

¿No debería la resolución de sobrecarga del compilador averiguar de qué método estoy hablando?

estoy usando Microsoft VS 2005. (Nota: el uso de Virtual no ayuda en este 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; 
} 
+0

Estoy tratando de llamar a DoSomething() en la clase A usando un puntero a la clase B. – Abe

Respuesta

40

Los dos “sobrecargas” no están en el mismo ámbito. De forma predeterminada, el compilador solo considera el alcance del nombre más pequeño posible hasta que encuentra una coincidencia de nombre. La coincidencia de argumentos se hace después. En su caso, esto significa que el compilador ve B::DoSomething. Luego intenta hacer coincidir la lista de argumentos, que falla.

Una solución sería la de bajar la sobrecarga de A en el alcance B 's:

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

De hecho, si no están en el mismo ámbito, tampoco se llaman sobrecargas – Chubsdad

+3

@Chubsdad: Por eso puse la palabra entre comillas Y una vez que el método se lleva al mismo alcance, se convierte en una sobrecarga. –

13

resolución de sobrecarga es una de las partes más feas de C++

Básicamente el compilador encuentra una coincidencia de nombre " DoSomething (int) "en el alcance de B, ve que los parámetros no coinciden, y se detiene con un error.

Se puede superarse mediante el uso de la A :: HacerAlgo en la clase 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; 
} 
2

Cuando se define una función en una clase derivada, entonces oculta todas las funciones con ese nombre en la clase base. Si la función de clase base es virtual y tiene una firma compatible, la función de clase derivada también anula la función de clase base. Sin embargo, eso no afecta la visibilidad.

Usted puede hacer la función de la clase base visible con una declaración using:

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

Al buscar en el árbol de herencia para la función de usar, C++ utiliza el nombre sin argumentos, una vez que se ha encontrado ninguna definición, se detiene, luego examina los argumentos. En el ejemplo dado, se detiene en la clase B. Con el fin de ser capaz de hacer lo que está después, clase B debe definirse así:

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

La función está oculta por la función con el mismo nombre en la subclase (pero con una firma diferente). Puede mostrarlo usando la instrucción using, como al usar A :: DoSomething();

5

No, este comportamiento está presente para garantizar que no se pille heredando de clases base distantes por error.

Para evitarlo, debe decirle al compilador qué método desea llamar colocando un uso de A :: DoSomething en la clase B.

Consulte this article para obtener una descripción rápida y fácil de este comportamiento.

3

Esto tiene algo que ver con la forma en que funciona la resolución de nombres. Básicamente, primero encontramos el alcance del que proviene el nombre, y luego recopilamos todas las sobrecargas para ese nombre en ese alcance.Sin embargo, el alcance en su caso es la clase B, y en la clase B, B :: HacerAlgo esconde A :: doSomething:

3.3.7 Nombre ocultar [basic.scope.hiding]

.. . [snip] ...

3 En una definición de función miembro, la declaración de un nombre local oculta la declaración de un miembro de la clase con el mismo nombre; ver basic.scope.class. La declaración de un miembro en una clase derivada (class.derived) oculta la declaración de un miembro de una clase base de del mismo nombre; ver class.member.lookup.

Debido a nombre de escondite, A :: HacerAlgo ni siquiera se considera para la resolución de sobrecarga

+0

Tener la referencia de especificación real es buena. – Sam

2

eso no es sobrecargar! ¡Eso es OCULTAR!

5

La presencia de un método en una clase derivada oculta todos los métodos con el mismo nombre (independientemente de los parámetros) en clases base. Esto se hace para evitar problemas como éste:

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

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

que tarde alguien cambia la clase A:

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

ahora de repente:

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

En otras palabras, si no funcionó así, un cambio no relacionado en una clase que no controlas (A), podría afectar silenciosamente cómo funciona tu código.

+0

Muy buen punto, debo decir. La mayoría de las respuestas que he visto hablan sobre por qué ocurre el error y cómo evitarlo, pero tu punto muestra por qué esta característica es esencial y útil en el escenario del mundo real. –