2011-09-13 14 views
34

¿Hay alguna circunstancia en la que sea legítimo que una clase derivada tenga un destructor que no sea virtual? Un destructor no virtual significa que una clase no se debe usar como una clase base. ¿Tener un destructor que no sea virtual de una clase derivada actúa como una forma débil del modificador Java final?Clase derivada con destructor no virtual

Estoy especialmente interesado en el caso en el que la clase base de la clase derivada tiene un destructor virtual.

Respuesta

51

¿Hay alguna circunstancia en la que sea legítimo que una clase derivada tenga un destructor no virtual?

Sí.

Un destructor no virtual significa que una clase no se debe utilizar como una clase base.

No realmente; un destructor no virtual significa que eliminar una instancia de derived mediante un puntero base no funcionará. Por ejemplo:

class Base {}; 
class Derived : public Base {}; 

Base* b = new Derived; 
delete b; // Does not call Derived's destructor! 

Si usted no hace delete de la manera anterior, entonces estará bien. Pero si ese es el caso, entonces probablemente estarías usando composición y no herencia.

Will tener un destructor no virtual de un acto de la clase derivada como una forma débil del modificador final de Java?

No, porque virtual -ness se propaga a las clases derivadas.

class Base 
{ 
public: 
    virtual ~Base() {} 
    virtual void Foo() {}; 
}; 

class Derived : public Base 
{ 
public: 
    ~Derived() {} // Will also be virtual 
    void Foo() {}; // Will also be virtual 
}; 

No hay un mecanismo de lenguaje incorporado en C++ 03 o anterior para evitar subclases (*). Lo cual no es un gran problema, ya que siempre debe prefer composition over inheritance. Es decir, usa herencia cuando una relación "es-a" tiene más sentido que una verdadera relación "tiene-a".

(*) modificador 'final' se introdujo en 11

+17

"' virtual'-ness se propaga a las clases derivadas ". ¿Lo hace? [consulta el Estándar]. Lo hace, 12.4.7: "Si una clase tiene una clase base con un destructor virtual, su destructor (ya sea declarado por el usuario o implícitamente) es virtual. – Raedwald

+2

