viernes, 28 de octubre de 2016

Capítulo 80: Constructores en C++


Diario de un programador. Día 182

Constructores en C++

Al igual que en Java, en C++ también se pueden utilizar constructores.
Un constructor es un tipo de función que tiene por finalidad inicializar los objetos o variables con algún valor válido, para evitar que estos se inicialicen con su valor predeterminado, todo esto con la finalidad de evitar algún posible error.
Un constructor debe definirse con el mismo nombre que su clase, no devuelven ningún tipo de valor (ni siquiera void) y por lo general se declaran como public.
Si una clase no incluye un constructor, el compilador en forma interna crea uno con valores predeterminados.
El código estudiado en el capítulo 75 (tutorial anterior de c++), no posee un constructor y a pesar de que ese programa funciona, las buenas prácticas de programación (según lo que leí en la web) indican que siempre se debe crear un constructor. El código mostrado en esa oportunidad, es el siguiente:

#include<iostream>
#include<string>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::getline;

class Antecedentes{

private:
    string nombre;
    int edad;

public:
    void setNombre(string nom){
        nombre = nom;
    }
    void setEdad(int ed){
        edad = ed;
    }

    string getNombre(){
        return nombre;
    }
    int getEdad(){
        return edad;
    }

    void informe(){
        cout << "Nombre: " << getNombre() << endl;
        cout << "Edad: " << getEdad() << endl;
    }
};

int main(void){

    string nombreUsuario;
    int edadUsuario;

    Antecedentes datos;

    cout << "Ingresa tu nombre: ";
    getline(cin, nombreUsuario);
    cout << "Ingresa tu edad: ";
    cin >> edadUsuario;

    datos.setNombre(nombreUsuario);
    datos.setEdad(edadUsuario);
    datos.informe();

return 0;

}


Este código, lo que hace es solicitar un nombre y una edad, para luego imprimirlo en pantalla. Si a este código le agregamos un constructor, quedaría así:

#include<iostream>
#include<string>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::getline;

class Antecedentes{

private:
    string nombre;
    int edad;


public:

    Antecedentes(){//constructor
      nombre = "";
      edad = 0;
    }

    void setNombre(string nom){
        nombre = nom;
    }
    void setEdad(int ed){
        edad = ed;
    }

    string getNombre(){
        return nombre;
    }
    int getEdad(){
        return edad;
    }

    void informe(){
        cout << "Nombre: " << getNombre() << endl;
        cout << "Edad: " << getEdad() << endl;
    }
};

int main(void){

    string nombreUsuario;
    int edadUsuario;

    Antecedentes datos;

    cout << "Ingresa tu nombre: ";
    getline(cin, nombreUsuario);
    cout << "Ingresa tu edad: ";
    cin >> edadUsuario;

    datos.setNombre(nombreUsuario);
    datos.setEdad(edadUsuario);
    datos.informe();

return 0;
}

He destacado el código correspondiente al constructor:

    Antecedentes(){//constructor
      nombre = "";
      edad = 0;
    }

Como se puede apreciar, este constructor tiene el mismo nombre de su clase, no tiene un valor de retorno y su tarea en este caso es inicializar un par de variables. La variable nombre la inicializó con unas comillas dobles, mientras que la variable edad fue inicializada a cero. De esta forma, cada variable queda inicializada.
Para inicializar un objeto, hay que agregar ciertos parámetros. A continuación un ejemplo, utilizando el mismo código anterior, pero agregando algunos parámetros.

#include<iostream>
#include<string>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::getline;

class Antecedentes{

private:
    string nombre;
    int edad;

public:

    Antecedentes(string nombre, int edad){ //constructor
      setNombre(nombre); //llama a la funcion set para inicializar el nombre
      setEdad(edad); //llama a la funcion set para inicializar la edad
    }

    void setNombre(string nom){
        nombre = nom;
    }
    void setEdad(int ed){
        edad = ed;
    }

    string getNombre(){
        return nombre;
    }
    int getEdad(){
        return edad;
    }
};

int main(void){

    Antecedentes datos("Gus", 36);

    cout << "Nombre: " << datos.getNombre() << endl;
    cout << "Edad: " << datos.getEdad() << endl;
return 0;
}


