2010-06-15 22 views
5

Estoy tratando de evitar que una clase pueda convertir su puntero 'this' en un puntero de una de sus interfaces. Lo hago usando herencia privada a través de una clase proxy intermedia. El problema es que encuentro que la herencia privada hace que todos los miembros estáticos públicos y los tipos de la clase base sean inaccesibles para todas las clases bajo la clase heredada en la jerarquía.C++ herencia privada y miembros/tipos estáticos

class Base 
{ 
public: 
    enum Enum 
    { 
     value 
    }; 
}; 

class Middle : private Base 
{ 
}; 

class Child : public Middle 
{ 
public: 
    void Method() 
    { 
     Base::Enum e = Base::value; // doesn't compile BAD!  
     Base* base = this; // doesn't compile GOOD! 
    } 
}; 

He intentado esto tanto en VS2008 (la versión requerida) como en VS2010, tampoco funciona.

¿Alguien puede pensar en una solución? ¿O un enfoque diferente para detener la conversión?

También soy curioso sobre el comportamiento, ¿es solo un efecto secundario de la implementación del compilador, o es por diseño? Si por diseño, ¿por qué? Siempre he pensado que la herencia privada significa que nadie sabe que Middle hereda de Base. Sin embargo, el comportamiento exhibido implica que la herencia privada significa mucho más que eso, de hecho, Child tiene menos acceso a Base que cualquier espacio de nombres que no esté en la jerarquía de clases.

+0

+1: una pregunta muy interesante, de hecho. –

Respuesta

6

Usted debe ser capaz de acceder Base::Enum al calificar plenamente:

class Child : public Middle 
{ 
public: 
    void Method() 
    { 
     ::Base::Enum e = ::Base::value; 
    } 
}; 

Este es el comportamiento especificado por el lenguaje (C++ 03 §11.2/3):

Nota: Un miembro de una clase base privada puede ser inaccesible como un nombre de miembro heredado, pero accesible directamente.

Esto es seguido por un ejemplo extendido que es efectivamente similar a su código de ejemplo.

Sin embargo, parece que ni Visual C++ 2008 ni Visual C++ 2010 implementan esto correctamente, por lo que aunque puede usar el tipo ::Base::Enum, todavía no puede acceder a ::Base::value. (En realidad, parece que Visual C++ ha cometido una gran cantidad de errores, ya que le permite incorrectamente utilizar el Base::Enum no calificado).

Para "moverse" el problema, puede agregar el uso de declaraciones a la clase Middle:

class Middle : private Base 
{ 
protected: 

    using Base::Enum; 
    using Base::value; 
}; 

Esto no le permite utilizar la Base::Enum o Base::value en su clase Child, sino que le permitirá utilice un Enum y value o Middle::Enum y Middle::value.

+1

Envié un informe de error en Microsoft Connect sobre los errores de Visual C++: https://connect.microsoft.com/VisualStudio/feedback/details/567693/ –

+0

Gracias por su respuesta detallada. Molesto es causado por un error. Lamentablemente, agregar el uso a la clase media no es una opción en mi caso del mundo real, ya que Middle es una clase de plantilla que toma la clase base como argumento Tipo. Mi única opción es mover la enumeración fuera de la interfaz. – WearyMonkey

+0

Notaré que la "nota" del estándar que cité realmente no especifica nada, solo explica este comportamiento. Mi suposición es que la especificación real de este comportamiento se extiende a través de varias secciones que describen cómo se lleva a cabo la resolución del nombre. –

1

Solo tengo una pregunta: ¿por qué usted herencia privada en absoluto?

La herencia es un concepto bastante rota, en mi opinión, ya que viola el principio de una sola Responsabilidad:

  • se hereda la interfaz
  • se hereda la implementación

Desafortunadamente se requiere la herencia para el polimorfismo en el código orientado a objetos, por lo que no puede evitarlo en este caso.

Pero aquí usted explícitamente desea NO utilizar el polimorfismo, por lo que me pregunto por qué usar herencia en absoluto, ya que es su único uso interesante (imo).

En cambio, en C++, puede utilizar:

  • composición, por la reutilización de código
  • funciones libres (definidos en su propio espacio de nombres)
  • using, typedef etc ... para llevar objetos de fuera de la clase

Su ejemplo parece restringido aquí, pero yo también envuelvo mis enums en struct para evitar la contaminación del espacio de nombres por una mil símbolos (y debido a que struct se pueden usar como parámetros de plantilla donde no pueden espacios de nombres).

struct MyEnum 
{ 
    enum type 
    { 
    value 
    }; 
}; 

class Child 
{ 
public: 
    typedef MyEnum::type Enum; 

    Child(Enum e = MyEnum::value); 

private: 
}; 

no veo nada de malo en calificar el nombre, en vez siento que hace que sea más fácil de leer de nuevo, ya que usted sabe, que la enumeración que estamos hablando ...

Realmente, private herencia es mejor evitarlo (y generalmente es reemplazado por la Composición). El único caso válido (imo) es para Empty Base Optimization ... y, francamente, no es frecuente que lo necesite (como suele suceder con las optimizaciones).

+0

El otro uso para herencia privada es si necesita acceso a miembros protegidos de una clase. Esa no es una situación ideal, pero aparece en raras ocasiones. Ciertamente estoy de acuerdo en que es mejor evitarlo. –

+0

Normalmente prefiero desacoplarme en este punto: creo una clase media que hereda públicamente y usa composición. –

+0

Ciertamente estoy de acuerdo en que la composición es preferible a la herencia siempre que sea posible. Sin embargo, la clase base en mi problema del mundo real es la clase central en una antigua base de código grande, hacer un trabajo de refactorización apropiado significaría reescribir la mayor parte del código. – WearyMonkey