Constructores¶

¿Qué son los Constructores?¶

Los constructores son métodos especiales en una clase que se invocan automáticamente cuando se crea un objeto de esa clase. Su principal propósito es inicializar atributos de objeto y realizar cualquier configuración o asignación que sea necesaria al momento de crear una nueva instancia.

Cual es su propósito¶

  1. Inicialización de Atributos: Establecer valores predeterminados para los atributos del objeto.
  2. Asignación de Recursos: Reservar recursos como memoria o archivos.
  3. Validación: Comprobar que los argumentos proporcionados durante la creación del objeto sean válidos.

Reglas generales¶

  1. Nombre Específico: Los constructores suelen tener un nombre específico y no pueden ser renombrados.
  2. No tienen Valor de Retorno: No devuelven ningún valor, ni siquiera void.
  3. Sobrecarga: Algunos lenguajes permiten sobrecargar constructores, lo que significa que puedes tener más de un constructor en la misma clase con diferentes listas de parámetros.

Constructor en Python¶

En Python, el constructor se define mediante el método __init__. Se ejecuta automáticamente cuando creas una nueva instancia de una clase.

class Persona:
    def __init__(self, nombre):
        self.nombre = nombre

p = Persona("Jose")  # Aquí se invoca el constructor

Constructor en Java¶

En Java, el constructor tiene el mismo nombre que la clase y puede sobrecargarse.

public class Persona {
    String nombre;

    public Persona(String nombre) {
        this.nombre = nombre;
    }
}

Persona p = new Persona("Jose");  // Invoca el constructor

Constructor en C++¶

En C++, el constructor también tiene el mismo nombre que la clase. C++ permite sobrecarga de constructores, y también tiene algo conocido como constructores de copia.

class Persona {
public:
    std::string nombre;

    Persona(std::string n) : nombre(n) {}  // Constructor
};

Persona p("Jose");  // Invoca el constructor

Detalles y diferencias clave¶

  1. Python:

    • Puedes tener un único método __init__ en una clase.
    • Se pueden usar argumentos con valores por defecto para emular la sobrecarga.
  2. Java:

    • Puedes tener múltiples constructores mediante sobrecarga.
    • Java proporciona un constructor predeterminado si no defines uno.
  3. C++:

    • Ofrece la sobrecarga de constructores, constructores de copia, y constructores de movimiento.
    • Si no se especifica un constructor, C++ crea uno predeterminado.

Espero que esto te ayude a entender mejor los constructores y cómo varían en diferentes lenguajes de programación.


Sobrecarga de Metodos:¶

La sobrecarga de métodos es un concepto en programación orientada a objetos donde dos o más métodos en una clase tienen el mismo nombre pero parámetros diferentes. La sobrecarga de métodos permite que un método realice diferentes tipos de tareas dependiendo del número o el tipo de argumentos que se le pasen. No está relacionada con el tipo de retorno del método; es decir, dos métodos no pueden diferenciarse únicamente por su tipo de retorno.

Reglas generales para la sobrecarga de métodos: *Nombre del Método: Deben tener el mismo nombre de método.

  • Lista de Parámetros: Deben diferir en el tipo, el número o ambos.

  • Tipo de Retorno: El tipo de retorno no puede ser utilizado para diferenciar métodos sobrecargados y no participa en la sobrecarga.

  • Nivel de Acceso: El nivel de acceso (público, privado, etc.) no puede ser utilizado para diferenciar métodos sobrecargados.

#include <iostream>
// Método para sumar dos enteros
int sumar(int a, int b) {
  return a + b;
}

// Método para sumar tres enteros
int sumar(int a, int b, int c) {
  return a + b + c;
}

// Método para sumar dos números de coma flotante
float sumar(float a, float b) {
  return a + b;
}

int main(int, char**){
    std::cout << sumar(2.6f,2.4f) << std::endl;
    std::cout << sumar(3,4) << std::endl;
    std::cout << sumar(7,6,9) << std::endl;

}

En estos ejemplos, el método sumar está sobrecargado con diferentes tipos y números de argumentos.

