23 de diciembre de 2008

¿Se borró o no se borró?

Hola de nuevo, hacia mucho que no escribía en el blog, y mucho más que no lo hacía criticando el código de los demás.

El Mal Código de hoy es un pequeño ejemplo cosas que sobran en un lenguaje moderno. En concreto la creación y destrucción explicita de variables.
void MiClase::Fin(){
this->p->Save();
this->p->Delete();
if( this->p ){
delete this->p;
this->p = 0;
}
}
En el ejemplo anterior, la única operación útil para el programador es Save. El programador en un momento dado desea salvar p, signifique lo que signifique. El resto del código es a lo que se ve obligado por usar C++. Llama a un método Delete, y por lo confuso que le resulta C++, piensa equivocadamente que actúa como un destructor. Pero a la vez, siente cierta inseguridad. ¿Se habrá borrado el objeto? Como por convenio, los punteros se asignan a cero/null si no apuntan a ningún objecto, realiza la comprobación y si aun apunta a algún sitio, entonces hace la destrucción a mano y pone a cero el puntero.

El error, creo yo, que esta causado por partir de dos supuestos bastante feos. El primero, que el código de Delete esta llamando a su vez al destructor del objeto y y el segundo supuesto, que el destructor del objeto asigna cero a los punteros que tiene asignado (en nuestro caso p). Algo tan simple como lo que pongo a continuación, permitiría llamar al destructor desde Delete.
void PClase::Delete(){
...
this->~PClase();
}
Simple pero mortal. Ya que de ser así estaría llamando al destructor dos veces ya que el segundo supuesto (que ponga a cero p) es casi imposible que se cumpla (se puede hacer pero sería un código tan horrible que os causaría pesadillas).

Mi solución

Para mí la solución es bastante simple, contando con que el código de la clase de p se mantiene. Si puede darse el caso de que p no apunte a ningún lado, se hace la comprobación al final. Y luego, seguro que Delete no llame al constructor, todos los pasos serán necesarios.
void MiClase::Fin(){
if( this->p ){
this->p->Save();
this->p->Delete();
delete this->p;
this->p = 0;
}
}

Epílogo

Para mí lo que parece obvio, es que en un trozo tan pequeño de código, con tan solo 5 lineas significativas, 4 de ellas son necesarias por usar un lenguaje de bajo nivel. Y os aseguro que el ámbito donde se usa este código no necesita de ese bajo nivel.