[Solucionado]Para qué #ifndef #define #endif??

Hola. Como muchos ya sabran en mi blog TuxMaya empece usa serie de tutoriales que servirian para aprender a programar en C++ y Qt, pero por peresa me dio flojera continuar pero pienso retomarlo de nuevo.

Asi que queria preguntarles como se podria explicar los signal y slots:

connect(holabutton, SIGNAL(clicked()),this, SLOT(hola()));

para que sirve esto:

#ifndef HOLAMENSAJE_H
#define HOLAMENSAJE_H
#endif // HOLAMENSAJE_H

y cual es la diferencia de usar las librerias o las clases???

#include <QPushButton>

y tambien a que se refieren con memoria estatica y dinamica?

Creo que esos ejemplos son

Creo que esos ejemplos son muy liosos, en mi opinion es mejor explicarlo por lo que es, y que es ?
Son punteros a funciones miembro, la señal en este caso clicked no es mas que un metodo que es llamado por Qt, cuando se produce un evento de pulsacion de boton del raton, y que hace clicked ?
Llamar a un puntero a funcion miembro que apunta al slot que alla sido asignado en la funcion connect.

Saludos

Problema con ejemplos

¿Lo más simple? "Cuando realizas ciertas acciones con los elementos Qt, se emiten señales. Estas señales llevan asociada información, y se pierden si no se conectan a slots. Son complementarios, las señales y los slots, como piezas de rompecabezas. Cuando conectas una señal a un slot quedan asociados, de tal forma que cuando se emite la señal se activa el slot, ejecutando su código."

Pero el problema es, que no todo el mundo tiene la misma capacidad de abstracción. Hay gente que trabaja bien con ejemplos visuales, como el caso de la televisión, porque le resulta más sencillo realizar una asociación entre algo que conocen bien y les resulta cercano, y lo que no entienden; y por otro lado, hay gente que se siente más cómoda con diagramas y dibujos, como por ejemplo aquellos del estilo de UML. Los hay que funcionan bien con el lenguaje, puro y duro, y los que necesitan de funciones matemáticas para poder comprender bien las cosas.

Otro problema que existe es el nivel del receptor del tutorial. Si hablamos de punteros a funciones miembro, tienes que explicar qué es un puntero, qué es una función y qué es un miembro... y si me apuras, incluso tienes que explicar qué es una clase. Lo he visto en mi facultad, cuando llega gente de primero o de segundo y pregunta qué es una clase, que si es parecida a una estructura, y cómo se trabaja con ellas. Y yo pienso, ¿para qué hablar de clases, de objetos, de punteros, etc... sí sólo tienes que explicar el mecanismo que hay detrás de las señales y los slots?

¡Un saludo!

Tal vez lleves razon, el tema

Tal vez lleves razon, el tema es que si una persona se pone a aprender Qt es por que se supone que ya deberia tener cierta experiencia con el lenguaje, por lo que explicar las cosas como funcionan exactamente deberia poder entenderse.

gracias

muchas gracias a los por resolver mis dudas. Er Bardo creo que te complicaste un poco explicar sobre los signal & slots, no te entendi.

y por si les interesa, donde vivo acaba de entrar la tormenta Hermine. xD

Imagina que tu clase es una caja :)

Imagina que los objetos Qt son como cajas, que tienen cables que salen y agujeros donde enchufarlos. Algunos cables tienen una patilla de conexión gorda y redonda, o dos más finas que son paralelas como los enchufes normales, o tres dispuestas en un triángulo y que son planas como palas. Los agujeros, algunos tienen el diámetro de la patilla gorda, otros son pequeños y paralelos y otros son ranuras.

Cada cable tiene un nombre, al igual que cada agujero. Tienes un cable llamado "clicked()", por ejemplo. Y un agujero que se llama "close()".

Así que tienes una caja llamada "Botón", y otra llamada "Ventana". Sabes que cuando pulsas el botón, se enciende el cable "clicked()", y quieres que al pulsarlo se cierre la ventana. Si enchufas el cable "clicked()" de la caja "Botón" con el agujero "close()" de la caja "Ventana", cuando pulses el botón se encenderá el cable "clicked()", se encenderá el agujero "close()" y la caja "Ventana" se cerrará. :D

¡Un saludo!

Librerías y clases