Ventajas de la sobrecarga de métodos:¶

  • Mayor Legibilidad: No es necesario memorizar múltiples nombres de métodos.

  • Facilita el Uso: No tienes que preocuparte demasiado acerca del tipo de argumento que debes pasar; el compilador se encargará de invocar el método correcto basado en los argumentos.

  • Mantenimiento Más Fácil: Si quieres cambiar cómo se realiza una tarea específica, sólo necesitas modificar un método en lugar de buscar múltiples métodos que hagan la misma tarea de diferentes maneras.

  • Polimorfismo: Facilita el polimorfismo, permitiendo que el mismo método tenga comportamientos diferentes según los parámetros que reciba.

La sobrecarga de métodos es un concepto ampliamente utilizado en muchos lenguajes de programación como Java, C++, C#, y más, aunque la forma en que se implementa y las reglas específicas pueden variar de un lenguaje a otro.

Sobrecarga de Métodos en Python¶

Python no soporta la sobrecarga de funciones en el sentido clásico (como en C++ o Java), donde puedes declarar múltiples funciones con el mismo nombre pero con diferentes tipos o número de argumentos.

Sin embargo, puedes lograr un efecto similar utilizando argumentos predeterminados, argumentos de longitud variable como args y kwargs, o mediante la inspección del tipo o el número de argumentos dentro de la función*.

Usando argumentos opcionales¶

Una forma común de emular la sobrecarga de métodos en Python es mediante el uso de argumentos opcionales con valores predeterminados.

In [ ]:
def sumar(a, b=0, c=0):
    return a + b + c

print(sumar(1))         # 1
print(sumar(1, 2))      # 3
print(sumar(1, 2, 3))   # 6
1
3
6

Usando el tipo de los argumentos¶

También podrías verificar los tipos de los argumentos dentro del método y luego realizar diferentes acciones en función de esos tipos.

In [ ]:
def sumar(a, b):
    if type(a) == type(b) == int:
        return a + b
    elif type(a) == type(b) == str:
        return a + " " + b
    else:
        return "Tipos incompatibles"

print(sumar(1, 2))      # 3
print(sumar("Hola", "Mundo")) # "Hola Mundo"
print(sumar(1, "dos"))  # "Tipos incompatibles"
3
Hola Mundo
Tipos incompatibles

Usando args y kwargs¶

Puedes usar args para pasar una lista variable de argumentos no clave, y *kwargs para pasar un diccionario de argumentos clave.

Uso de *args¶

La notación *args en una función permite pasar un número variable de argumentos no clave.

In [ ]:
def sumar(*args):
    return sum(args)

print(sumar(1, 2, 3, 4, 5))  # Devuelve 15

def multiplicar(*args):
    resultado = 1
    for num in args:
        resultado *= num
    return resultado

print(multiplicar(1, 2, 3))  # Devuelve 6
15
6

Uso de **kwargs¶

La notación **kwargs permite pasar un número variable de argumentos clave.

In [ ]:
def imprimir_datos(**kwargs):
    for clave, valor in kwargs.items():
        print(f"{clave}: {valor}")

imprimir_datos(nombre="John", edad=30, país="EE.UU.")
# Salida:
# nombre: John
# edad: 30
# país: EE.UU.

Combinando args y *kwargs¶

También puedes combinar args y *kwargs en una sola función.

In [ ]:
def combinado(a, b, *args, clave1=None, **kwargs):
    print(f"a: {a}")
    print(f"b: {b}")
    print(f"args: {args}")
    print(f"clave1: {clave1}")
    print(f"kwargs: {kwargs}")

combinado(1, 2, 3, 4, 5, clave1="clave uno", clave2="clave dos", clave3="clave tres")
# Salida:
# a: 1
# b: 2
# args: (3, 4, 5)
# clave1: clave uno
# kwargs: {'clave2': 'clave dos', 'clave3': 'clave tres'}
a: 1
b: 2
args: (3, 4, 5)
clave1: clave uno
kwargs: {'clave2': 'clave dos', 'clave3': 'clave tres'}

En este ejemplo, 1 y 2 son asignados a a y b respectivamente. 3, 4, 5 son recogidos por args. clave1="clave uno" es capturado por clave1, y el resto de los argumentos clave (clave2 y clave3) son recogidos por *kwargs.

