polimorfismo - funciones virtuales c++
C++ 11 destructores virtuales y generación automática de funciones especiales de movimiento. (1)
Las reglas para la generación automática de funciones especiales de movimiento (constructor y operador de asignación) en C ++ 11 especifican que no se puede declarar ningún destructor. La lógica es, presumiblemente, que si necesita hacer algo especial en la destrucción, un movimiento puede no ser seguro.
Sin embargo, para las llamadas adecuadas del destructor en el polimorfismo, es necesario declarar el destructor de una clase base como virtual (de lo contrario, eliminar una instancia de una subclase a través de un puntero de su clase base no encadenará correctamente el destructor).
Supongo, entonces, que incluso un destructor vacío impediría que el compilador genere automáticamente funciones especiales de movimiento. Como en:
class Base {
virtual ~Base() { }
};
Sin embargo, puedes usar el destructor por defecto, como en:
class Base {
virtual ~Base() = default;
}
Entonces, pregunta 1: ¿Permitirá esto al compilador generar automáticamente funciones de movimiento especiales?
Sin embargo, hay un problema con el destructor predeterminado explícito. Al menos en el caso de GCC 4.8.2, la firma se cambia implícitamente a noexcept. Como en:
class Base {
virtual ~Base() = default; // compiler changes to:
// virtual ~Base() noexcept;
}
Si bien no tengo problemas con noexcept en un destructor, esto rompería el siguiente código de "cliente":
class Sub : public Base {
virtual ~Sub(); // this declaration is now "looser" because of no noexcept
}
Entonces, la pregunta 2 es más precisa: ¿existe una manera de permitir la generación automática de funciones de movimiento especiales en C ++ 11 y permitir el encadenamiento adecuado del destructor a las subclases (como se describió anteriormente), todo sin romper el código de subclase ("cliente")? ?
No, un destructor predeterminado todavía se considera definido por el usuario, por lo que evitará la generación de operaciones de movimiento. También declare las operaciones de movimiento
default
para que el compilador las genere.Solo debe declarar las operaciones de movimiento como
default
en la clase base. En la clase derivada, el destructor ya no será definido por el usuario (a menos que usted lo diga explícitamente), por lo que las operaciones de movimiento no se eliminarán.
Entonces, lo que yo haría es lo siguiente:
class Base
{
virtual ~Base() = default;
Base(Base&&) = default;
Base& operator=(Base&&) = default;
// probably need to think about copy operations also, as the move disables them
Base(const Base&) = default;
Base& operator=(const Base&) = default;
};
Recomiendo encarecidamente esta charla de la persona que probablemente contribuyó más a la semántica del movimiento: http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014
O, si puede conseguirlo, debería leer el Ítem 17: Entender la generación de funciones de miembro especial del excelente libro Efectivo Moderno de Scott Meyers. Este problema está muy bien explicado.
PD: Creo que deberías pensar un poco más sobre tus clases base. La mayoría de las veces, debe usar clases abstractas, por lo que no habrá necesidad de copiar / mover instancias de ellas.
PSS: Creo que, por defecto, los destructores están marcados como " noexcept
en C ++ noexcept
, por lo que no especificar explícitamente no debería causar ningún problema:
Los constructores hereditarios y los constructores predeterminados declarados implícitamente, los constructores de copia, los constructores de movimiento, los destructores, los operadores de asignación de copia, los operadores de asignación de movimiento son todos excepto excepción (verdadero) de forma predeterminada, a menos que sean necesarios para llamar a una función que no sea excepción (falso) , en cuyo caso estas funciones son noexcept (false).