2011-02-14 16 views
13
# include <iostream> 
using namespace std; 

class A 
{ 
    public: 
    virtual void f() 
    { 
     cout << "A::f()" << endl; 
    } 
}; 
class B:public A 
{ 
    private: 
    virtual void f() 
    { 
     cout << "B::f()" << endl; 
    } 
}; 
int main() 
{ 
    A *ptr = new B; 
    ptr->f(); 
    return 0; 
} 

Este código funciona correctamente e imprime B :: f(). Sé cómo funciona, pero ¿por qué se permite este código?¿Por qué se permite llamar al método virtual privado de la clase derivada mediante el puntero de la clase base?

Respuesta

14

El control de acceso se realiza en tiempo de compilación, no en tiempo de ejecución. No hay forma en general para que la llamada al f() conozca el tipo de tiempo de ejecución del objeto apuntado por ptr, por lo que no hay verificación en los especificadores de acceso de la clase derivada. Es por eso que la llamada está permitida.

En cuanto a por qué la clase B puede anular el uso de una función privada en absoluto, no estoy seguro. Ciertamente, B viola la interfaz implícita en su herencia de A, pero en general el lenguaje C++ no siempre impone la herencia de la interfaz, por lo que el hecho de que sea simplemente erróneo no significa que C++ lo detendrá.

Supongo que probablemente exista algún caso de uso para esta clase B: la sustitución aún funciona con polimorfismo dinámico, pero estáticamente B no sustituye a A (por ejemplo, puede haber plantillas que llamen a f, que funcionarían con A como argumento pero no como B como argumento). Puede haber situaciones en que eso es exactamente lo que quiere. Por supuesto, podría ser una consecuencia involuntaria de alguna otra consideración.

+0

No es una violación de LSP, porque un objeto B se puede usar en cualquier lugar donde pueda A. Tales contextos desencadenan una conversión implícita Derivado a Base, posiblemente cortando las partes adicionales. Después de dicha conversión, se accede a 'B :: f' como' A :: f', que es público. – MSalters

+0

@MSalters: hay dos tipos diferentes de sustitución en C++, porque hay (al menos) dos tipos diferentes de polimorfismo en C++. Podemos evaluar por separado si LSP se aplica a cada uno. Si su objeto B es, por ejemplo, pasado por valor en una llamada de función que utiliza la deducción de argumentos de la plantilla para crear una instancia de la función a partir de una plantilla, entonces bien podría no cortarse, y de hecho accedería a 'f' como' B :: f'. B no satisface todos los conceptos que A satisface, básicamente, por lo que los objetos solo son sustituibles una vez que el tipo ha sido eliminado (por ejemplo, usando un A * o convirtiéndolo en A). –

+0

LSP es un principio de OOP que restringe _conformance conformance_. El contexto de la plantilla no es un contexto OOP normal. Es programación genérica, que generalmente ignora la herencia. En cambio, usa _conformidad estructural_. Por lo tanto, el principio LSP de OOP no se aplica a los parámetros de plantilla. – MSalters

0

Su clase base define la interfaz para todos los elementos secundarios heredados. No veo por qué debería evitar el acceso mencionado. Puede intentar deducir una clase de 'B' y usar la interfaz Base para llamar, lo que daría lugar a un error.

¡Salud!

2

Este código está permitido porque f es público en la interfaz de A. Una clase derivada no puede cambiar la interfaz de una clase base. (Anular un método virtual no está cambiando la interfaz, ni está ocultando miembros de una base, aunque puede parecer que ambos lo hacen). Si una clase derivada pudiera cambiar la interfaz de una base, violaría el "is a" relationship.

Si los diseñadores de A quieren hacer f inaccesible, entonces debe marcarse como protegido o privado.

-1

Como en Java, en C++ puede aumentar la visibilidad de los métodos pero no disminuirlos.

+1

No sé sobre Java, pero B :: f sí oculta A :: f, y [B :: f es privado] (http://codepad.org/GhjYTeqY). –

+0

En Java, no puede hacer que un método heredado sea privado en la clase derivada, no se compilará. – Ali

0

Además de la respuesta de Steve:

  • B se deriva públicamente de A. Esto implica Liskov sustitución
  • Anulación f ser privada parece violar ese principio, pero en realidad no tiene por qué - se puede aún use B como una A sin que el código se interponga, por lo tanto, si la implementación privada de f sigue siendo válida para B, no hay problemas
  • Es posible que desee utilizar este patrón B debe ser Liskov sustituible por A, pero B es también la raíz de otra jerarquía que no está realmente relacionada (en forma de Liskov-sustituible) con A, que re f ya no forma parte de la interfaz pública. En otras palabras, una clase C derivada de B, utilizada a través de un puntero a B, ocultaría f.
  • Sin embargo, esto es en realidad muy poco probable, y probablemente habría sido una mejor idea de derivar B de una empresa privada o protegida
0

comprobación de control de acceso a la función ocurre en la etapa posterior de un C++ llamada a la función. El orden en el nivel alto sería como la búsqueda de nombre, la deducción del argumento de la plantilla (si existe), la resolución de sobrecarga, y luego el control de acceso (público/proteger/privado).

Pero en su fragmento, usaba un puntero a la clase base y la función f() en la clase base es pública, eso es lo que el compilador puede ver en el compilador, así que el compilador dejará pasar su fragmento.

A *ptr = new B; 
ptr->f(); 

Pero todos los anteriores ocurren en tiempo de compilación, por lo que son realmente estáticos. Mientras que la llamada de función virtual a menudo potenciada por vtable & vpointer son cosas dinámicas que ocurren en tiempo de ejecución, la llamada de función virtual es ortogonal al control de acceso (la llamada de función virtual ocurre después del control de acceso), por eso la llamada a f() terminó B :: f() independientemente de que el control de acceso sea privado.

Pero si intenta utilizar

B* ptr = new B; 
ptr->f() 

Esto no pasará a pesar de la viable vpointer &, compilador no permitirá que se compile en tiempo de compilación.

Pero si se intenta:

B* ptr = new B; 
((static_cast<A*>(ptr))->f(); 

esto iba a funcionar muy bien.

Cuestiones relacionadas