Sobrecarga de Métodos en C++¶

En C++, la sobrecarga de métodos permite a múltiples funciones con el mismo nombre existir en el mismo ámbito, diferenciándose por su lista de parámetros (también conocida como "lista de argumentos").

Ahora, vamos a ver algunos ejemplos para aclarar estos puntos:

Ejemplo Básico¶

#include <iostream>
using namespace std;

class Calculadora {
public:
    // Sobrecarga del método sumar para manejar enteros
    int sumar(int a, int b) {
        return a + b;
    }

    // Sobrecarga del método sumar para manejar flotantes
    float sumar(float a, float b) {
        return a + b;
    }
};

int main() {
    Calculadora calc;

    cout << calc.sumar(1, 2) << endl;   // Llama a sumar(int, int)
    cout << calc.sumar(1.5f, 2.5f) << endl;  // Llama a sumar(float, float)

    return 0;
}

Ejemplo con Argumentos Predeterminados (¡Cuidado con la ambigüedad!)¶

#include <iostream>
using namespace std;

class Ejemplo {
public:
    void mostrar(int a, int b = 10) {
        cout << "Entero: " << a << ", " << b << endl;
    }

    void mostrar(double a, double b) {
        cout << "Flotante: " << a << ", " << b << endl;
    }
};

int main() {
    Ejemplo obj;

    // Esto es ambiguo porque C++ no sabe si debería llamar a mostrar(int, int) con b = 10
    // o convertir 5 y 10 a double y llamar a mostrar(double, double).
    // obj.mostrar(5, 10);  // Esto causará un error de compilación

    // Sin ambigüedad aquí
    obj.mostrar(5, 10.0);  // Llama a mostrar(double, double)

    return 0;
}

En el ejemplo anterior, el llamado obj.mostrar(5, 10) es ambiguo y provocará un error de compilación. Este es un ejemplo de cómo los argumentos predeterminados y la sobrecarga pueden interactuar de manera problemática si no se tiene cuidado.

Otro ejemplo simple para demostrar la sobrecarga de funciones en C++:

#include <iostream>
using namespace std;

class Calculadora {
public:
    // Sumar dos enteros
    int sumar(int a, int b) {
        return a + b;
    }

    // Sumar tres enteros
    int sumar(int a, int b, int c) {
        return a + b + c;
    }

    // Sumar dos números de punto flotante
    double sumar(double a, double b) {
        return a + b;
    }
};

int main() {
    Calculadora calc;
    cout << "Resultado 1: " << calc.sumar(2, 3) << endl;  // Salida: Resultado 1: 5
    cout << "Resultado 2: " << calc.sumar(2, 3, 4) << endl;  // Salida: Resultado 2: 9
    cout << "Resultado 3: " << calc.sumar(2.0, 3.0) << endl;  // Salida: Resultado 3: 5.0
    return 0;
}

Espero que esto aclare las reglas, restricciones y dificultades asociadas con la sobrecarga de funciones en C++.

Reglas y restricciones para la sobrecarga de métodos en C++¶

  1. Firma única: La sobrecarga de funciones en C++ debe diferir en el número y/o tipo de argumentos que toman. No puedes sobrecargar funciones simplemente cambiando el tipo de retorno.

  2. Tipos de datos: Puedes sobrecargar una función cambiando el tipo de uno o más de los parámetros.

  3. Número de parámetros: Puedes sobrecargar una función cambiando el número de parámetros.

  4. Tipos de datos y número de parámetros: Puedes sobrecargar una función cambiando tanto el tipo como el número de parámetros.

  5. Valor predeterminado: No puedes sobrecargar una función únicamente por tener un valor predeterminado diferente para un parámetro.

  6. Modificadores de acceso: El cambio en el nivel de acceso (público, privado o protegido) no es suficiente para la sobrecarga de funciones.

