rectangulo - Prevenir la herencia de clase en C++
programacion orientada a objetos c++ ejemplos (9)
1) es una cuestión de gusto. Si lo veo correctamente, sus soluciones segunda y tercera más elegantes mueven el error en ciertas circunstancias desde el tiempo del enlace hasta el tiempo de compilación, que en general debería ser mejor.
2) Se necesita herencia virtual para forzar la responsabilidad de inicializar la clase base (virtual) a la clase más derivada desde donde el ctor de la clase base ya no es alcanzable.
Recientemente, uno de mis amigos me preguntó cómo evitar la herencia de clases en C ++. Él quería que la compilación fallara.
Estaba pensando en eso y encontré 3 respuestas. No estoy seguro cuál es el mejor.
1) Constructor privado (s)
class CBase
{
public:
static CBase* CreateInstance()
{
CBase* b1 = new CBase();
return b1;
}
private:
CBase() { }
CBase(CBase3) { }
CBase& operator=(CBase&) { }
};
2) Utilizando CSealed base class, private ctor & virtual inheritance
class CSealed
{
private:
CSealed() {
}
friend class CBase;
};
class CBase : virtual CSealed
{
public:
CBase() {
}
};
3) Usando una clase base CSealed, ctor protegido y herencia virtual
class CSealed
{
protected:
CSealed() {
}
};
class CBase : virtual CSealed
{
public:
CBase() {
}
};
Todos los métodos anteriores se aseguran de que la clase CBase no se pueda heredar más. Mi pregunta es:
1) ¿Cuál es el mejor método? ¿Hay otros métodos disponibles?
2) Los métodos 2 y 3 no funcionarán a menos que la clase CSealed se herede virutalmente. Porqué es eso ? ¿Tiene algo que ver con vdisp ptr?
PD:
El programa anterior se compiló en el compilador MS C ++ (Visual Studio). referencia: http://www.codeguru.com/forum/archive/index.php/t-321146.html
A partir de C ++ 11, puede agregar la palabra clave final a su clase, por ej.
class CBase final
{
...
La razón principal que puedo ver para querer hacer esto (y la razón por la que vine a buscar esta pregunta) es marcar una clase como no subclassable para que pueda usar de forma segura un destructor no virtual y evitar un vtable por completo.
Estás pasando por contorsiones para evitar nuevas subclases. ¿Por qué? Documente el hecho de que la clase no es extensible y haga que el dtor no sea virtual. En el espíritu de c, si alguien realmente quiere ignorar la forma en que pretendía que se usara, ¿por qué detenerlos? (Nunca vi el punto de las clases / métodos final
en Java tampoco).
//Note: this class is not designed to be extended. (Hence the non-virtual dtor)
struct DontExtened
{
DontExtened();
/*NOT VIRTUAL*/
~DontExtened();
...
};
No puede evitar la herencia (antes de la palabra clave final
C ++ 11): solo puede evitar la instanciación de clases heredadas. En otras palabras, no hay forma de prevenir:
class A { ... };
class B : public A { ... };
Lo mejor que puede hacer es evitar que los objetos de tipo B sean instanciados. Siendo ese el caso, sugiero que tome el consejo de kts y documente el hecho de que A (o lo que sea) no está destinado a ser usado para la herencia, le da un destructor no virtual y no tiene otras funciones virtuales, y déjelo así.
Para profundizar en la respuesta de Francis : si la clase Bottom
deriva de la clase Middle
, que prácticamente hereda de la clase Top
, es la clase más derivada ( Bottom
) la responsable de construir la clase base virtualmente heredada ( Top
). De lo contrario, en el escenario de herencia múltiple / diamante de la muerte (donde la herencia virtual se usa de forma clásica), el compilador no sabría cuál de las dos clases "intermedias" debería construir la clase base única. Por lo tanto, la llamada del constructor del Middle
al constructor de Top
se ignora cuando Middle
se construye desde Bottom
:
class Top {
public:
Top() {}
}
class Middle: virtual public Top {
public:
Middle(): Top() {} // Top() is ignored if Middle constructed through Bottom()
}
class Bottom: public Middle {
public:
Bottom(): Middle(), Top() {}
}
Entonces, en el enfoque 2) o 3) en su pregunta, Bottom()
no puede llamar a Top()
porque se hereda de manera privada (por defecto, como en su código, pero vale la pena hacerlo explícito) en Middle
y así es no visible en la parte Bottom
. ( source )
Para responder a su pregunta, no puede heredar de CBase porque en la herencia virtual una clase derivada necesitaría tener acceso directo a la clase de la cual se heredaba virtualmente. En este caso, una clase que se derivaría de CBase necesitaría tener acceso directo a CSealed que no puede, ya que el constructor es privado.
Aunque no veo la utilidad de todo esto (es decir, detener la herencia) puede generalizar el uso de plantillas (no creo que se compile en todos los compiladores, pero sí en MSVC)
template<class T>
class CSealed
{
friend T; // Don''t do friend class T because it won''t compile
CSealed() {}
};
class CBase : private virtual CSealed<CBase>
{
};
Si puedes, iré por la primera opción (constructor privado). La razón es que casi cualquier programador de C ++ experimentado lo verá de un vistazo y podrá reconocer que está tratando de evitar la creación de subclases.
Puede haber otros métodos más complicados para evitar la creación de subclases, pero en este caso, cuanto más simple, mejor.
Una solución más:
template < class T >
class SealedBase
{
protected:
SealedBase()
{
}
};
#define Sealed(_CLASS_NAME_) private virtual SealedBase<_CLASS_NAME_>
#include "Sealed.h"
class SomeClass : Sealed(Penguin)
{
};
class myclass;
class my_lock {
friend class myclass;
private:
my_lock() {}
my_lock(const my_lock&) {}
};
class myclass : public virtual my_lock {
// ...
public:
myclass();
myclass(char*);
// ...
};
myclass m;
class Der : public myclass { };
Der dd; // error Der::dd() cannot access
// my_lock::my_lock(): private member
Lo encontré aquí para dar crédito. Estoy publicando aquí solo otras personas pueden acceder fácilmente a http://www.devx.com/tips/Tip/38482