En una nota relacionada, si tiene una clase Base no virtual, y usted tiene algunos métodos virtuales en su clase derivada, entonces 'Base * b = new Derived(); delete b;' será un comportamiento indefinido y posiblemente bloqueará su programa. Parece bastante seguro, pero no lo es. (Es porque 'b 'no apuntará al 'inicio' del objeto' Derived' - será compensado por el espacio necesario para el vtable. Entonces, el 'delete' no funcionará exactamente en la misma dirección que el' new', y por lo tanto, no es una dirección válida para liberar. Si va a tener métodos virtuales en cualquier lugar, ponga un nombre de usuario virtual en la base. –

0

Sí, No y No.

destructor virtual no tiene nada que ver con la capacidad de la clase a ser una base o una clase derivada. Es uno legítimo como ambos.

Sin embargo, existen ciertas razones para hacer que los destructores sean virtuales. Vea aquí: http://en.wikipedia.org/wiki/Virtual_destructor#Virtual_destructors. Esto hace que una clase, entre otras cosas, tenga una tabla virtual si aún no tiene una. Sin embargo, C++ no necesita tablas virtuales para hacer herencia.

+0

Wikipedia es ideal para un punto de partida en la investigación, no debe utilizarse como fuente de referencia. http://www.wikihow.com/Cite-a-Wikipedia-Article-in-MLA-Format –

1

Un destructor no virtual C++ está perfectamente bien, siempre y cuando no se desea utilizarlo como puntero base para las clases derivadas cuando borrando el objeto.

Si sus clases derivadas de forma polimórfica, pasando y almacenarlo con un puntero base y luego eliminarlo, entonces la respuesta es no, utilice un destructor virtual.

+0

Mi comentario sobre: ​​* Un destructor no virtual está perfectamente bien siempre y cuando lo use No quiero eliminar el objeto con un puntero del tipo uno de sus superclases. * ... Incluso si el destructor es virtual, d Elegir el objeto usando un puntero de tipo superclase, invocará un comportamiento indefinido SI el destructor de superclase no es virtual. – Nawaz

+0

Ahhm derecho, lo siento y thx –

+0

Eso todavía no es correcto. Debes editar la oración anterior. – Nawaz

24

Es perfectamente válido tener una clase Base con un destructor no virtual si nunca va a llamar a eliminar en un puntero de clase Base apuntando a un objeto de clase derivado.

Follow Herb Sutter's Advice:

Norma N °: Sólo si las clases derivadas necesidad de invocar la implementación base de una función virtual, realizar la función virtual protegido. Para el caso especial de sólo el destructor:

Norma N °: Un destructor de la clase base debe ser pública y virtual, o protegido y no virtual.


Tal vez tu pregunta realmente es:
¿El destructor en Derivado clase tiene que ser virtual si destructor de la clase base es virtual?

La respuesta es NO.
Si el destructor de la clase base es virtual, el destructor de la clase derivada ya es implícitamente virtual, no necesita especificarlo como virtual explícitamente.

+0

"¿La clase Destructor en Derivado debe ser virtual si el Destructor de clase base es virtual?" Sí, esa es realmente mi pregunta. – Raedwald

+0

Si no se asignan datos (pila, montón) en la clase derivada (es decir, el tamaño de la base y los objetos derivados son iguales), aún puede ir con el destructor de bases públicas no virtuales. Esta puede ser una solución para "typedefs" que deben emitirse explícitamente: una clase base vectorial (de alguna API no modificable), con subclases punto, normal y dirección con constructores explícitos. – Matthias

2

Depende del propósito de su clase. A veces es una buena práctica para hacer su destructor protegida, pero que no son virtuales - que básicamente dice: "No eliminar un objeto de la clase derivada a través de un puntero de tipo base"

1

Sí, se encuentran:

void dothis(Base const&); 

void foo() { 
    Derived d; 
    tothis(d); 
} 

Aquí la clase se usa polimórficamente, pero delete no se llama, por lo tanto está bien.

Otro ejemplo sería:

std::shared_ptr<Base> create() { return std::shared_ptr<Base>(new Derived); } 

porque un shared_ptr es capaz de utilizar un no-polimórfica delete (a través de tipo de borrado).

Implementé una advertencia en Clang específicamente para detectar la llamada de delete en clases polimórficas no finales con destructores no virtuales, por lo que si usa clang -Wdelete-non-virtual-dtor, advertirá específicamente para este caso.

0

Si su clase derivada no agrega ningún miembro de datos a la clase base, y tiene un cuerpo destructor vacío, entonces no importará si el destructor es virtual o no - todo lo que hará el destructor derivado es llamar al base uno de todos modos. No se recomienda porque es demasiado fácil para que alguien venga y modifique la clase sin ser consciente de estas restricciones.

Si nunca intentas eliminar un objeto mediante un puntero a la clase base, estarás a salvo. Esta es otra regla que es difícil de aplicar y debe usarse con cuidado.

A veces no tiene ningún control sobre la clase base y está obligado a derivar de ella, aunque el destructor no sea virtual.

Finalmente, tener un destructor no virtual en la clase base no impone ninguna restricción en la clase derivada que será impuesta por el compilador, por lo que no creo que se asemeje a la final de Java.

3

Su pregunta no es muy clara. Si la clase base tiene un destructor virtual , la clase derivada tendrá uno, independientemente. No hay forma de que apague la virtualidad, una vez que ha sido declarada.

Y ciertamente hay casos en los que tiene sentido derivar de una clase que no tiene un destructor virtual. La razón por la cual el destructor de clase base debe ser virtual es para que pueda eliminar mediante un puntero a la clase base. Si la derivación es privada, no tiene que preocuparse por , ya que su Derived* no se convertirá a Base*. De lo contrario, he visto la recomendación de que si el destructor de la clase base no es virtual, debe estar protegido; esto evita que un caso de comportamiento indefinido (borrando a través de un puntero a base) que podría ocurrir. En la práctica, sin embargo, muchas clases base (por ejemplo, std::iterator<>) tienen una semántica tal que ni siquiera se le ocurre a que se creen punteros; mucho menos eliminar a través de tales punteros . Entonces, agregar la protección puede ser más esfuerzo de lo que vale.

+0

"Tu pregunta no está clara" +1 –

8

Addresssing la última edición:

Editar: Estoy especialmente interesado en el caso en que la clase base de la clase derivada tiene un destructor virtual.

En ese caso, el destructor de la clase derivada se ser virtual, independientemente de si se agrega el virtual palabra clave o no:

struct base { 
    virtual ~base() {}  // destructor is virtual 
}; 
struct derived : base { 
    ~derived() {}   // destructor is also virtual, because it is virtual in base 
}; 

Esto no se limita a los destructores, si en cualquier señalar en una jerarquía de tipos que un miembro de la función se declara virtual, todas las sustituciones (no sobrecargas) de esa misma función serán virtuales, ya sea que estén declaradas como tales o no. El bit específico para los destructores es que ~derived()anulavirtual ~base() incluso si el nombre del miembro difiere: esa es la única especificidad para los destructores aquí.

0

¿Tener un destructor no virtual de una clase derivada actúa como una forma débil del modificador final de Java?

No, en absoluto. Aquí está mi sugerencia para evitar las subclases en C++ (como el modificador final en Java); hacer que el destructor sea privado en una clase. Entonces puede evitar crear subclases

0

¿No le conviene crear un destructor virtual en la clase base? No destructor en este caso. Si utiliza un puntero a la clase base y crea un destructor no virtual en el compilador principal, ¡genere automáticamente esta advertencia! Puede bloquearlo si desea crear una clase final para padres. Pero lo mejor es marcarlo como un final como:

class Base{ 
public: 
    //No virtual destructor defined 
    virtual void Foo() {}; 
}; 

class Derived final : public Base{ 
public: 
    ~Derived() {} // define some non-virtual destructor 
    void Foo() {}; // Will also be virtual 
}; 

En este caso, el compilador sabe lo que quiere y no genera advertencias. La decisión con el destructor de base virtual vacío no es muy buena pero aceptable. No necesita establecer el atributo final. Pero esto no es lo que want.You Tampoco necesita definir método de base virtual vacío Foo demasiado Mejor es:

class Base{ 
public: 
    //No virtual destructor defined 
    virtual void Foo() = 0; // abstract method 
}; 
class Derived final : public Base{ 
public: 
    ~Derived() {} // define some non-virtual destructor 
    void Foo() {}; // Will also be virtual 
}; 

Es claro código completo para compilador. No se generan avisos y tampoco se usa código de repuesto.

Cuestiones relacionadas