Dificultades:¶

  1. Ambigüedad: Asegúrate de que no haya ambigüedad cuando sobrecargues funciones. Por ejemplo, si tienes un método que toma int, double y otro que toma double, int, y luego intentas llamar a este método con dos int, el compilador no sabrá qué versión usar.

  2. Conversión de tipos implícita: C++ también realiza conversiones de tipos implícitas, lo cual podría llevar a resultados inesperados y errores difíciles de rastrear.

  3. Legibilidad del código: El uso excesivo de la sobrecarga de funciones puede dificultar la lectura del código, ya que una sola función podría hacer muchas cosas diferentes dependiendo de los argumentos proporcionados.

Sobrecarga de Métodos en Java¶

En Java, puedes sobrecargar métodos cambiando el tipo y/o el número de parámetros. No puedes sobrecargar métodos solo cambiando el tipo de retorno. Aquí hay un ejemplo simple pero ilustrativo:

public class Calculadora {

    // Método para sumar dos enteros
    public int sumar(int a, int b) {
        return a + b;
    }

    // Método para sumar tres enteros
    public int sumar(int a, int b, int c) {
        return a + b + c;
    }

    // Método para sumar dos números de punto flotante
    public double sumar(double a, double b) {
        return a + b;
    }

    // Esto NO es una sobrecarga válida, ya que solo cambia el tipo de retorno
    // public double sumar(int a, int b) {
    //    return (double) (a + b);
    // }

    public static void main(String[] args) {
        Calculadora calc = new Calculadora();
        System.out.println("Resultado 1: " + calc.sumar(2, 3));  // Salida: Resultado 1: 5
        System.out.println("Resultado 2: " + calc.sumar(2, 3, 4));  // Salida: Resultado 2: 9
        System.out.println("Resultado 3: " + calc.sumar(2.0, 3.0));  // Salida: Resultado 3: 5.0
    }
}

Reglas y restricciones para la sobrecarga de métodos en Java¶

  1. Lista de parámetros: Deben variar en tipo, número o ambos para ser considerados como diferentes.
  2. Tipo de retorno: No puede ser el único factor para sobrecargar un método.
  3. Modificadores de acceso: Pueden ser diferentes para métodos sobrecargados.
  4. Lanzamiento de Excepciones: No afecta la firma del método, por lo que no se considera en la sobrecarga.

Dificultades¶

  1. Ambigüedad: Si defines métodos que son demasiado similares, podrías terminar con ambigüedad. Por ejemplo, si tienes un método que toma double y otro que toma float, y luego llamas a este método con un valor int, el compilador no podrá decidir cuál de los dos métodos es más específico.
  2. Conversión de tipos implícita: Java realizará conversiones de tipos implícitas, lo que a veces puede llevar a resultados inesperados.

Espero que este ejemplo y las reglas y restricciones te sean útiles para entender la sobrecarga de métodos en Java.


Sobrecarga de contructores¶

Por supuesto, la sobrecarga de constructores se refiere a la práctica de tener múltiples constructores en una clase que difieren en la cantidad o tipo de argumentos. Aquí hay un ejemplo simple de una clase Rectangulo que tiene múltiples constructores para inicializar su longitud y ancho.

Java¶

En Java, puedes tener múltiples constructores en la misma clase, siempre y cuando tengan listas de parámetros diferentes.

public class Rectangulo {
    int longitud;
    int ancho;

    // Constructor sin argumentos
    public Rectangulo() {
        this.longitud = 0;
        this.ancho = 0;
    }

    // Constructor con un argumento
    public Rectangulo(int lado) {
        this.longitud = lado;
        this.ancho = lado;
    }

    // Constructor con dos argumentos
    public Rectangulo(int longitud, int ancho) {
        this.longitud = longitud;
        this.ancho = ancho;
    }
}

Cosas a tener en cuenta en Java:¶

  • Si no proporcionas ningún constructor, Java crea un constructor predeterminado sin argumentos para ti. Pero si proporcionas al menos un constructor, entonces debes proveer un constructor sin argumentos si lo necesitas.

C++¶

En C++, puedes sobrecargar constructores de manera similar a Java. Además, puedes usar listas de inicialización para asignar valores a los miembros.

class Rectangulo {
public:
    int longitud;
    int ancho;

    // Constructor sin argumentos
    Rectangulo() : longitud(0), ancho(0) {}