Supongo que te refieres a algunos ejemplos de Qt 4, donde en vez de incluir una librería...

//Método estandar o clásico
#include <QString>
#include <QLineEdit>
#include <QToolBox>
#include <QComboBox>
#include <QSpinBox>
 
// ...
// ...
// ...

... se define simplemente la clase.

//Método extraño
#include <QString>
 
class QLineEdit;
class QToolBox;
class QComboBox;
class QSpinBox;
 
// ...
// ...
// ...

¿Por qué se hace esto? Es un poco complicado, pero allá voy...

Mediante la definición de la clase (más concretamente, del símbolo de la clase), en vez de la inclusión de la librería, aceleramos el tiempo de pre-compilación en proyectos grandes, puesto que si no incluimos el archivo de cabecera, el precompilador no necesitará buscarlo, parsearlo y parsear también el resto del código de nuestro archivo fuente para comprobar si existen o no las guardas de inclusión. Lo que hacemos es 'engañar', en cierto modo, al compilador: le hacemos creer que ya está definido el símbolo de la clase (identificador de la misma) dentro del archivo, con lo cual no nos pitará durante la fase de compilado de nuestro código en un archivo objeto separado del main... ¡pero ojo, eso no significa que no nos vaya a pitar si, durante la fase de enlazado, descubre que le hemos engañado y no existe la definición REAL de nuestras clases! Esto sólo lo haremos si, en el archivo principal, vamos a incluir todas las cabeceras que vayamos a emplear en el resto de nuestras clases.

¡Un saludo!

Guardas de inclusión

#ifndef HOLAMENSAJE_H
#define HOLAMENSAJE_H
// Codigo  del archivo
// ...
// ...
// ...
#endif HOLAMENSAJE_H

Los comandos que aparecen arriba se conocen como 'guardas de inclusión'. Para explicarlo bien, es necesario comprender el funcionamiento de los archivos de cabecera (.h) y los archivos fuente (.cpp,.cxx,.c)

Cuando empleamos un compilador, siempre existe una primera fase de PRE-COMPILADO. Durante esta primera fase, se incrusta el contenido de los archivos de cabecera dentro de los archivos fuente, para que de esa forma se puedan encontrar las cabeceras de las funciones (entre otras cosas). Pero, ¿y si, en un despiste, incluimos dos veces un archivo de cabecera? Eso significa que, en la fase de pre-compilación, el contenido del archivo se va a copiar dos veces en el archivo de codigo fuente, con lo cual tendremos dos declaraciones de clases que se pisarán la una a la otra, dando un error. En el caso de funciones quizás pudiera pasarse por alto, ya que dependerá de cómo maneje dicho compilador la sobrecarga de operadores, pero en el caso de clases es prácticamente seguro que nos dará un error.

Para evitar esto, existen las guardas de inclusión. Las guardas de inclusión hacen uso de comandos de precompilación, precedidos por el símbolo '#' La clausula #ifndef significa "si no se ha definido el valor que viene a continuación, copia el siguiente código en el archivo fuente; en caso contrario, ignóralo". El código ha de encerrarse, obligatoriamente, entre la etiqueta #ifndef y la etiqueta #endif, la cual indica el final de cualquier etiqueta condicional (existen #ifndef y también #ifdef). Dentro de dicha clausula, definimos la etiqueta con el comando #define.

¿Para qué sirve esto? Bien, el precompilador recorrerá el primer archivo fuente donde empleemos el archivo de cabecera con las guardas de inclusión, comprobará si se ha definido la etiqueta HOLAMENSAJE_H, y si no se ha definido insertará la etiqueta y el código encerrado entre el #ifndef y el #endif. Cuando vuelva a encontrarse con la librería, la cual puede haberse incluido como parte de otra librería, comprobará la guarda de inclusión y no volverá a pegar el contenido del archivo de cabecera, porque ya existe. :)

Con eso, logramos evitar errores de redefiniciones de clase, y también aligeramos el código: al no tener repetidos los mismos archivos de cabecera una y otra y otra vez, la cantidad de código que debe de procesar el parser (analizador sintáctico) del compilador es menor, y por tanto se ejecuta más rápido, logrando compilaciones más rápidas del código.

¡Un saludo!

Pensemos en ejemplos sencillos

