2010-01-21 12 views
8

Necesito evitar que una clase se derive de, así que pensé para mí mismo, esto es algo que Boost ya debe haber hecho. Sé que tienen un noncopyable, deben tener un nonderivable ...¿Hay alguna manera, usando plantillas, para evitar que una clase sea derivable en C++

imaginar mi sorpresa cuando no pude encontrarlo ....

Eso me hizo pensar .. Debe haber una razón. Tal vez no sea posible usar plantillas.

Estoy seguro de que si fue fácil, estará en las bibliotecas de impulso.

sé cómo hacerlo sin el uso de plantillas, es decir, utilizando una clase base con un constructor privado es decir

class ThatCantBeDerived; // Forward reference 

class _NonDeriv 
{ 
    _NonDeriv() {} 
    friend class ThatCantBeDerived; 
}; 

class ThatCantBeDerived : virtual public _NonDeriv 
{ 
public: 
    ThatCantBeDerived() : 
     _NonDeriv() 
    { 
    } 
}; 

O algo como esto ..

Tal vez sea la referencia hacia delante que causa el problema , o tal vez no hay una manera portátil para lograrlo ..

de cualquier manera, no estoy seguro de por qué no está en alza ..

¿Alguna idea?

+0

ScaryA, de curiocity inactivo, ¿podría revelar por qué uno querría tener una clase no derivable? (Pregunta sincera: nunca tuve un diseño que lo hubiera solicitado, pero supongo que tu pregunta tiene alguna fuente práctica). – DVK

+0

Los identificadores que comienzan con guión bajo seguido de una letra mayúscula (como identificadores que comienzan con doble subrayado) están reservados para la implementación (compilador + biblioteca). No debe llamar a su plantilla '_NonDeriv'. –

+0

@DVK Supongamos que está diseñando una herramienta que está utilizando una estructura fija a la que la clase se asigna directamente (es decir, una struct sk_buff) y toda su biblioteca realiza optimizaciones/suposiciones sobre esa asignación. Seguramente no quieres que alguien pueda cambiar eso, ¿verdad? – ezpz

Respuesta

2

Bajo la especificación actual, se prohíbe explícitamente a "amigo" de un parámetro de plantilla, por lo que su templatizing ejemplo podría hacer que no cumplen las normas. Boost probablemente no quiera agregar algo así a sus bibliotecas. Creo que esta restricción se está relajando en Ox sin embargo, y hay soluciones para los compiladores.

+0

Para ser justos, el impuesto vptr mencionado en el C++ faq lite es probablemente también una muy buena razón –

3

No hay forma en C++ de prevenir derivación - no se puede evitar esta situación:

class A {}; 

class B : public A {}; 

Sin embargo, hay varias maneras de prevenir instanciación de objetos de tipo B. Si esto vale la pena es discutible Preferiría documentar que la clase A no está destinada a derivación, y darle un destructor no virtual.

Además, tenga en cuenta que el nombre _NonDeriv está reservado en C++ para la implementación, ya que todos los nombres comienzan con un guión bajo y una letra mayúscula. No tiene permitido crear dichos nombres en su propio código.

0

Tal vez convertir su ejemplo en una plantilla, usando CRTP - el patrón de plantilla curiosamente recurrente:

template <typename T> 
_NonDeriv 
{ 
    _NonDeriv() {} 
    friend class T; 
}; 

class ThatCantBeDerived : virtual public _NonDeriv<ThatCantBeDerived> 
{ 
public: 
    ThatCantBeDerived() : 
     _NonDeriv() 
    { 
    } 
}; 

podría funcionar ...

+0

Ah, la respuesta de Todd Gardner sugiere que esto no funcionará (hasta C++ 0x tal vez). Oh bien. – tony

+0

Eso es exactamente lo que habría hecho.La única razón por la que paré fue cuando pensé que esto ya debería haber sido hecho en impulso ... Hasta que descubrí que no era ... – ScaryAardvark

+0

Y luego @Todd Gardner me explicó amablemente por qué :) – ScaryAardvark

1

Adobe tiene una solución no-perfecto para esto utilizando plantillas.

El problema es que, dado que las plantillas no pueden declarar amigos dependientes del argumento [*], se basa en el constructor que se está protegiendo en lugar de privado. Es una solución parcial, ya que desencadenará un error de compilación cuando alguien decida equivocadamente derivar de su clase, pero no es una solución completa ya que alguien intencionalmente puede forzar la herencia.

template <typename SealedClass> 
class seal 
{ 
protected: 
    seal() {} 
}; 

class Sealed : private virtual seal<Sealed> 
{ 
//... 
}; 

class NaiveExtension : public Sealed { // fails 
    NaiveExtension() {} // NaiveExtension cannot call seal<Sealed> constructor 
}; 

class BreakingExtension : public Sealed, private virtual seal<Sealed> { 
    BreakingExtension() : seal<Sealed>(), Sealed() {} // now it can 
}; 

La ventaja es que puede ser templated (la biblioteca de adobe en realidad define una macro que ocultará la 'virtual privado' desde el lector casual). La desventaja es que se puede romper.

Por otra parte, siempre se puede hacer lo que C++ FAQ sugiere algunas preguntas sobre el bloqueo de algunas funcionalidades:

'how can I inhibit people from...': write a comment not to do it

'but how do I really inhibit others from...': write a comment: You will be fired if...

'but how do I actually block it in case they don't follow the comment?': fire them

[*] Esta restricción será quitado con la próxima norma, siempre que estén disponibles, siempre que sea implementado en los compiladores ...

1

Fácil:

hacer todos los constructores privados:
Entonces nobdy puede derivce de usted. Por supuesto, esto añade problemas othe como que no puede instanciar una variable del objeto, pero hay soluciones para que los que utilizan métodos miembro estáticas públicas y amigos:

#include <memory> 
class InstnaceCantDeriveFromMe; 
class CantDeriveFromMe 
{ 
    private: 
     friend class InstnaceCantDeriveFromMe; 
     CantDeriveFromMe() 
     {} 

    public: 
     static std::auto_ptr<CantDeriveFromMe> getDynamicObj() 
     { 
      return std::auto_ptr<CantDeriveFromMe>(new CantDeriveFromMe()); 
     } 
}; 

class Plop: public CantDeriveFromMe 
{ 
}; 

class InstnaceCantDeriveFromMe 
{ 
    private: 
     CantDeriveFromMe instnace; 
    public: 
     CantDeriveFromMe& get() {return instnace;} 
}; 

int main() 
{ 
    std::auto_ptr<CantDeriveFromMe> a =  CantDeriveFromMe::getDynamicObj(); 
    InstnaceCantDeriveFromMe   b; 


    // This fails to compile: 
    Plop        c; 
} 
Cuestiones relacionadas