    // Constructor con un argumento
    Rectangulo(int lado) : longitud(lado), ancho(lado) {}

    // Constructor con dos argumentos
    Rectangulo(int longitud, int ancho) : longitud(longitud), ancho(ancho) {}
};

Cosas a tener en cuenta en C++:¶

  • Los constructores en C++ también pueden tener valores predeterminados para los argumentos, lo cual puede reducir la necesidad de sobrecargar constructores.

Python¶

Python no permite múltiples constructores, pero puedes utilizar argumentos opcionales y lógica condicional en el método __init__ para lograr algo similar.

class Rectangulo:
    def __init__(self, longitud=0, ancho=0):
        if longitud == ancho:
            print("Es un cuadrado.")
        self.longitud = longitud
        self.ancho = ancho

Cosas a tener en cuenta en Python:¶

  • Puedes usar argumentos con palabras clave y argumentos opcionales para hacer que tu constructor sea más flexible.
  • Si necesitas más control, puedes usar *args o **kwargs para capturar una lista arbitraria de argumentos posicionales o nombrados.

Resumen¶

  • En Java y C++, la sobrecarga de constructores es más estricta y debes declarar múltiples constructores con diferentes firmas.
  • En Python, debido a su tipado dinámico, tienes más flexibilidad pero también más responsabilidad para manejar diferentes casos dentro del constructor.

Manejo de Errores¶

El manejo de errores es una parte crucial de la programación robusta. Cada lenguaje tiene sus propias formas de tratar con situaciones excepcionales. A continuación, un ejemplo simple que muestra cómo manejar un error de división por cero en Java, C++ y Python.

Java¶

En Java, puedes usar las instrucciones try, catch y finally para manejar excepciones. Las clases de excepción son subclases de la clase Throwable.

public class Main {
    public static void main(String[] args) {
        int numerador = 10;
        int denominador = 0;

        try {
            int resultado = numerador / denominador;
            System.out.println("Resultado: " + resultado);
        } catch (ArithmeticException e) {
            System.out.println("Error: " + e.getMessage());
        } finally {
            System.out.println("Este bloque siempre se ejecuta.");
        }
    }
}

Cosas a tener en cuenta en Java:¶

  • El bloque finally es opcional y se ejecuta siempre, independientemente de si se lanza o no una excepción.
  • Las excepciones verificadas deben ser declaradas o manejadas, mientras que las excepciones no verificadas (como ArithmeticException) no necesitan ser declaradas en la firma del método.

C++¶

C++ utiliza try, catch y throw para manejo de excepciones.

#include <iostream>
#include <stdexcept>
using namespace std;

int main() {
    int numerador = 10;
    int denominador = 0;

    try {
        if (denominador == 0) {
            throw runtime_error("Denominador no puede ser cero");
        }
        int resultado = numerador / denominador;
        cout << "Resultado: " << resultado << endl;
    } catch (runtime_error &e) {
        cout << "Error: " << e.what() << endl;
    }

    cout << "Este bloque siempre se ejecuta." << endl;

    return 0;
}

Cosas a tener en cuenta en C++:¶

  • Puedes lanzar cualquier tipo como una excepción, aunque es más común utilizar clases que heredan de std::exception.

Python¶

En Python, puedes usar try, except, finally y raise para manejo de excepciones.

try:
    numerador = 10
    denominador = 0
    resultado = numerador / denominador
    print(f"Resultado: {resultado}")
except ZeroDivisionError as e:
    print(f"Error: {e}")
finally:
    print("Este bloque siempre se ejecuta.")

Cosas a tener en cuenta en Python:¶

  • Python permite capturar múltiples tipos de excepciones y manejarlas de diferentes maneras.
  • La cláusula finally es opcional y se ejecutará independientemente de si se lanza una excepción o no.

Resumen¶

  • En Java y C++, generalmente capturas objetos que son subclases de una clase de Excepción.
  • En Python, debido a su tipado dinámico, cualquier objeto puede ser lanzado como una excepción, aunque es común utilizar subclases de la clase base Exception.
  • En todos estos lenguajes, el bloque que maneja la excepción (catch en Java y C++, except en Python) debe especificar qué tipo de excepción está preparado para manejar.