En este caso el constructor recibe dos argumentos, uno de tipo string y uno de tipo int

    Antecedentes(string nombre, int edad){ //constructor

En este caso necesita de estos dos parámetros, debido a que en este constructor se van a inicializar dos variables locales, correspondiente a las funciones setNombre y setEdad.

setNombre(nombre); //llama a la funcion set para inicializar el nombre
setEdad(edad); //llama a la funcion set para inicializar la edad

Posteriormente, al crear un nuevo objeto, este debe llevar como argumento los datos que se utilizarán para inicializar las variables de las funciones setNombre y setEdad.

Antecedentes datos("Gus", 36);
Las últimas dos líneas realizan una llamada implícita al constructor inicializando los objetos.

    cout << "Nombre: " << datos.getNombre() << endl;
    cout << "Edad: " << datos.getEdad() << endl;

Hay que tener en cuenta que cualquier constructor que no lleve argumentos, se le conoce como constructor predeterminado
Recordar siempre proporcionar un constructor para asegurar que los miembros de datos de las clases se inicialicen con valores significativos.
Es una buena práctica asegurarse que un objeto se inicialice antes que el código invoque a las funciones.
Esto es todo por ahora. Saludos

Gustavo J. Cerda Nilo
Septiembre 2016, Octubre 2016



martes, 25 de octubre de 2016

Capítulo 79: Búsqueda binaria en C


Diario de un programador. Día 181

Búsqueda binaria en C

La búsqueda binaria se debe aplicar en un arreglo ordenado, ya sea de mayor a menor o vice versa. Lo que hace es comparar el valor buscado por el elemento central del arreglo, si son iguales, entonces se da por finalizada la búsqueda, pero si no son iguales, entonces el elemento a buscar se compara con el elemento central y verifica si es mayor o menor que este. Al hacer esto, se  divide el arreglo en dos partes, uno de mayor valor al elemento buscado y otro de menor valor al elemento buscado. Si el elemento buscado es menor al valor central, entonces se seguirá buscando en el arreglo de menor tamaño descartando a la otra parte, no tiene sentido buscar en la otra mitad si todos los números son mayores al número buscado. Es así que se compara nuevamente el valor buscado con el valor central de esta mitad y así sucesivamente hasta encontrar o no encontrar el valor.

Ejemplo:

Supongamos que tengo el siguiente arreglo ordenado:
2,4,5,7,9,11,13,15,17,21,25,30

Lo que necesito verificar es si el número 5 se encuentra en esta lista. Entonces, nuestro algoritmo hará lo siguiente:

Primero, se comparará con el número central de este arreglo.
2,4,5,7,9,11,13,15,17,21,25,30

Entonces, diremos ¿ es 5 (nuestro número) igual a  11 ?
No, no es igual. Entonces ¿ 5 es menor a 11 ?
Si, es menor. Entonces buscaremos en los números que se encuentren antes que el número 11.

Al hacer esta búsqueda dentro de los números menores a 11, es que se crea una especie de división del arreglo. No es una división literalmente como dice la palabra, yo al principio pensé que habría que modificar el tamaño del arreglo a su mitad, pero no es así, simplemente la división consiste en buscar dentro de un rango en específico y ese rango es dado por el número que se encuentra a la mitad del arreglo. Sigamos. Como dijimos, buscaremos en los números que se encuentren antes que el número 11. Entonces nuestra búsqueda se realizará en este rango:
2,4,5,7,9,11

Realizamos la misma operación que se hizo en un principio. Se hará la comparación con el elemento central de esta lista.
2,4,5,7,9,11
¿Es 5 (nuestro número) igual a 5?
Si, si es igual. Entonces el número ha sido encontrado.
Es así como funciona la búsqueda binaria.

En el peor caso, utilizando la búsqueda binaria, la búsqueda en un arreglo de 1024 elemento solo tomará 10 comparaciones (210) en cambio, en la búsqueda lineal, en el peor escenario, tomaría hacer 512 comparaciones para hallar el resultado. Si tuviésemos un arreglo de mil millones de elementos 230 , una búsqueda binaria en el peor de los casos tendría que hacer 30 comparaciones, en cambio la búsqueda lineal tendría que realizar por lo menos 500 millones de comparaciones. Esa es una gran diferencia en cuanto a rendimiento entre la búsqueda lineal  y la binaria.

El algoritmo de búsqueda que se comentó (2,4,5,7,9,11,13,15,17,21,25,30) puede ser implementado de la siguiente manera.

#include<stdio.h>

int main(void){
  int i, buscar, lista[100], mitad;
  int a = 0;
  int b = 100;
  int contadorA = 0;
  int contadorB = 0;

  printf("====================================================\n\n");
  //se llena el arreglo
  for( i = 0; i <= 100; i++){
    lista[i] = i * 2;
  }//fin for

//se muestra el arreglo en pantalla
  for(i = 0; i <= 100; i++){
    printf("%d  ", lista[i]);
    if( i % 10 == 0 && i != 0)
      printf("\n");
  }
  printf("\n\n====================================================\n");

  printf("Ingresa un numero a buscar: ");
  scanf("%d", &buscar);

  while (a <= b){
    contadorA++;
    mitad = (a + b) / 2;

    if(buscar > 200){
      printf("Numero no encontrado\n");
      break;
    }

    if(buscar == lista[mitad]){
      printf("Numero %d encontrado en posicion %d\n", lista[mitad], mitad);
      break;
    }
    else if(buscar < lista[mitad]){
      b = mitad -1;
    }
    else{
      a = mitad + 1;
    }
    contadorB++;

  }//fin while

if(contadorA == contadorB){
  printf("Numero no encontrado\n");
}

return 0;

}



Esto es todo por ahora, espero se haya entendido.


Gustavo J. Cerda Nilo
Agosto 2016, Octubre 2016




viernes, 21 de octubre de 2016

Capítulo 78: Creando líneas en Java


Diario de un programador. Día 180


Dibujando líneas en Java


Para dibujar líneas en Java hay que utilizar un sistema de coordenadas, el mismo que se ha visto en oportunidades anteriores, donde la ubicación 0,0 corresponde a la esquina superior izquierda.
En este tutorial, se van a ver varias instrucciones nuevas así que trataré de explicarlas poco a poco.
Lo primero que vamos a hacer es dibujar una simple línea diagonal en una ventana gráfica, donde posteriormente iremos agregando más líneas.

Lo primero que haremos será crear un proyecto nuevo. En mi caso el proyecto se llama Grafico01. Este proyecto tiene dos clases, una que llamé Inicio y la otra que llamé Ventana


En primer lugar voy a escribir en la clase Ventana.

En esta clase, lo primero que voy a hacer es importar dos clases. La clase Graphics, que se encuentra en el paquete java.awt y la clase JPanel, que se encuentra en el paquete javax.swing
La clase Graphics proporciona varios métodos que permiten dibujar texto y figuras en la pantalla, mientras la clase JPanel, proporciona un área en la cual podemos dibujar.
Para importar ambas clases hacemos lo siguiente en la clase Ventana:

import java.awt.Graphics;
import javax.swing.JPanel;

Una vez escrito lo anterior, lo siguiente es crear la clase, la cual creamos de la siguiente forma:

import java.awt.Graphics;
import javax.swing.JPanel;

public class Ventana extends JPanel{
}

Esta clase incluye algo que no habíamos visto hasta ahora, la palabra extends, significa que la clase Ventana hereda las características y atributos de la clase JPanel. En resumidas cuentas, esto significa que la clase Ventana podrá ocupar los métodos de la clase JPanel. El tema de la herencia es otra de las características de la programación orientada a objetos. La herencia la estudiaremos con más detalle en futuros tutoriales.
Lo siguiente que debemos crear, es un método que el sistema llama automáticamente cada vez que necesita llamar a JPanel. Todo JPanel debe tener este método, el cual se escribe de la siguiente forma:

import java.awt.Graphics;
import javax.swing.JPanel;

public class Ventana extends JPanel{

    public void paintComponent(Graphics g){

    }
}

Este método se llama cuando se muestra un objeto JPanel por primera vez en la pantalla, cuando una ventana en la pantalla lo cubre y después lo descubre, y cuando la ventana en la que aparece cambia su tamaño.
La primera instrucción que debe tener todo método paintComponent es la siguiente:

import java.awt.Graphics;
import javax.swing.JPanel;

public class Ventana extends JPanel{

    public void paintComponent(Graphics g){
        super.paintComponent(g);
    }
}

Esta instrucción asegura que el panel se despliegue apropiadamente en la pantalla antes de empezar a dibujar en él.
Lo siguiente que vamos a ocupar, aunque no es obligatorio es interesante. Son los métodos getHeight() y getWidth(), los cuales en este caso devolverán el alto y el ancho del objeto JPanel. Estos valores los almacenaremos en unas variables llamadas "alto" y "ancho", de la siguiente manera:

import java.awt.Graphics;
import javax.swing.JPanel;

public class Ventana extends JPanel{

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        int alto = getHeight();
        int ancho = getWidth();
    }
}

