¿Añadir control "this != NULL" en métodos de la cl

Si eres principiante y tienes alguna consulta entra en este foro.
Responder
Mensaje
Autor
Avatar de Usuario
postit
Mensajes: 59
Registrado: 14/11/2008 9:42 am

¿Añadir control "this != NULL" en métodos de la cl

#1 Mensaje por postit » 25/11/2009 11:32 am

Hola, tengo esta sencilla clase de prueba y esta ejecución que devuelve a posta una violación de segmento:

Código: Seleccionar todo

#include <iostream>
	using namespace std;

class foobar
{
public:
	foobar(int num = 0) { n = num; }
	int get_n() const { return n; }
private:
	int n;
};

int main () {
	foobar *ptr = NULL;
	cout << ptr->get_n() << endl;
	return 0;
}

Código: Seleccionar todo

$ g++ -Wall -W -pedantic -g test.cpp 

$ ./a.out 
Violación de segmento       

$ gdb ./a.out
(gdb) l                                                                      
5       {                                                                    
6       public:
7               foobar(int num = 0) { n = num; }
8               int get_n() const { return n; }
9       private:
10              int n;
11      };
12
13      int main () {
14              foobar *ptr = NULL;

(gdb) b 14
Breakpoint 1 at 0x804866d: file test.cpp, line 14.

(gdb) r
Starting program: /home/postit/a.out

Breakpoint 1, main () at test.cpp:14
14              foobar *ptr = NULL;

(gdb) s
15              cout << ptr->get_n() << endl;

(gdb) s
foobar::get_n (this=0x0) at test.cpp:8
8               int get_n() const { return n; }

(gdb) p this
$1 = (const foobar * const) 0x0

(gdb) p this->n
Cannot access memory at address 0x0

(gdb) s
Program received signal SIGSEGV, Segmentation fault.
0x0804870a in foobar::get_n (this=0x0) at test.cpp:8
8               int get_n() const { return n; }

(gdb) s
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.

(gdb) s
The program is not being run.

(gdb) q
$
Bueno, el objetivo es declarar un puntero a clase "foobar" que apunte a NULL y hacer una llamada al método público foobar::get_n() que devuelve el dato miembro int n;

Entiendo perfectamente por qué se da el error, la pregunta es ¿Debería de comprobar al principio de todos mis métodos (que trabajen con datos miembro de la clase) que el objeto (this) es distinto de NULL?

Es decir, debería de corregir el código de la definición de void get_n() const a algo como esto?:

Código: Seleccionar todo

	int get_n() const {
	  if (this != NULL)
		return n;
	  else
		return 0; // o exit(1) o tratamiento de error o ...
	}
Haciendo la modificación todo va bien lógicamente:

Código: Seleccionar todo

$ g++ -Wall -W -pedantic -g test.cpp
$ gdb ./a.out
(gdb) r
Starting program: /home/postit/a.out
0

Program exited normally.
(gdb) q
Gracias y saludos
int n[]={0x48,0x65,0x6C,0x6C,0x6F,0x2C,0x20,0x77,0x6F,0x72,0x6C,0x64,
0x21,0x0A,0x00},*m=n;main(n){putchar(*m)!='\0'?main(m++):exit(n++);}

Avatar de Usuario
daltomi
Mensajes: 355
Registrado: 28/04/2007 7:29 pm
Ubicación: Argentina

#2 Mensaje por daltomi » 26/11/2009 7:57 am

Buenas.
Si vemos el mensaje del depurador:

Código: Seleccionar todo

(gdb) s
foobar::get_n (this=0x0) at test.cpp:8
8               int get_n() const { return n; } 
Vemos que this = 0 desde un principio, por lo tanto comparar dentro de un metodo como this!=NULL es innecesario, siempre que un puntero sea válido this!=NULL dará verdadero y, caso contrario this==NULL, nunca se podría llamar a ese método en primer lugar.
Sobrecargar una clase con éstas verificaciones esta "desaprobado" porque genera programas lentos y tal vez grandes. Decidir dónde colocarlas, o utilizar lo mecanísmos propios de C++, depende de cada uno por ejemplo cuando se determina un bloque crítico o algo asi.
Por lo tanto dejar que la clase verifique sus propios miembros(sobre todo punteros) y verificar por nuestra cuenta los punteros y demás cuando las declaramos fuera de la clase, me refiero a:

Código: Seleccionar todo

int main () {
   foobar *ptr = NULL; //elección del cliente.
   ... 
   if(ptr != NULL) {  //responsabilidad del cliente.
        call ptr->m1();
        call ptr->m2();
        ...
        }else{
         ptr = new foobar(...);
        ...
        }
        
   return 0;
}
Siempre que se pueda habria que buscar técnicas, como TMP, que permitan detectar errores en compile-time, link-time y asi posponer la verificación de errores en run-time.

Saludos.

Avatar de Usuario
postit
Mensajes: 59
Registrado: 14/11/2008 9:42 am

#3 Mensaje por postit » 27/11/2009 12:17 pm

OK, muchas gracias :-)