Ejercicios parte 1¶

  1. Ejercicio: Calculadora de Formas Geométricas

Descripción: Crear una clase FormaGeometrica que tenga atributos como alto y ancho. La clase debe tener los siguientes métodos:

  • area(): para calcular el área.
  • perimetro(): para calcular el perímetro.
  • Crear subclases como Rectangulo, Triangulo y Circulo que sobrecarguen los métodos area() y perimetro().

Requerimientos adicionales:

  • Use constructores para inicializar los atributos.
  • Sobrecargue los constructores para proporcionar diferentes formas de inicialización.
  • Utilice el manejo de errores para manejar casos como dimensiones negativas.

  1. Ejercicio: Sistema de Calificaciones de Estudiantes

Descripción: Crear una clase Estudiante con atributos como nombre, matricula y calificaciones (un arreglo de números). Implemente los siguientes métodos:

  • agregar_calificacion(): para agregar una calificación al estudiante.
  • promedio(): para calcular el promedio de calificaciones del estudiante.
  • estado(): para determinar si el estudiante aprobó o reprobó, basado en el promedio.

Requerimientos adicionales:

  • Use constructores para inicializar los atributos.
  • Sobrecargue el método agregar_calificacion() para que acepte una única calificación o una lista de calificaciones.
  • Maneje errores para casos como calificaciones no válidas o fuera de rango.

  1. Ejercicio: Calculadora Básica

Descripción Cree una clase Calculadora con métodos como suma, resta, multiplicacion y division. Ademas de los métodos matematicos agregue 2 más en base a su criterio.

Requerimientos adicionales:

  • Use constructores para inicializar cualquier estado inicial si es necesario.
  • Sobrecargue el método suma para que pueda sumar tres números además de dos.
  • Utilice el manejo de errores para situaciones como la división por cero.

  1. Ejercicio: Gestor de Tareas

Descripción: Cree una clase Tarea con atributos como nombre, descripcion y completada. Implemente los siguientes métodos:

  • completar(): para marcar la tarea como completada.
  • descripcion(): para obtener la descripción de la tarea.

Requerimientos adicionales:

  • Use constructores para inicializar los atributos.
  • Sobrecargue el constructor para crear una tarea solo con nombre y asumir que no está completada.
  • Utilice el manejo de errores para situaciones como nombres de tareas inválidos.

  1. Ejercicio: Sistema de Puntajes para Videojuegos

Descripción: Cree una clase Jugador con atributos como nombre, puntaje. Implemente los siguientes métodos:

  • aumentar_puntaje(): para aumentar el puntaje del jugador.
  • mostrar_puntaje(): para mostrar el puntaje actual.

Requerimientos adicionales:

  • Use constructores para inicializar los atributos.
  • Sobrecargue el método aumentar_puntaje para que pueda aceptar un valor adicional como bonificación.
  • Utilice el manejo de errores para situaciones como nombres de jugadores inválidos o puntajes negativos.


Ejercicios parte 2¶

Este ejercicio solo se puede desarrollar en un lenguaje de compilado (Java o C++), y no pueden usar listas.

El juego consiste en colocar un "tesoro" y una "trampa" en una matriz de tamaño NxN. El jugador debe adivinar las coordenadas para encontrar el tesoro, para esto crearemos una clase llamada Tablero que gestionará el juego.

Requerimientos

  • El constructor debe inicializar la matriz de NxN llena de ceros, N debe ser entregada por el usuario al iniciar el programa.
  • El constructor debe colocar el tesoro ('T') y la trampa ('X') en posiciones aleatorias dentro de la matriz.
  • Método mostrar_matriz(): muestra la matriz en la consola, pero ocultando el tesoro y la trampa (mostrando ceros).
  • Método adivinar_posicion(x, y): toma dos argumentos para la coordenada a adivinar.
  • Sobrecarga: Permitir adivinar usando una lista [x, y] además de los dos argumentos separados.
  • Si el jugador encuentra el tesoro o la trampa, el juego muestra un mensaje y termina.
  • Utilice manejo de errores para coordenadas inválidas.