Por último, vamos a utilizar la referencia g de la clase Graphics para llamar al método drawLine, el cual nos permitirá dibujar líneas. El método drawLine requiere de cuatro argumentos, x1,y1 para indicar el comienzo de la línea y x2,y2 para indicar el final de la línea.

import java.awt.Graphics;
import javax.swing.JPanel;

public class Ventana extends JPanel{

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        int alto = getHeight();
        int ancho = getWidth();
       
        g.drawLine(0,0,ancho,alto);
    }
}

Las primeras dos coordenadas se establecieron en la coordenada 0,0 lo que significa que el inicio de la línea se encuentra en la equina superior izquierda. Las últimas dos coordenadas se establecieron según el ancho y el alto del panel. Esto significa que el final de la línea se encuentra en la esquina inferior derecha. Estas coordenadas dibujan una línea diagonal. Si se cambia el tamaño de la ventana, las líneas se escalarán de manera uniforme ya que los argumentos se basan en el alto y ancho del panel. Al cambiar el tamaño de la ventana, el sistema llama a paintComponent para volver a dibujar el contenido.

Ya con esto, damos por finalizado el código de la clase Ventana. Pasemos ahora a escribir el código de la clase Inicio.

Para poder colocar el panel necesitamos de un marco, (por esta razón a la otra clase le di el nombre ventana. Imaginé que la ventana es el vidrio y que el vidrio debe ser puesto en un marco). Para esto, vamos a importar la clase JFrame, la cual se encuentra en el paquete javax.swing. Esto quedará de la siguiente manera:

