2012-04-27 10 views
5

Considere el siguiente código:¿Cuándo debería restringir el acceso a una función virtual en una clase derivada?

class Base 
{ 
public: 
    virtual void Foo() {} 
}; 

class Derived : public Base 
{ 
private: 
    void Foo() {} 
}; 

void func() 
{ 
    Base* a = new Derived; 
    a->Foo(); //fine, calls Derived::Foo() 

    Derived* b = new Derived; 
// b->Foo(); //error 
    static_cast<Base*>(b)->Foo(); //fine, calls Derived::Foo() 
} 

He oído dos diferentes escuelas de pensamiento sobre el asunto:

1) Deja la accesibilidad al igual que la clase base, ya que los usuarios podrían utilizar para obtener acceso static_cast de todas formas.

2) Haga las funciones lo más privadas posible. Si los usuarios requieren a-> Foo() pero no b-> Foo(), entonces Derived :: Foo debe ser privado. Siempre se puede hacer público si es necesario.

¿Hay alguna razón para preferir una u otra?

+1

Este diseño es muy contrario a la intuición por las razones que menciona. Aconsejaría que no lo haga a menos que encuentre un escenario que solo pueda resolverse de esta manera. –

+0

Si su intención es restringir _direct_ el uso de la clase derivada (por ejemplo, el patrón de fábrica), entonces la privacidad protegida o privada es la forma más adecuada (en lugar de restringir determinados métodos) – user396672

Respuesta

6

Restringir el acceso a un miembro en un subtipo rompe el Liskov substitution principle (el L en SOLID). Recomendaría en contra de esto en general.

actualización: Se puede "trabajar", al igual que en el código se compila y se ejecuta y produce el resultado esperado, pero si usted está ocultando un miembro de su intención es, probablemente, haciendo el subtipo menos general que el original. Esto es lo que rompe el principio. Si, por el contrario, tu intención es limpiar la interfaz de subtipo dejando solo lo que es interesante para el usuario de la API, adelante y hazlo.

+2

No creo que rompa el principio de sustitución. Todavía puede usar una referencia a 'Derived' en cualquier lugar donde se requiera una referencia a' Base', y funcionará muy bien. –

+0

@ BjörnPollex Iba a responder por comentario, pero luego me di cuenta de que la respuesta se beneficiaría de una actualización. – Joni

+0

¿Los compiladores lo marcan como advertencia? (Solo otro punto de datos) – Jon

1

Esto depende de su diseño, si desea acceder a la función virtual con un objeto de clase derivado o no.
Si no, entonces sí, siempre es mejor hacerlos private o protected.

No hay diferencia en la ejecución del código en función del especificador de acceso, pero el código se vuelve más claro.
Una vez que haya restringido el acceso de la clase virtual función; el lector de ese class puede estar seguro de que esto no se llamará con ningún objeto o puntero de clase derivada.

3
No

una respuesta a su pregunta original, pero si estamos hablando de diseño de clases ...

Como Alexandrescu y Sutter recomiendan en su 39 ta regla, se debe preferir el uso de las funciones públicas y no virtuales privado/protegido virtual:

class Base { 
public: 
    void Foo(); // fixed API function 

private: 
    virtual void FooImpl(); // implementation details 
}; 

class Derived { 
private: 
    virtual void FooImpl(); // special implementation 
}; 
+0

Podría razonablemente poner la implementación de 'Base :: Foo' allí en la definición de clase,' void Foo() {FooImpl(); } ' Nunca va a ser algo más que un one-liner predecible, ese es el punto. –

+0

@SteveJessop De acuerdo, si FooImpl() es realmente solo una implementación para Foo. En la vida real, puede ser una parte del algoritmo de Foo con estructura fija pero algunos detalles flotantes. Y a veces, los estándares de codificación corporativa pueden prohibir la implementación en la declaración de clase, incluso para one-liners :) – anxieux

+1

@SteveJessop: ¡En realidad, NO! El punto es que puede cambiar 'Foo' a voluntad para incluir el tratamiento previo/posterior antes de llamar a la función' virtual'. Siempre fue un trazador de líneas, ¡sería inútil incluir una envoltura extra! –

Cuestiones relacionadas