Sé que esta duda (o la del chequeo de la autoasignación en constructores de copia, aquí) suena muy absurda (de echo lo es...). También sé que es mejor obtener errores en fases de compilación y/o enlazado en vez de en tiempo de compilación, independientemente de errores lógicos del propio código (los peores) errores de violación de segmento y afines (en tiempo de ejecución) son mucho peores que obtenerlos directamente en la fase de construcción de los binarios.

Pero pregunto estas cosas porque también sé que una no puede fiarse del código del cliente y que se ha de intentar practicar la programación segura :) Si por ejemplo estás escribiendo una librería, no puedes fiarte del cliente y en principio deberías de hacer chequeos de los parámetros de entrada, el tema es ver hasta qué punto (porque en mi caso me he sobrepasado intentando controlar el puntero this...)

Otra razón por la que es absurdo comparar el puntero this del modo en que lo he hecho es la siguiente, imaginemos que en el main() que puse la declaración e inicialización del puntero ptr la realizamos de la siguiente manera:

Código: Seleccionar todo

foobar *ptr = (foobar*)1;
Obtendríamos una violación de segmento, el código sería igualmente compilable y al final has añadido pasos de programa innecesariamente. La traza de depuración que arroja gdb sería:

Código: Seleccionar todo

...
Breakpoint 1, main () at test.cpp:19
19              foobar *ptr = (foobar*)1;
(gdb) s
20              cout << ptr->get_n() << endl;
(gdb) p ptr
$1 = (foobar *) 0x1
(gdb) s
foobar::get_n (this=0x1) at test.cpp:9
9                 if (this != NULL)
(gdb) s
10                      return n;
(gdb) p this->n
Cannot access memory at address 0x1
(gdb) s

Program received signal SIGSEGV, Segmentation fault.
0x08048710 in foobar::get_n (this=0x1) at test.cpp:10
10                      return n;
(gdb) q
A debugging session is active.

        Inferior 1 [process 3284] will be killed.

Quit anyway? (y or n) y
Gracias por la ayuda daltomi :) Por cierto:
daltomi escribió:Siempre que se pueda habria que buscar técnicas, como TMP, que permitan detectar errores en compile-time, link-time y asi posponer la verificación de errores en run-time.
¿TMP? He intentado googlearlo pero no he dado con algo que pueda ser lo que te refieres, ¿Qué es? ¿Algún enlace al respecto? :)

¡Saludos!
int n[]={0x48,0x65,0x6C,0x6C,0x6F,0x2C,0x20,0x77,0x6F,0x72,0x6C,0x64,
0x21,0x0A,0x00},*m=n;main(n){putchar(*m)!='\0'?main(m++):exit(n++);}

Avatar de Usuario
daltomi
Mensajes: 355
Registrado: 28/04/2007 7:29 pm
Ubicación: Argentina

#4 Mensaje por daltomi » 27/11/2009 2:15 pm

Buenas.
Recuerda que:

Código: Seleccionar todo

foobar *ptr = (foobar*)1;
Es del tipo C-style cast, desaprobado por el estandard, lo correcto seria utilizar static_cast<foobar*>(1) donde el compilador advertirá el error.
postit escribió:TMP? He intentado googlearlo pero no he dado con algo que pueda ser lo que te refieres, ¿Qué es? ¿Algún enlace al respecto?
Siento no haber dejado una referencia. Mira éste link. No podría darte un ejemplo concreto cómo utilizarlo sobre lo antes dicho, pero en definitiva es útil también para detectar en compile-time.

Saludos.

Responder

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 8 invitados