Pregunta

Tenemos la restricción de que una clase no puede actuar como clase base para más de 7 clases.¿Hay alguna manera de hacer cumplir la regla anterior en tiempo de compilación?

Conozco la técnica Usable_Lock de Andrew Koenig para evitar que se herede una clase, pero solo fallará cuando intentemos crear una instancia de la clase.¿No se puede hacer esto al derivarse?

La clase base puede saber quiénes son sus hijos.Así que supongo que podemos declarar una combinación de clases de amigos y encapsularlos para hacer cumplir esta regla.Supongamos que intentamos algo como esto

class AA {
   friend class BB;
   private:
      AA() {}
      ~AA() {}
};

class BB : public AA {

};

class CC : public AA 
{};

La derivación de la clase CC generaría una advertencia del compilador sobre un dtor inaccesible.A continuación, podemos marcar Advertencias como errores usando ajustes del compilador (como marcar todas las advertencias como errores), pero no me gustaría confiar en tales técnicas.

Otra forma, pero a mí me parece bastante torpe, es:

class B;

class InheritanceRule{
    class A {
    public:
        A() {}
        ~A() {}
    };
    friend class B;
};

class B {
public:
    class C : public InheritanceRule::A
    {};
};


class D : public InheritanceRule::A{};

La derivación de la clase D se marcará como un error del compilador, lo que significa que todas las clases que se derivarán deben derivarse dentro de la clase B.Esto permitirá al menos una inspección del número de clases derivadas de la clase A, pero no impedirá que nadie agregue más.

¿Alguien aquí que tenga una manera de hacerlo?Mejor aún si la clase base no necesita saber quiénes son sus hijos.

NOTA:Se puede crear una instancia de la clase que actúa como clase base (no es abstracta).

Gracias de antemano,

EDITAR-1:Según el comentario de jon.h, una ligera modificación.

// create a template class without a body, so all uses of it fail
template < typename D> 
class AllowedInheritance;

class Derived; // forward declaration
// but allow Derived by explicit specialization 
template<> 
class AllowedInheritance< Derived> {};

template<class T>
class Base : private AllowedInheritance<T> {};

// privately inherit Derived from that explicit specialization    
class Derived : public Base<Derived> {};

// Do the same with class Fail Error
// it has no explicit specialization, so it causes a compiler error
class Fail : public Base<Fail> {}; // this is error

int main()
{   
   Derived d;

   return 0;
}
¿Fue útil?

Solución

Estoy muy cansado, apenas puedo mantener los ojos abiertos, así que probablemente haya una manera más elegante de hacer esto, y ciertamente no estoy respaldando la extraña idea de que una Base debería tener como máximo siete subclases.

// create a template class without a body, so all uses of it fail
template < typename D, typename B> class AllowedInheritance;


class Base {};
class Derived; // forward declaration

// but allow Derived, Base by explicit specialization 

template<> class AllowedInheritance< Derived, Base> {};

// privately inherit Derived from that explicit specialization    
class Derived : public Base, private AllowedInheritance<Derived, Base> {};


// Do the same with class Compiler Error
// it has no explicit specialization, so it causes a compiler error
class CompileError: public Base, 
     private AllowedInheritance<CompileError, Base> { };

//error: invalid use of incomplete type 
//‘struct AllowedInheritance<CompileError, Base>’


int main() {

   Base b;
   Derived d;
   return 0;
}

Comentario de jon.h:

¿Cómo se detiene esto, por ejemplo?clase falla:Base pública { };?\

No es así.Pero tampoco lo hizo el ejemplo original del OP.

Al OP:su revisión de mi respuesta es prácticamente una aplicación directa del "Patrón de plantilla curiosamente recurrente" de Coplien]

También lo había considerado, pero el problema es que no existe una relación de herencia entre una derived1 : pubic base<derived1> y un derived2 : pubic base<derived2>, porque base<derived1> y base<derived2> Son dos clases completamente independientes.

Si su única preocupación es la herencia de la implementación, esto no es un problema, pero si desea la herencia de la interfaz, su solución lo rompe.

Creo que hay una manera de conseguir herencia y una sintaxis más limpia;Como mencioné, estaba bastante cansado cuando escribí mi solución.Al menos, hacer de RealBase una clase base de Base en su ejemplo es una solución rápida.

Probablemente haya varias formas de limpiar esto.Pero quiero enfatizar que estoy de acuerdo con markh44:Aunque mi solución es más limpia, todavía estamos saturando el código para respaldar una regla que tiene poco sentido.El hecho de que esto se pueda hacer no significa que deba hacerse.

Si la clase base en cuestión tiene diez años y es demasiado frágil para ser heredada, la verdadera respuesta es arreglarla.

Otros consejos

Lo siento, no sé cómo aplicar dicho límite utilizando el compilador.

Personalmente, no me molestaría en tratar de forzar la regla en el código mismo: está abarrotando el código con cosas que no tienen nada que ver con lo que está haciendo el código, no es un código limpio.

En lugar de saltar a través de aros, trataría de relajar esa regla. En cambio, debería ser una directriz que podría romperse si fuera necesario y de acuerdo con los demás miembros del equipo.

Por supuesto, no conozco exactamente lo que está haciendo, por lo que la regla podría ser apropiada, pero en general probablemente no lo sea.

Cualquier programación " regla " eso dice que nunca debes hacer x o siempre debes hacer y ¡casi siempre está mal! Observe la palabra & Quot; casi & Quot; ahí.

A veces es posible que necesite más de 7 clases derivadas. ¿Qué debe hacer entonces? Salta a través de más aros. Además, ¿por qué 7? ¿Por qué no 6 u 8? Es tan arbitrario: otra señal de una regla pobre.

Si debe hacerlo, como dice JP, el análisis estático es probablemente la mejor manera.

Muchas de las diversas herramientas de análisis de código estático proporcionan información sobre la jerarquía de herencia. En lugar de tratar de manejarlo en su código, buscaría una herramienta que pudiera establecer algunas reglas para la jerarquía de herencia y fallaría la compilación si no se siguen esas reglas. Puede costar un poco de $ y es posible que tenga que escribir una regla personalizada (he visto la profundidad de herencia, pero no la herencia & Quot; amplitud & Quot; como desee). Pero, a la larga, creo que es tu mejor apuesta.

Por comentario: he usado Coverity con cierto éxito. Poco caro. Hay varios hilos SO buenos que pueden tener mejores opciones.

En lugar de saturar el código con aserciones, es posible que pueda usar algo como GCC-XML , que analiza la fuente de C ++ utilizando la interfaz del compilador g ++ y genera una salida XML. Espero que sea razonablemente sencillo desarrollar una herramienta que analice este resultado y verifique las violaciones de la regla; esto podría integrarse con el registro del código fuente.

Por cierto, que las clases base sepan acerca de sus descendientes viola el Principio abierto-cerrado , lo que significa que en realidad socava la utilidad de la programación OO en general. El motivo principal para separar el código en clases base y subclases es para que la clase base no tenga que saber sobre sus subclases; esto hace posible cosas como paquetes de complementos entregados después de instalación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top