2011-03-07 29 views
8

¿Es posible hacer un resumen de clase en C++ sin declarar ningún método abstracto? Actualmente, tengo una clase Sprite con una subclase StaticSprite y DynamicSprite. Me gustaría hacer que la clase Sprite sea abstracta.Clase abstracta sin métodos abstractos

El problema es que no hay ningún método que compartan. Bueno, tanto StaticSprite como DynamicSprite podrían compartir un método draw(), pero los parámetros de este método son diferentes, por lo que esta no es una opción.

¡Gracias!

EDIT: Este es el código para demostrar lo que estoy tratando de hacer:

Sprite:

class Sprite 
{ 
    public: 
     Sprite(HINSTANCE hAppInst, int imageID, int maskID); 
     ~Sprite(); 

    protected: 
     HINSTANCE hAppInst; 
     HBITMAP hImage; 
     HBITMAP hMask; 
     BITMAP imageBM; 
     BITMAP maskBM; 
     HDC hSpriteDC; 
}; 

Staticsprite:

class StaticSprite : public Sprite 
{ 
    public: 
     StaticSprite(HINSTANCE hAppInst, int imageID, int maskID); 
     ~StaticSprite(); 

     void draw(Position* pos, HDC hBackbufferDC); 
}; 

Dynamicsprite:

class DynamicSprite : public Sprite 
{ 
    public: 
     DynamicSprite(HINSTANCE hAppInst, int imageID, int maskID); 
     ~DynamicSprite(); 

     void draw(HDC hBackbufferDC); 
}; 

Como ve, es inútil crear un objeto Sprite, por lo que me gustaría hacer que esa clase sea abstracta. Pero no puedo hacer un extracto de draw() ya que usa diferentes parámetros.

+2

¿Por qué debería ser abstracto sin métodos abstractos? –

+1

Probablemente no quieran que se cree una instancia – Erix

+1

¿De qué sirve tener una clase base sin métodos para heredar de todos modos? – Rob

Respuesta

15

Se puede declarar su destructor como virtual pura, ya que todas las clases Toma uno.

class AbstractClass 
{ 
public: 
    virtual ~AbstractClass() = 0 ; 
} ; 

Sin embargo, tendrá que definir este destructor en otro lugar.

AbstractClass::~AbstractClass() {} 
+0

El problema es que la clase tiene algunos miembros que deberían eliminarse en el destructor, por lo que hacerlo abstracto causaría pérdidas de memoria ... – Bv202

+0

Como el destructor de la clase base es virtual, se llamará al destructor de su destructor de clase derivado cuando y donde sea necesario. No debería haber ningún problema. –

+2

@ Bv202: también proporciona una implementación del destructor, por lo que puede realizar la limpieza allí. – fizzer

2

No, no hay.

Usted podría utilizar esto, por supuesto:

class Sprite 
{ 
}; 

pero por supuesto que el compilador no va a quejarse cuando intenta crear una instancia del mismo.

Puede añadir un destructor virtual puro:

class Sprite 
{ 
public: 
    virtual ~Sprite() = 0; 
}; 

o puede hacer que el constructor protegido, parando la creación de instancias:

class Sprite 
{ 
protected: 
    Sprite(); 
}; 
0

Para crear una clase puramente abstracta que no pueda crearse una instancia sin derivar de ella, debe definir al menos un método abstracto.

por ejemplo.

virtual void blah() const = 0; 

lo contrario se puede utilizar cualquier clase como una clase abstracta

+0

En última instancia, la pregunta es "¿por qué molestarse"? – Randolpho

+0

@Randolpho - la pregunta ha cambiado. ¿Quiere decir por qué tiene una clase abstracta o por qué hacerla abstracta si no tiene ningún método? –

+0

Sí, la pregunta cambió. Odio cuando eso ocurre. :) – Randolpho

2

La forma tradicional de hacer esto es hacer un destructor virtual puro, a continuación, ponerlo en práctica.

// .h 
struct Foo { 
    virtual ~Foo() = 0; 
}; 

// .cpp 
Foo::~Foo() { 
} 
+1

No puede implementar un virtual puro. – Cthutu

+5

@Cthutu En realidad * puedes * implementar métodos virtuales puros. Simplemente significa que el niño tiene que implementarlo de nuevo. –

+2

Y en el caso de destructor, debe hacerlo. – fizzer

11

Si la clase no hace nada y no proporciona nada, y sólo existe como un marcador, no hay razón para preocuparse acerca de si es o no es abstracto. Esto es esencialmente un no problema.

Si desea asegurarse de que nunca se crea una instancia, excepto en el contexto de la herencia, utilice protected constructors.

Por favor, evite crear un destructor virtual puro. De esa manera, la locura miente.

2

El problema es que no hay ninguno métodos que comparten.

Bueno, ahí está su problema. ¡No deberías estar usando herencia aquí! Si no tiene la intención de sustituir una clase por la otra, no deberían estar en una relación padre-hijo.

Dependiendo de las necesidades, no deberían tener ninguna relación, plantillas o composición del uso.

EDITAR: Basado en el ejemplo de código, está heredando para volver a usar y sugeriría usar composición en su lugar. Sprite podría ser un struct que es propiedad de ambos DynamicSprite y StaticSprite. Puede poner tanta/poca lógica de ayuda en Sprite según corresponda.

+0

He agregado el código en mi primera publicación. ¿Cómo deberías resolverlo? – Bv202

+0

-1 porque no es posible saber si la herencia se debe usar o no. Revise el marco de visitantes de Alexandrescu para obtener un excelente contrapunto a su declaración. –

3

Para las circunstancias específicas de su publicación, considere la herencia privada. De esta forma, puede utilizar la implementación de la base de manera perezosa, sin publicitar una relación IS-A espuria con el mundo.

-1

Si realmente tiene que utilizar la herencia, considere el uso del idioma de interfaz no virtual (NVI) (ver http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.3)

Esencialmente la clase base definirá una sobrecarga conjunto, no virtual de métodos, que requieren protegida pura funciones virtuales de las clases derivadas.

Lo que definitivamente no quieres hacer es sobrecargar una función virtual (que parece que realmente quieres hacer).

1

El mejor enfoque es proteger a los constructores de clase, de esta manera la clase no se pueden crear instancias directamente y por lo tanto es esencialmente abstracta:

class SomeBaseClass 
{ 
protected: 
    SomeBaseClass() {} 
    SomeBaseClass(const SomeBaseClass &) {} 
    SomeBaseClass &operator=(const SomeBaseClass&) {} 
public: 
    virtual ~SomeBaseClass() {} 
    ... 
}; 

O, más nuevo estilo:

class SomeBaseClass 
{ 
protected: 
    SomeBaseClass() = default; 
    SomeBaseClass(const SomeBaseClass &) = delete; 
    SomeBaseClass &operator=(const SomeBaseClass&) = delete; 
public: 
    virtual ~SomeBaseClass() = default; 
    ... 
}; 

Destructor es generalmente es mejor mantenerlo público porque es posible que desee eliminar un objeto por su puntero de clase base.

Cuestiones relacionadas