Para explicar el concepto de señales y slots, vamos a fijarnos por un momento en nuestro televisor. Todos (espero) tenemos en nuestra casa un televisor, con sus botones, su mando a distancia, su toma de antena... y si no lo tenemos, lo hemos visto en casa de nuestros padres, nuestras abuelas, etc... ¡y quien no ha tenido que instalar la antena, enchufar el vídeo o el reproductor DVD y hacerse un lío de cables, ¿verdad?! :D

Bien, pensemos en nuestro televisor: si le damos la vuelta, por detrás veremos un montón de conexiones, algunas diferentes, otras similares. Los más modernos tendrán conexiones HDMI, o USB... pero (casi) todos tendrán su toma de antena externa y al menos un euro-conector (al menos, en Europa HAY euroconectores). La cuestión es: resulta evidente que no podemos enchufar nuestro cable de antena en cualquier lado, ¿verdad? Únicamente en su toma correspondiente, la que tiene la misma forma y el mismo hueco que nuestro cable. Y no podemos enchufar el cable de antena de cualquier manera, ¿cierto? Tendremos que conectar el extremo macho o hembra, dependiendo de la forma de la toma. Igual si tenemos un euroconector, no podremos enchufarlo de cualquier manera, únicamente del lado que corresponda (para arriba o para abajo, pues de otra forma no encajará).

Ahora que ya hemos enchufado nuestra antena de televisión, y podemos ver nuestros canales favoritos, volvamos a Qt por un momento. Consideremos que Televisión es una clase, y nuestraTelevisión es un objeto de tipo Television:

Television *nuestraTelevision=new Television(0,Marca::Telefunken); //Si, es un poco vieja

Ahora, imaginemos que Antena es otra clase, y tenemos antenaColectiva como un objeto:

Antena *antenaColectiva=new Antena(0,Tipo::Parabolica); //En la comunidad, derrochamos

Si queremos que la emisión de canales llegue a nuestra tele, tenemos que conectar el cable en ambos extremos, ¿cierto? En la toma de pared (o en la clavija de la parabólica), y en nuestra maravillosa Telefunken:

connect(antenaColectiva,SIGNAL(emision(CanalTV)),nuestraTelevision,SLOT(entradaAntena(CanalTV)));

El concepto es el siguiente: connect establece un 'cable' entre el objeto antenaColectiva, que emite una señal (el conector de tipo 1, o macho) con el objeto nuestraTelevision, en el slot apropiado (el conector de tipo 2, o hembra). Podemos decir que el objeto que 'emite' es el que envía la señal (produce la información), mientras que el objeto que 'recibe' es el que posee el slot (procesa la información recibida).

Por supuesto, el orden en el que hagamos la conexión no resulta relevante: podemos enchufar primero la tele al cable y luego la antena, o al reves:

connect(antenaColectiva,SIGNAL(emision(CanalTV)),nuestraTelevision,SLOT(entradaAntena(CanalTV)));
//o bien
connect(nuestraTelevision,SLOT(entradaAntena(CanalTV)),antenaColectiva,SIGNAL(emision(CanalTV)));

Volvamos de nuevo a la realidad de nuestro salón. Tenemos conectada la antena con la tele, de acuerdo, pero... ¿qué hacemos con el DVD? Resulta que su conector es diferente... ¿pero eso significa que enchufarlo es más complicado? Realmente, el procedimiento es el mismo: buscar la toma correspondiente, que tenga la misma forma, y hacerlo encajar con cuidado. Pero claro, tenemos que asegurarnos de que es la toma correspondiente.

La 'forma' de nuestro cable, es el que determina el tipo de señal que emitimos. Las señales se definen como funciones con un argumento. Dicho argumento es la 'información' que transmitimos con la señal. Así, si tenemos otra clase llamada ReproductorDVD y un objeto llamado nuestroDVD:

ReproductorDVD *nuestroDVD=new ReproductorDVD(0,Marca::Sony,Tipo::DVD); //Todavía no nos hemos pasado al BlueRay

Sabemos que no podemos conectar su salida, que es un euroconector, en la toma de antena... tenemos que buscar UN SLOT COMPATIBLE:

connect(nuestroDVD,SIGNAL(reproduccion(CanalEURO)),nuestraTelevision,SLOT(tomaEuroconector(CanalEURO)));

