Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





como verificar objetos null en c++

Iniciado por Yotes, 05 de Junio de 2009, 09:57:32 PM

« anterior - próximo »

Yotes

Hola a todos, como va?

Estoy programando en C++ hace muy poco, y me surge una duda que no se como solucionar :(.

Resulta que hay algo que no me queda claro, si bien estoy leyendo todo medio apurado por falta de tiempo, me encuentro con que no se como verificar un valor nulo.

Yo vengo de java, y ahi todo lo que no sea tipo de dato primitivo, osea, a los objetos, se los compara directamente con "null" para verificar que esté instanciado.. pero en C++ aun no me hago la idea.. si alguien pudiese decirme mas o menos cuales son las formas de comprobar que que un objeto esta inicializado, que un puntero apunta a algo, que una variable esta inicializada, y no se que otros casos hay, le agradecería muchisimo. Por mi parte estoy tratando de encontrar algo al respecto, pero parece que hoy no es mi día, porque no encuentro mucha data al respecto  Oo.

Desde Ya, muchas gracias :D.  y si no me explico, me avisan y veo si lo redacto mejor :P. Saludos!
Yotes!

HarvesterOfAcorns

Puede que me equivoque, pero creo que no hay una manera de hacer lo que quieres, cuando declaras una variable en C o C++ no tienes ninguna seguridad de que esa variable sea inicializada con un valor nulo, por ejemplo si haces:

int i;

std::cout << i << std::endl;

lo más probable es que imprima cualquier valor distinto de 0

Una buena costumbre es inicializar las variables al declararlas.

int i = 0;
int *p = NULL;

y lo mismo para las instancias de clase, procuras que la constructora inicialice todas las variables que declare.

fjfnaranjo

Mmm, perdonad, pero si no me equivoco, en C++ eso del null no existe.

No puedes tener una variable a null. Otra cosa es que haya definida por ahí alguna macro tipo:

Código (cpp) [Seleccionar]

#define NULL 0
#define NULL ((void*) 0)
etc....


Pero no programes considerando que cuentas con esa posibilidad.
fjfnaranjo.com - Creating entertainment - Creando entretenimiento
fjfnaranjo [4t] gm4il [d0t] c0m (mail y msn)

Yotes

Ok, por lo pronto prabando cosas sencillas parece que se puede comprobar al menos lo del puntero.. ej:

int *p;

if(p){

     std::cout << "inicializado: " << *p << std::endl;

}else{

    std::cout << "nulo"

}

si no inicializo el puntero entra por el false, sino entra por true,.. asi que aqui tengo algo.. supongo entonces que puedo hacer algo similares con punteros y arrays.. ya haré pruebas con objetos haber que se puede hacer.
Yotes!

fjfnaranjo

Como lo estás haciendo te debería de funcionar siempre. Pero recuerda que lo mejor es inicializar los punteros bajo su declaración. Y punto.

Eso te funciona porque el compilador asigna automáticamente 0 a los punteros sin inicializar, que ya de paso es el único valor int que los compiladores dejan asignar a un puntero, y probablemente está así para que se puedan usar como valores que retornan false...

Pero de todas formas, inicializa bajo la declaración siempre que sea posible.
fjfnaranjo.com - Creating entertainment - Creando entretenimiento
fjfnaranjo [4t] gm4il [d0t] c0m (mail y msn)

HarvesterOfAcorns

Cita de: Yotes en 06 de Junio de 2009, 01:24:24 AM
Ok, por lo pronto prabando cosas sencillas parece que se puede comprobar al menos lo del puntero.. ej:

int *p;

if(p){

     std::cout << "inicializado: " << *p << std::endl;

}...

Como lo tienes provoca un error del sistema, aunque tal vez esto dependa del compilador que se use. Imprimes el valor de algo a lo que apunta un indicador que en realidad no sabes donde está apuntando, vamos que si no le pegas un tiro a alguien será puro milagro :P

Creo que en lugar de imprimir el valor de la variable a la que apunta p

std::cout << "inicializado: " << *p << std::endl;

deberías imprimir el valor de p

std::cout << "inicializado: " << p << std::endl;

que si no me equivoco es la dirección de memoria donde se guarda esa variable, y en mi caso no da 0

Cita de: fjfnaranjo en 06 de Junio de 2009, 12:18:54 AM
Mmm, perdonad, pero si no me equivoco, en C++ eso del null no existe.

No puedes tener una variable a null. Otra cosa es que haya definida por ahí alguna macro tipo:


Pues seguro tienes razón sobre lo de NULL, que probablemente sea un valor predefinido mediante alguna macro, pero lo que no tengo tan claro es si forma parte del estándar del lenguaje, o no.

???

Yotes

#6
ok lo de las variables es lo de menos, porque yo siempre las inicializo, pero los objetos si me resulta importante. Por ejemplo, yo estoy haciendo ahora unos tutoriales de nehe para opengle verdad?

bien, yo tengo esta clase por ejempo: (agrego solo la cabecera)

Código (cpp) [Seleccionar]

class AUX_RGBImageRec {
   void convertBGRtoRGB();
public:
   byte *data;
   DWORD sizeX;
   DWORD sizeY;
   bool NoErrors;
   AUX_RGBImageRec(): NoErrors(false), data(NULL) {};
   AUX_RGBImageRec(const char *FileName);
   ~AUX_RGBImageRec();
   bool loadFile(const char *FileName);
   friend AUX_RGBImageRec *auxDIBImageLoad(const char *FileName);
};


Bien despues mas adelante creamos un puntero:

Código (cpp) [Seleccionar]

AUX_RGBImageRec *TextureImage[1]; // Create Storage Space For The Texture
memset(TextureImage,0,sizeof(void *)*1);            // Set The Pointer To NULL


ahora, despues de que yo creo el objeto AUX_RGBImageRec en loadBMP  (TextureImage[0]=LoadBMP(path)), para borrarlo hace lo siguiente:

Código (cpp) [Seleccionar]

if (TextureImage[0]) // If Texture Exists
{
if (TextureImage[0]->data) // If Texture Image Exists
{
free(TextureImage[0]->data); // Free The Texture Image Memory
}

free(TextureImage[0]); // Free The Image Structure
}


Osea que al parecer, esta es una manera de comprobar objetos inicializados mediante punteros... habra otra forma??
Yotes!

HarvesterOfAcorns

Pues es que creo que tenemos conceptos distintos de lo que es inicializar, yo, declarar lo entiendo como nombrar un identificador asociándolo con un tipo determinado:

int n;//sería una declaración

e inicializar lo entiendo como dar un valor determinado a una variable, el valor inicial más concretamente,

int n=5;//sería inicialización

en ese código que has puesto me da que lo que compruebas es que TextureImage[0] ha sido declarada, que existe, para evitar intentar borrar una zona de memoria que "no existe" en tu aplicación. Seguramente si borras la línea

memset(TextureImage,0,sizeof(void *)*1);

que es lo más parecido que veo a una inicialización

if (TextureImage[0])

seguirá dando verdadero y la memoria se liberará igualmente

...o eso creo. :-X

Yotes

claro quiza me expresé mal, pero sería mas o menos asi:

declaramos:
Código (cpp) [Seleccionar]

AUX_RGBImageRec *TextureImage[1];


esta es la parte que quizá pueda dudar:
Código (cpp) [Seleccionar]

memset(TextureImage,0,sizeof(void *)*1);            // Set The Pointer To NULL

Donde dice que lo setea a null.. al parecer lo que hace es hacer que apunte a 0 o mas bien a NULL.. pero no se si estará definido en un macro.. osea que claro, aqui podemos comprobar con un if, si es 0, por lo que no apunta a ningun objeto, cualquier otro valor significaria que apunta a un objeto. Por lo tanto si en mi código declaro un objeto, y lo inicializo, y gestiono el puntero al mismo tiempo, parece ser una solucion.. pero como les digo, soy demasiado novato en esto, asi que no se si hay una mejor manera.
Yotes!

fjfnaranjo

Yo lo que sigo sin entender es para que quieres un puntero sin inicializar.

Implementa la aplicación de forma que no tengas que hacer eso, y punto.

Si es una lista, guarda la cuenta de los objetos declarados en otra variable, por ejemplo.

Y no uses código que a priori sabes que puede estar implementado de varias formas diferentes en distintos compiladores: osea, no uses la macro 'null' y no asumas que un puntero recién declarado pero no inicializado va a valer false.

Estas cosas no están en el estándar, así que lo que vas a provocar es que el código sea muy poco portable (incluso entre compiladores de la misma plataforma).
fjfnaranjo.com - Creating entertainment - Creando entretenimiento
fjfnaranjo [4t] gm4il [d0t] c0m (mail y msn)

Pogacha

#10
No entiendo de que mierda estan hablando ...

Un puntero no es otra cosa que un numero, que puede valer 10 o 20 o cualquier cosa.

Si un puntero vale 0 o 100 es lo mismo para cualquier cosa exepto para el operador delete, que comprueba si el puntero es 0 antes de llamar al destructor y al free. Por eso es una practica mas que razonable tener la convencion de asignar 0 a los punteros cuando no apuntan a un a una zona valida de memoria.

int* p = 0;
delete p;
if(p) *p = 10;

Segundo punto
Si una variable la dejo sin inicializar, ya sea un int o un puntero:
int* p;
El valor de p no esta determinado y puede ser cualquier cosa, 0 o 1000 o 0xcdcdcdcd o 0xbadf00d o cualquier cosa que estaba en la memoria (ya sea ram de datos, codigo, stack o registro) donde p fue alojado y por lo tanto *p no es valido.
Eso muy probablemente lleve a un error de protección.

Edit:
Pero no me lean a mi, aca esta mejor:
http://en.wikipedia.org/wiki/Pointer_(computing)


davur

Varias cosas.

- NULL sí forma parte del estándar de C++ (¿cómo si no aseguras la compatibilidad con C?), concretamente su definición aparece en el apartado C.2.2.3/1:

Cita de: El estándar de C++
The macro NULL, defined in any of<clocale>, <cstddef>, <cstdio>, <cstdlib>, <cstring>, <ctime>, or <cwchar>, is an implementation-defined C++ null pointer constant in this International Standard (18.1).

- Una definición válida, pero enormemente limitada y que no abarca todas sus sutilezas, es que un puntero es una dirección de memoria. Y no, un puntero no es un número: a diferencia de los números, los punteros sólo pueden compararse por orden en casos muy específicos, no pueden convertirse a y desde números de manera fiable generalmente, y no obedecen correctamente a la aritmética de números.

- Siendo p un puntero no inicializado, el comportamiento de *p es indefinido. Si tienes suerte, se manifestará en un crash en tiempo de ejecución. Pero no tienes ninguna garantía de ello, precisamente porque el comportamiento es indefinido.

- El puntero nulo evalúa a false en un contexto booleano, mientras que todos los demás punteros evalúan a true.

- La constante entera 0 evalúa al puntero nulo en un contexto de punteros.

- C++ idiomático y moderno restringe enormemente el uso de punteros, y en su lugar opta por abstracciones de más alto nivel como std::auto_ptr o std::tr1::shared_ptr (entre otros). Si estás utilizando punteros llanos en gran parte de tu código, posiblemente estás haciendo las cosas mal.

- El código de la clase AUX_RGBImageRec es horrible, y ya en pocas líneas muestra muchas formas de cómo no hacer las cosas en C++. Comenzando por el uso de memset para inicializar un puntero a 0 y terminando en el uso de free en lugar de delete.

HarvesterOfAcorns

Cita de: Pogacha en 06 de Junio de 2009, 09:54:17 PM
if(p) *p = 10;

Eso parece un poco arriesgado, no? >:D


Magistral, davur, grax

Yotes

Bueno, es interesante eso del c++ idiomático o moderno, tendría que leer algo al respecto para ver este tema de los punteros. Por lo pronto, ahora confirmas entonces de manera técnica la prueba que habia realizado :P... pero a mi la verdad sigue sin agradarme.

A ver.. este hilo no trata de punteros en si mismo, sinó de saber cuando un objeto esta inicializado o no. Mi pregunta es solo si hay alguna manera de saber esto. Ya se que muchos me dirán, bueno, lo declaras y lo inicializas y listo... pero este no es el tema, la cosa es, puedo saberlo o no? por ejemplo si yo declaro (y pongo esto porque es la que ya esta en el foro)
Código (cpp) [Seleccionar]

AUX_RGBImageRec TextureImage;


¿¿puedo en algun momento saber si TextureImage ha sido inicializado o no?? Oo
Yotes!

davur

#14
Si tienes:


AUX_RGBImageRec textureImage;


Entonces el objeto textureImage está correctamente inicializado (en la zona de memoria conocida como stack, que no debes preocuparte de liberar manualmente), asumiendo que la clase AUX_RGBImageRec tiene un constructor por defecto que se encarga de la correcta inicialización, que debería ser el caso porque los constructores en C++ se utilizan para asegurar las invariantes de una clase (lanzando una excepción en caso de no poder hacerlo).

Por otro lado, si tienes:


AUX_RGBImageRec* textureImage;


Entonces textureImage no está inicializado, y apunta a una dirección de memoria indefinida. Mientras que si tienes:


AUX_RGBImageRec* textureImage = 0;


Sabes que textureImage está inicializado con el valor del puntero nulo (pero no apunta a ningún objeto de tipo AUX_RGBImageRec válido), y puedes utilizar ese hecho para que el objeto participe en contextos booleanos.

Y si tienes:


AUX_RGBImageRec* textureImage = new AUX_RGBImageRec(...);
//...
delete textureImage;


Entonces la situación es básicamente la misma que en el primer caso: asumiendo que el constructor de textureImage no ha lanzado ninguna excepción, textureImage apunta a una región de memoria que contiene un objeto AUX_RGBImageRec correctamente inicializado (pero en esta ocasión en la zona de memoria conocida como free store; deberás liberar dicha región posteriormente y de manera manual con delete).

Tanto para evitar memory leaks ante la presencia de excepciones como para evitar tener que liberar la memoria manualmente, utiliza algún tipo de puntero inteligente, como std::auto_ptr. Es la manera estándar de proceder en C++.






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.