import javax.swing.JFrame;
Lo siguiente es crear la clase y su método main:

import javax.swing.JFrame;
public class Inicio {
  public static void main(String[]args){

}

Lo siguiente que vamos a hacer es crear una instancia de la clase Ventana, de la siguiente manera:

Ventana panel = new Ventana();

Esto  es algo que ya se sabe hacer así que no hay nada nuevo que explicar.
Lo siguiente, es crear un objeto de la clase JFrame que permita contener y mostrar el panel. Esto quedará de la siguiente manera:

import javax.swing.JFrame;
public class Inicio {
  public static void main(String[]args){
   
    Ventana panel = new Ventana();
    JFrame marco = new JFrame();

}

Lo siguiente es utilizar un método que permite adjuntar el objeto que contiene el dibujo al objeto JFrame.  Este es el método add de JFrame. Se utiliza de la siguiente forma:


import javax.swing.JFrame;
public class Inicio {
  public static void main(String[]args){
   
    Ventana panel = new Ventana();
    JFrame marco = new JFrame();
    marco.add(panel);

}
Lo siguiente es dar un tamaño a nuestro marco. Para esto, se utiliza el método setSize, el cual recibe como parámetros el ancho y el alto del objeto JFrame.

import javax.swing.JFrame;
public class Inicio {
  public static void main(String[]args){
   
    Ventana panel = new Ventana();
    JFrame marco = new JFrame();
    marco.add(panel);
    marco.setSize(250,250);

}

El siguiente método, nos permite mostrar el objeto JFrame, cuando se muestra este objeto, se hace una llamada al método paintComponent. El método a utilizar es setVisible y se utiliza de la siguiente manera:

import javax.swing.JFrame;
public class Inicio {
  public static void main(String[]args){
   
    Ventana panel = new Ventana();
    JFrame marco = new JFrame();
    marco.add(panel);
    marco.setSize(250,250);
    marco.setVisible(true);
}

Ya por último, nos queda implementar un método que permita que el programa finalice si el usuario cierra la ventana (presionando la X de la esquina superior derecha). Sin este método, el programa continuará corriendo si el usuario cierra la ventana. Este método es setDefaultCloseOperation y utiliza como argumento JFrame.EXIT_ON_CLOSE, para indicar que el programa debe finalizar si se cierra la ventana. Este método se vería de la siguiente forma:

import javax.swing.JFrame;
public class Inicio {
  public static void main(String[]args){
   
    Ventana panel = new Ventana();
    JFrame marco = new JFrame();
    marco.add(panel);
    marco.setSize(250,250);
    marco.setVisible(true);
    marco.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

Ya está listo. Al ejecutar el programa esto es lo que debería salir:


Además, si con el ratón cambias el tamaño de la ventana, la línea se ajusta al tamaño de esta


Esto es todo por ahora. Saludos


Gustavo J. Cerda Nilo
Agosto 2016, Octubre 2016




C++ El apuntador This

El apuntador This En C++, cada objeto tiene acceso a su propia dirección a través de un puntero o apuntador denominado This. Lo...