Como podréis comprobar, no se puede conectar un tipo de señal en un slot que no sea compatible. Por lo general, eso significa que sólo se conectará una señal con un slot que esté esperando recibir como argumento el mismo tipo que el que tiene asociado la señal emitida. Existen algunas excepciones, pero no es el objeto de este mensaje el explicarlas.

Espero que con esta pequeña introducción, haya quedado más claro el concepto de señales y slots.

¡Un saludo!

Veamos, haber si me sale

Veamos, haber si me sale :P

connect(holabutton, SIGNAL(clicked()),this, SLOT(hola()));

connect es una función que te permite conectar una señal(clicks, pulsaciones, cambios, eventos varios, etc.), con alguna función/método.
El primer argumento(holabutton) indica el objeto del cual queres recibir las señales.
El segundo argumento(SIGNAL(clicked())) indica el tipo de señal que queres recibir, pueden ser varias, en tu caso cuando el boton reciba un click.
El tercero(this), si mal no recuerdo, indica el objeto que recibe la señal, en este caso la señal se propaga hacia el widget o ventana contenedora.
El cuarto(SLOT(hola())), indica la funcion que va a recibir la señal.

#ifndef HOLAMENSAJE_H
#define HOLAMENSAJE_H
#endif // HOLAMENSAJE_H

Impide que el compilador incluya mas de una vez una cabecera, y por ende evita molestos errores de compilación.

#include <QPushButton>
class QPushButton

La librería(que si no mal recuerdo la terminología correcta es "biblioteca") es donde se definen las clases, sus propiedades y sus métodos.
Mientras que una clase es un atributo/tipo de variable, que engloba o puede aplicarse a un grupo de objetos, por ejemplo
Surubi, pejerey, trucha y sabalo, son animales(objetos) que pertenecen a la misma clase(pez).

Memoria estática es aquella que se define en tiempo de compilación y no puede ser cambiar en tiempo de ejecución.
La memoria dinámica es aquella que es variable en tiempo de ejecución.

holaa....googleando

bueno googleando te paso esto:

para el sistema de signals and slot:
http://www.pinelo.com/blog/2008/08/29/qt-signals-and-slots/

para lo de las cabeceras:
http://www.zator.com/Cpp/E4_9_10e.htm

http://mx.answers.yahoo.com/question/index?qid=20080816104159AAhSIXh

con lo de las clases y librerias:

http://www.zator.com/Cpp/E1_4_4b.htm

y para lo de la memoria:

http://es.answers.yahoo.com/question/index?qid=20080902213322AAQO50u

personalmente si te puedo decir que conviene, siempre que puedas, usar varibles dinamicas. estas son las que se crean en tiempo de ejecucion y no en tiempo de compilacion.
es decir cuando declaras un puntero miDato *objeto, y luego lo inicializas en el momento que lo necesites como objeto=new miDato; estas haciendo una reserva de memoria en el computador para trabajar con dicha variable justo en el momento que se ejecute esa linea de codigo.

Usando estas varibales dinamicas, podemos asegurarnos, usando la palabra reservada delete, de que variables dinamicas sean destruidas y se libere el espacio de memoria reservado para la variable. por eso en los destructores de cada clase usamos esa linea de codigo: delete this; o delete objeto;

algo muy distinto sucede cuando se usan variables estaticas. pues las que se declaran normalmente.... como por ejemplo miDato objeto o int arreglo[9];
En C++ al momento de declarar una variable de esa forma ya se esta reservando un espacio en memoria para dicha variable al compilar el programa aun cuando no se usen. imaginate la cantidad de memoria que se reserva al compilar un programa con todas sus variables declaradas estaticamente!!! y solo dios sabe si haremos uso de todas ellas... :S

por eso conviene SIEMPRE trabajar con variables dinamicas. porque otorga mayor rendimiento a nuestra aplicacion ya que se consumen menos recursos del computador(RAM y CPU). porque reservamos lo que usamos. y a parte podemos liberarlo.

en conclusion, cuando se dice memoria estatica o dinamica se habla de la manera en la que una aplicacion X maneja la memoria..si dinamicamente o estaticamente. igualmente podemos hablar de pilas estaticas o dinamicas...colas estaticas o dinamicas...arreglos estaticos o dinamicos...etc.

Anuncios Google