2009-06-16 4 views
11

Tengo una clase de valor de acuerdo con la descripción en "C++ Coding Standards", artículo 32. En resumen, eso significa que proporciona semántica de valores y no tiene ningún método virtual.¿Es posible prohibir derivar de una clase en tiempo de compilación?

No quiero una clase para derivar de esta clase. Además de otros, una razón es que tiene un destructor público no virtual. Pero una clase base debe tener un destructor público y virtual o protegido y no virtual.

No conozco la posibilidad de escribir la clase de valor, por lo que no es posible derivarla. Quiero prohibirlo en tiempo de compilación. ¿Hay quizás algún idioma conocido para hacer eso? Si no, tal vez haya algunas nuevas posibilidades en el próximo C++ 0x? ¿O hay buenas razones para que no exista tal posibilidad?

Respuesta

6

Incluso si la pregunta no está marcada para C++ 11, para las personas que llegan aquí, se debe mencionar que C++ 11 admite el nuevo identificador contextual final. Ver wiki page

8

Si está dispuesto a permitir solo que la clase se cree mediante un método de fábrica, puede tener un constructor privado.

class underivable { 
    underivable() { } 
    underivable(const underivable&); // not implemented 
    underivable& operator=(const underivable&); // not implemented 
public: 
    static underivable create() { return underivable(); } 
}; 
+0

Ah, ¿y ese podría ser un método de fábrica estático de mi clase para mantener las cosas juntas? Suena muy bien. – SebastianK

+3

Desafortunadamente, esto no detiene la derivación.Simplemente evita la instalación del tipo derivado, excepto a través de la fábrica. Sería bueno si pudieras prevenir la derivación, pero hasta donde yo sé, no puedes. –

+0

¿De qué manera práctica hay una diferencia entre no poder instanciar un objeto del tipo derivado y no poder obtener nada (acceso a miembros estáticos protegidos?) – Motti

25

Bjarne Stroustrup ha escrito sobre esta here.


El bit relevante desde el enlace:

¿Puedo evitar que la gente se deriva de mi clase?

Sí, pero ¿por qué quieres? Hay dos respuestas comunes:

  • por eficiencia: para evitar que mi función las llamadas sean virtuales.
  • para la seguridad: para asegurarse de que mi clase no se utiliza como una clase base (por ejemplo, para asegurarse que puedo copiar objetos sin temor de rebanar)

En mi experiencia, la eficiencia la razón usualmente es un miedo fuera de lugar. En C++, las llamadas a funciones virtuales son tan rápidas que su uso en el mundo real para una clase diseñada con funciones virtuales no produce gastos generales mensurables en tiempo de ejecución en comparación con soluciones alternativas que utilizan llamadas a funciones ordinarias. Tenga en cuenta que el mecanismo de llamada a la función virtual se usa generalmente solo cuando se llama a través de un puntero o una referencia. Cuando se llama a una función directamente para un objeto nombrado, la sobrecarga de la clase de función virtual se optimiza fácilmente.

Si existe una necesidad real de "limitar" una jerarquía de clases para evitar llamadas a funciones virtuales, uno podría preguntarse por qué esas funciones son virtuales en primer lugar. He visto ejemplos donde las funciones críticas para el rendimiento se han hecho virtuales sin una buena razón, simplemente porque "esa es la forma en que generalmente lo hacemos".

La otra variante de este problema, cómo evitar la derivación por razones lógicas, tiene una solución. Desafortunadamente, esa solución no es bonita. Se basa en el hecho de que la clase más derivada en una jerarquía debe construir una base virtual. Por ejemplo:

class Usable; 

class Usable_lock { 
    friend class Usable; 
private: 
    Usable_lock() {} 
    Usable_lock(const Usable_lock&) {} 
}; 

class Usable : public virtual Usable_lock { 
    // ... 
public: 
    Usable(); 
    Usable(char*); 
    // ... 
}; 

Usable a; 

class DD : public Usable { }; 

DD dd; // error: DD::DD() cannot access 
     // Usable_lock::Usable_lock(): private member 

(de D&E seg 11.4.3).

+0

Muy bonito, mejor que mi respuesta! – Motti

+2

@Motti: Me gustó bastante su respuesta, lo hice +1. Dependiendo de sus requisitos, podría ser una solución mucho mejor. Bjarnes es un poco complejo si todo lo que realmente quieres hacer es evitar que alguien use su tipo en lugar del tuyo. Creo que lo importante a tener en cuenta es por qué está tratando de evitar la derivación, y realmente logra algo útil. –

+0

¿Por qué la clase base es virtual en esta solución? – SebastianK

2

Bueno, tuve un problema similar. Esto se publica here en SO. El problema era de otra manera; es decir, solo permita derivar esas clases que permita. Verifica si resuelve tu problema.

Esto se hace en en tiempo de compilación.

4

Echa un buen vistazo here.
Es genial, pero es un truco.
Pregúntese por qué stdlib no hace esto con sus propios contenedores.

+2

Sí, utiliza el segundo de los tres enfoques. –

+0

En realidad, tuve una clase que heredó el mapa una vez. No me proporcionó ningún error. –

+0

oh espera ... no importa. No recuerdo lo que fue el segundo jajaja. –

0

Esta solución no funciona, pero la dejo como un ejemplo de lo que no debe hacer.


No he utilizado C++ desde hace un tiempo, pero por lo que yo recuerdo, se obtiene lo que desea al hacer destructor privado.

ACTUALIZACIÓN:

En Visual Studio 2005 obtendrá una advertencia o un error. Compruebe el siguiente código:

class A 
{ 
public: 
    A(){} 
private: 
    ~A(){} 
}; 

class B : A 
{ 
}; 

Ahora,

B B;

producirá un error "C2248 de error: 'A :: ~ A': no ​​se puede miembro privado de acceso declarado en la clase 'A'"

mientras

B *b = new B(); 

producirá advertencia "de advertencia C4624: 'B': destructor no se pudo generar porque un destructor de clase base es inaccesible ".

Parece una solución a medias, PERO como orsogufo puntiagudo, haciendo inutilizable la clase A. respuestas que salen

+1

Al hacer eso obtienes objetos que no se pueden eliminar ... es cierto que no puedes heredar de una clase como esa, pero ¿cómo la usas? –

+0

Maldita sea, necesito actualizar mi C++. ¡Gracias por eso! – ya23

+0

de nada :) gracias por actualizar la publicación. De todos modos, lo que quise decir es que no puedes instanciar un objeto de tipo A, no solo de tipo B. –

1

que serían generalmente lograr esto de la siguiente manera:

// This class is *not* suitable for use as a base class 

El comentario va en el encabezado y/o en la documentación. Si los clientes de su clase no siguen las instrucciones del paquete, en C++ pueden esperar un comportamiento indefinido. Derivar sin permiso es solo un caso especial de esto. Deberían usar composición en su lugar.

Btw, esto es un poco engañoso: "una clase base debe tener un destructor que sea público y virtual o protegido y no virtual".

Eso es cierto para las clases que se utilizarán como bases para el polimorfismo en tiempo de ejecución. Pero no es necesario si las clases derivadas nunca van a ser referenciadas mediante punteros al tipo de clase base. Puede ser razonable tener un tipo de valor que se use solo para polimorfismo estático, por ejemplo con enlace dinámico simulado. La confusión es que la herencia se puede usar para diferentes propósitos en C++, lo que requiere un soporte diferente de la clase base. Esto significa que, aunque no desee el polimorfismo dinámico con su clase, podría estar bien crear clases derivadas siempre que se utilicen correctamente.

Cuestiones relacionadas