C++ en el Ecosistema Moderno de Desarrollo: Una Ventaja Duradera

Introducción

En un mundo donde nuevos lenguajes de programación emergen con promesas de simplicidad y eficiencia, C++ se mantiene como un pilar de la ingeniería de software. Este veterano del código, con su nacimiento en los años 80, continúa siendo fundamental en el desarrollo de tecnologías avanzadas, demostrando que su legado trasciende las décadas.

¿Por qué C++?

La pregunta surge naturalmente: en la era de la agilidad y la rapidez, ¿qué hace que C++ siga siendo relevante? La respuesta yace en sus fortalezas innatas. C++ ofrece un nivel de eficiencia y control sobre el hardware que pocos lenguajes pueden igualar, permitiendo a los desarrolladores gestionar recursos con precisión quirúrgica. Además, su compatibilidad con C brinda acceso a un tesoro de bibliotecas y aplicaciones legadas, asegurando que proyectos nuevos puedan construirse sobre fundamentos sólidos y probados.

Comparación con Lenguajes Modernos

Frente a lenguajes contemporáneos como Python, que seduce con su sintaxis amigable, o JavaScript, omnipresente en el desarrollo web, C++ se distingue por su rendimiento superior. Mientras que lenguajes más nuevos facilitan la entrada a principiantes y ofrecen rapidez en el desarrollo, C++ se reserva para aquellos momentos donde la eficiencia es crítica. En áreas como el desarrollo de sistemas operativos, software de simulación y videojuegos, la capacidad de C++ para maximizar el rendimiento del hardware lo hace insustituible.

C++ Hoy: Aplicaciones y Comunidad

A pesar de su curva de aprendizaje más empinada, la demanda de programadores de C++ no ha disminuido. Las aplicaciones desarrolladas en C++ —desde sistemas operativos hasta motores de videojuegos de vanguardia— son testimonio de su potencial sin límites. La comunidad de C++ es vasta y activa, ofreciendo un soporte robusto a quienes se embarcan en el aprendizaje de este lenguaje. Foros, conferencias, y grupos de usuario en todo el mundo facilitan un entorno de colaboración y aprendizaje continuo.

Conclusión y Recursos

La permanencia de C++ en el panorama tecnológico no es casualidad. Su combinación única de potencia, eficiencia y flexibilidad lo mantienen como una herramienta indispensable en el arsenal de cualquier desarrollador serio. Para aquellos interesados en profundizar en C++, la documentación oficial es el mejor punto de partida. Además, comunidades como Stack Overflow y subforos dedicados en Reddit ofrecen espacios excelentes para resolver dudas y compartir conocimientos.

C++ no es solo un lenguaje de programación; es una herencia de la ingeniería del software, una que sigue evolucionando y adaptándose, demostrando que algunas tecnologías realmente están construidas para perdurar.


Este artículo ofrece una visión general de por qué C++ continúa siendo una elección relevante en el desarrollo de software, destacando sus fortalezas en comparación con otros lenguajes modernos y señalando recursos útiles para principiantes. Si necesitas más detalles o ajustes, estaré encantado de ayudarte.

¿Cómo se usan los punteros en C++?

Los punteros son una característica fundamental en C++, pero pueden resultar confusos para los principiantes. En este artículo, explicaremos las particularidades de los punteros en C++ de manera clara y concisa.

¿Qué es un puntero?

Un puntero no es más que una variable que almacena la dirección de memoria de otra variable. Esto significa que, en lugar de almacenar el valor de una variable directamente, un puntero almacena la dirección de memoria donde se encuentra esa variable.

Cómo se usan los punteros en C++

Para declarar un puntero en C++, se utiliza el símbolo de asterisco (*). Por ejemplo, la declaración «int *p» declara un puntero a un entero llamado «p». Una vez que se ha declarado un puntero, se puede asignar a él la dirección de memoria de una variable utilizando el operador de dirección «&». Por ejemplo, «p = &x» asigna a «p» la dirección de memoria de la variable «x».

Diagrama punteros

En el diagrama de ejemplo anterior hemos creado dos punteros que apuntan a variables de tipo entero. Estos punteros p1 y p2 solamente señalan a la dirección de memoria que contiene a las variables 1 y 2. En este caso los punteros contienen los valores de memoria 0x01 y 0x05.

Particularidades

Una de las particularidades de los punteros en C++ es que se pueden utilizar para acceder al valor de la variable a la que apuntan mediante el operador de indirección «*». Por ejemplo, «cout << *p» imprimirá el valor de la variable a la que apunta el puntero «p».

Un uso habitual de los punteros en C++ es para crear y manipular estructuras de datos dinámicas, como matrices y listas enlazadas. Esto se logra utilizando los operadores «new» y «delete» para asignar y liberar memoria dinámicamente.

Peligros

Es importante tener en cuenta que los punteros en C++ pueden ser peligrosos si se usan incorrectamente. Es fácil cometer errores como acceder a una dirección de memoria no válida o liberar la memoria dos veces. Para evitar estos errores, se recomienda utilizar técnicas de programación defensiva, como inicializar los punteros a null y comprobar que no son null antes de utilizarlos.

Conclusión

En resumen, los punteros son una característica importante en C++, que permiten acceder a la memoria de manera eficiente y crear estructuras de datos dinámicas. Sin embargo, es fundamental tener cuidado al utilizarlos para evitar errores peligrosos.

Depurar con GDB

Una de las tareas que cualquier progamador debe realizar a menudo es depurar el código. Vamos a aprender a depurar con GDB nuestros programas de C++, para ello utilizaremos un sencillo programa de Hola Mundo como ejemplo. Si no tienes uno preparado puedes utilizar el que se muestra en el tutorial de CMAKE.

Preparación

Edita o crea un archivo hello.cpp para que quede tal que así:

#include <iostream>

int main()
{
  int x = 10;
  x += 2;
  std::cout << "Hello word! x = " << x << std::endl;
  
  return 0;
}
  

Asegúrate de que funciona mediante make.

Ejecutando GDB

Una vez que estamos seguros de que el programa funciona perfectamente procedemos a depurarlo mediante gdb. Para ello escribe gdb hello en la consola, lo que produce la siguiente salida:

GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hello...
(No debugging symbols found in hello)
(gdb)

Como puedes ver, la salida del programa nos indica que no hay símbolos de depuración para poder proceder, así que vamos a comunicarlos al compilador durante el propio paso de compilación. Antes de nada sal de la sesión de compilación simplemente escribiendo q y pulsando Enter. Edita el archivo makefile para añadir la opción -g en la sección del compilador (sección hello.o):

CC = g++
all: hello
hello: hello.o
	${CC} -o hello hello.o
hello.o: hello.cpp
	${CC} -c -g hello.cpp
clean:
	rm hello.o hello

Ahora tenemos que establecer un punto de ruptura para detener la ejecución en ese punto. Para ello entramos al modo depuración, como ya hicimos antes, mediante:

gdb hello

Comandos comunes

Una vez dentro de la sesión de depuración, teclea b 5 para establecer un punto de ruptura en la línea 5.

(gdb) b 5
Breakpoint 1 at 0x11d5: file hello.cpp, line 5.

Con esto ya estaríamos listos para ejecutar de nuevo el programa. Para ello introducimos el comando r y pulsamos Enter. En este momento el programa se ejecutará con nuestro punto de ruptura correctamente establecido.

Starting program: /home/jflores/CLionProjects/cplusplus/hello

Vemos como el proceso indica que se ha detenido en la línea 5:

Breakpoint 1, main () at hello.cpp:5
5               int x = 10;

Para proceder paso a paso a partir de aquí, introducimos el comando n, equivalente a next step. Otro comando útil es s, que significa step into, para continuar dentro del paso actual (útil si estamos detenidos sobre una función, por ejemplo).

6               x += 2;

Si tenemos que conocer el contenido de una variable podemos ejecutar el comando p, que significa imprimir:

$1 = 10

Se puede continuar la ejecución del programa hasta el final, o bien hasta el próximo punto de ruptura, mediante el comando c (significa continuar).

(gdb) c
Continuing.
Hello word! x = 12
[Inferior 1 (process 423538) exited normally]

Puedes encontrar más información sobre GDB en la página oficial del proyecto (en inglés).

Usar CMAKE para compilar proyectos en C++

En este breve artículo vamos a mostrar cómo usar CMAKE para compilar programas de cualquier complejidad.

Para ello vamos a utilizar la herramienta g++ como compilador (en el resto de ejemplos vamos a tratar de usar siempre g++).

Comencemos por generar un archivo de código fuente en C++ como el ejemplo que se presenta a continuación:

#include <iostream>

int main()
{
  std::cout << "Hello World!" << std::endl;
  
  return 0;
}

Si necesitas una breve introducción a C++ puedes empezar por aquí.

Llama a este archivo de código fuente hello.cpp.

Ahora necesitarás un archivo CMAKE por lo que puedes crear uno escribiendo lo siguiente desde la consola:

touch Makefile

Con el siguiente contenido:

CC = g++
all: hello
hello: hello.o
      ${CC} -o hello hello.o
hello.o: hello.cpp
      ${CC} -c hello.cpp
clean: 
      rm hello.o hello

Es importante señalar que se deben usar tabulaciones en vez de espacios para indentar los comandos.

A pesar de que se trata de un simple programa Hello World! el ejemplo resulta útil para ver como se estructura un archivo makefile.

De forma simplificada, se puede decir que un archivo makefile define una serie de reglas, que se componen de prerrequisitos y comandos.

La primera regla, all, tiene como prerrequisito a hello. Esta regla carece de comando.

La segunda regla, hello, tiene como prerrequisito a hello.o y como comando:

${CC} -o hello hello.o

La tercera regla, hello.o, tiene como prerrequisito al archivo hello.cpp y un comando para compilar:

${CC} -c hello.cpp

La última regla, clean, tiene como comando una instrucción para eliminar tanto hello como hello.o forzando así una nueva compilación en la próxima ejecución.

Ahora por lo tanto podríamos compilar el programa sin problemas mediante el archivo makefile que hemos creado introduciendo el comando:

make

Lo cuál nos dejaría el binario compilado y enlazado, solamente tendríamos que ejecutarlo con:

./hello

Este ejemplo únicamente nos ha mostrado unos conceptos muy básicos sobre los archivos makefile y la utilidad make. Otros casos de uso más complejos son:

  • Uso de macros: Un makefile permite el uso de macros, que aparecen como variables. Éstas a su vez pueden ser usadas para dar más modularidad al makefile, como por ejemplo:
    • Macros para las librerías dinámicas usadas en el programa: LIBS = -lasd -lfgh
    • Macro para el compilador: COMPILER = GCC
    • Puedes usar como referencia esas macros en cualquier parte del makefile: ${COMPILER} o ${LIBS}
  • Cuando tecleas make en la terminal, la primera regla definida el el makefile será ejecutada. En nuestro ejemplo era all. Si lo hubiéramos cambiado por clean por ejemplo se habría ejecutado esa regla. Como norma general siempre es deseable ejecutar make con algún parámetro a continuación, por ejemplo: make clean.

¡Si tienes alguna pregunta no dudes en dejar tus comentarios!

Operaciones Aritméticas

Las operaciones aritméticas básicas en C++ son muy parecidas a las de otros lenguajes.

OperadorOperación
+Suma
Resta
*Multiplicación
/División
%Módulo

Una librería muy útil a la hora de realizar operaciones matemáticas es cmath. Una de sus funciones más útiles es pow().

Fíjate en su uso:

#include <cmath>

std::pow(base, exponente);

Como por ejemplo:

std::pow(2, 5);

cmath incluye también algunas constantes muy útiles a la hora de realizar operaciones matemáticas, como por ejemplo M_PI que es el equivalente al número PI.

M_PI = 3.14159265358979323846

Conversiones implícitas

Como ya sabes, C++ es un lenguaje compilado y como tal requiere conocer los tipos de variable que van a ser usados durante la ejecución.

Sin embargo se permiten ciertas conversiones implícitas, como por ejemplo un entero que se asigna a una variable de tipo float o un entero tratado como un char.

Echa un vistazo al siguiente ejemplo para comprender mejor este concepto:

#include<iostream>

int main()
{
    
    int a = 65;
    char charA = 65;
    char charB = 'B';
    float answer = 0;
    char charC = 67;
    int integer = 80;
    float floatNumber = 0.0;
    
    std::cout<<"a = "<<a<<"\n";
    std::cout<<"charA = "<<charA<<"\n";
    std::cout<<"charB = "<<charB<<"\n";
    
    // se puede asignar un integer a un float 
    floatNumber = integer;
    std::cout<<"integer = "<<integer<<"\n\n";
    std::cout<<"floatNumber = integer = "<<floatNumber<<"\n";
    
    // se puede asignar un char a un float
    floatNumber = charB;
    std::cout<<"floatNumber = charB = "<<floatNumber<<"\n";  
    
    answer = floatNumber/4;
    std::cout<<"respuesta = floatNumber/4 = "<<answer<<"\n"; 
    
    // Asignar un float a un char no funciona tan bien...
    charC = answer;
    std::cout<<"charC = respuesta = "<<charC<<"\n";
    
    // y si asignas un float a un integer el valor s truncado
    integer = answer;
    std::cout<<"integer = floatNumber = "<<integer<<"\n";   
    return 0;
}

Prefijo y postfijo

En C++, como en otros lenguajes, hay operadores que pueden funcionar como prefijos y postfijos.

Para incrementar una variable:

  • prefijo: ++a
  • postfijo: a++

Para reducir:

  • prefijo: –a
  • postfijo: a–

Existe una diferencia pequeña pero crucial entre ambas y es que el prefijo modifica el valor de la variable y después la devuelve y el postfijo crea una copia de la variable, la modifica y devuelve una copia anterior a la modificación.

Entrada y salida de datos II

Ya hemos visto anteriormente (lección anterior aquí) que para escribir en la consola usamos std::cout. De forma muy parecida tenemos std::cin para capturar la entrada desde la consola. Prueba un ejemplo:

#include <iostream>
#include <string>

int main()
{
  int pokNumber = 0;
  int pokPs = 0;
  std::string pokemon = "";
  
  std::cout<<"¿Cuántos pokemon tienes? ";
  std::cin >> pokNumber;
  
  std::cout << "No está mal, yo tengo " << pokNumber + 1 << std::endl;
  
  std::cout << "¿Cuántos PS tiene tu pokemon más fuerte? ";
  std::cin >> pokPs;
  
  std::cout<<"Está muy bien, mi Pikachu tiene "<< pokPs + 10 << " PS" << std::endl;

  std::cout<<"¿Cuál es tu pokemon favorito? ";
  std::cin >> pokemon;
  
	std::cout << "Qué casualidad! Mi pokemon favorito también es " << pokemon << std::endl;

	return 0;
}

¿Qué sucede al intentar capturar la entrada del usuario si se introducen dos palabras?

Te invito a que lo pruebes en tu propio programa y veas el resultado.

Podemos comprobar como std::cin no está pensado para capturar cadenas largas o espacios. Para ello tenemos a nuestra disposición una función llamada getline.

Su forma básica de operar es la siguiente: captura caracteres de std::cin y los almacena en la variable asignada. Se realiza esta operación hasta alcanzar una línea nueva o bien el carácter asignado, por defecto ‘\n’. Más info aquí.

#include<iostream>
#include<string>

int main()
{
    std::string apodo; 
    std::cout<<"¿Cuál es tu apodo?: ";
    std::getline(std::cin, apodo);
    std::cout<<"Hey, "<<apodo<<"!\n";
    return 0;
}

Stringstream

Esta librería resulta muy útil a la hora de manipular cadenas. Stringstream permite la manipulación de variables como si fueran streams de datos.

Para utilizarla debemos incluir la cabecera:

#include <sstream>

Fíjate en el siguiente ejemplo:

#include <string>
#include <iostream>
#include <sstream>

int main () {

  std::stringstream ss;

  ss << "pikachu" << ' ' << "bulbasaur";

  std::string pika, bulbasaur;
  ss >> pika >> bulbasaur;

  std::cout << "pokemon 1: " << foo << '\n';
  std::cout << "pokemon 2: " << bar << '\n';

  return 0;
}

Archivos de cabecera

También conocidos como header files. Como ya vimos en lecciones anteriores, podemos incluir todo tipo de librerías adicionales en C++, y podemos crear las nuestras e incluirlas también.

Generalmente los archivos de cabecera tienen la extensión .hpp, pero puedes utilizar la extensión que prefieras. Lo más importante acerca de los archivos de cabecera es que tienes que saber que son usados para definir información sobre como ejecutar una tarea, a diferencia del programa principal, que define lo que se va a ejecutar.

Veamos como un archivo de cabecera funciona con un sencillo programa ‘Hola, mundo!’

#include <iostream>

using namespace std;

int main(){
  cout << "Hola, estamos usando archivos de cabecera!";
  return 0
}

Si quisiéramos usar un archivo de cabecera para este propósito podríamos mover todo el código que no estuviera estrictamente relacionado con la tarea, de forma que podríamos generar un archivo main.hpp con el siguiente contenido:

#include <iostream>

using namespace std;

Y modificaríamos nuestro archivo principal (el que contiene la funcion main) para que quedara de la siguiente manera:

#include "main.hpp"

int main(){
  cout << "Hola, estamos usando archivos de cabecera!";
  return 0
}

Fíjate en las comillas dobles para incluir el archivo. De esta forma indicamos que se encuentra en el directorio actual.

Entrada y salida de datos I

En este artículo vamos a ver como funciona la entrada y salida de datos en C++, también conocida como I/O. En C++ los flujos de datos son llamados Streams y pueden tomar muchas formas: Desde texto que el usuario introduce en la terminal, a la lectura de un sensor de infrarrojos de un robot, pasando por archivos o bases de datos.

Escribir en la consola

Como ya hemos visto en el programa de ejemplo de la primera lección, escribimos en la consola usando ‘cout‘.

Las cadenas de texto tienen que ir entre comillas dobles ‘»‘, las comillas simples »’ denotan un caracter únicamente, es decir, un tipo char. Si utilizamos cout con una variable, el valor de la variable es impreso. Cout puede ser usado multiples veces, encadenando el resultado en una única salida:

int integer = 42;
std::cout<<"El valor del entero es "<<integer;

Es importante señalar que cout no añade un salto de línea de forma automática, para ello debes hacerlo tú poniendo un ‘\n’ al final de la cadena de texto.

Prueba a escribir un programa que imprima por consola el tamaño en bytes de los diferentes tipos de variable. Utiliza la función sizeof() para ello.

Más información acerca de sizeof() aquí.

Dicho programa se podría parecer al siguiente ejemplo:

#include <iostream>
using namespace std;
 
int main()
{
   cout << "Bytes de variable char : " << sizeof(char) << endl;
   cout << "Bytes de variable int : " << sizeof(int) << endl;
   cout << "Bytes de variable short int : " << sizeof(short int) << endl;
   cout << "Bytes de variable long int : " << sizeof(long int) << endl;
   cout << "Bytes de variable float : " << sizeof(float) << endl;
   cout << "Bytes de variable double : " << sizeof(double) << endl;
   cout << "Bytes de variable wchar_t : " << sizeof(wchar_t) << endl;
  
   return 0;
}

Fíjate sobre todo en el uso del namespace y de endl el cual finaliza la línea de forma muy parecida a como lo hace ‘\n’.

Formato de salida

Podemos dar formato a la salida usando secuencias de escape sin necesidad de usar ninguna librería adicional. Estas secuencias de escape son muy parecias a las de otros lenguajes, por ejemplo \n para el salto de línea o \t para insertar una tabulación,

Puedes incluir la librería iomanip para poder darle un ancho fijo a la salida con la función setw:

#include <iomanip>

std::cout<<"\n\nTexto sin formato\n";
std::cout<<"Roca"<<"Tierra"<<"Volador"<< "\n";
std::cout<<"\nTexto con setw(15)\n";
std::cout<<"Roca"<<std::setw(15)<<"Tierra"<<std::setw(15)<<"Volador"<< "\n";
std::cout<<"\nTexto con tabulaciones\n";
std::cout<<"Roca\t"<<"Tierra\t"<<"Volador"<< "\n";

Que produce:

Texto sin formato
RocaTierraVolador

Texto con setw(15)
Roca         Tierra        Volador


Texto con tabulaciones
Roca    Tierra    Volador

Entrada y salida de archivos

Las operaciones con archivos son algo que suele generar quebraderos de cabeza en los programadores noveles. En C++ es muy importante seguir determinadas pautas si no queremos encontrarnos con programas erróneos o archivos corruptos.

Sigue estos pasos cada vez que vayas a realizar algún tipo de operación con archivos:

 - Incluye la librería <fstream> 
 - Crea un stream (input, output, both)
      - ofstream miarchivo; (para escribir a un archivo)
      - ifstream miarchivo; (para leer un archivo)
      - fstream miarchivo; (para leer y escribir a un archivo)
 - Abre el archivo miarchivo.open("nombre");
 - Escribe o lee el archivo
 - Cierra el archivo miarchivo.close();

Te adjunto un programa de ejemplo que puedes usar como base para el trabajo con archivos:

// incluye la libreria iostream
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main () {
    string line;
    // crea un stream para escritura
    ofstream miArchivo1 ("entrada.txt", ios::app);
    if (miArchivo1.is_open())
    {
        miArchivo1 << "\nAñado una linea.\n";
        miArchivo1 << "Otra línea más.\n";
        miArchivo1.close();
    }
    else cout << "Imposible abrir el archivo para escritura";

    // crea un stream para lectura
    ifstream miArchivo2 ("entrada.txt");
    // crear una variable de tipo ifstream abre el archivo
    if (miArchivo2.is_open())
    {
        while ( getline (miArchivo2, line) )
        {
            cout << line << '\n';
        }
        miArchivo2.close();
    }
    else cout << "Imposible abrir el archivo para lectura";

    return 0;
}

Constantes

Hay un tipo de variable en C++ que podemos usar si queremos forzar que su valor no cambie a lo largo de la ejecución del programa, las constantes.

Hay dos formas de declarar constantes en C++:

  • Mediante la palabra clave const.
  • Utilizando el preprocesador mediante #define

Definición de constantes mediante const

El formato de esta declaración es el siguiente:

const type variable = value;

Por ejemplo:

const int pokemonCount = 898;	

Tratar de cambiar este tipo de variables nos mostrará un error. Compruébalo por tu cuenta:

#include <iostream>
using namespace std;

int main()
{
    const int pokemonCount = 898;
    cout<<"Número de pokemon = "<<pokemonCount<<"\n";
    pokemonCount = 1000;
    cout<<"Número de pokemon = "<<pokemonCount<<"\n";
    return 0;
}

Definición de constantes en la cabecera

El formato de la declaración es el siguiente:

#define identifier value

Por ejemplo:

#include <iostream>
using namespace std;

#define POKEMON_COUNT 898

int main()
{
    cout<<"Número de pokemon = "<<POKEMON_COUNT<<"\n";
    POKEMON_COUNT = 1000;
    cout<<"Número de pokemon = "<<POKEMON_COUNT<<"\n";
    return 0;
}

De nuevo nos encontraremos con un error al tratar de ejecutar este programa.

Constantes enumeradas

Hay un tipo de constante especialmente útil, las constantes enumeradas o enumerated constants. Se usan para declarar un tipo de variable y asignarle así un número finito de valores. Fíjate en este ejemplo:

enum pokemonTipo {
	PLANTA,
  	FUEGO,
    AGUA,
    LUCHA,
    BICHO,
    ELECTRICO
};

Los valores son automáticamente traducidos a enteros de tal forma que:

PLANTA = 0
FUEGO = 1
...

Fíjate en el programa de ejemplo:

#include <iostream>

using namespace std;

int main()
{
    enum POKEMON_TIPO {
    PLANTA,
  	FUEGO,
    AGUA,
    LUCHA,
    BICHO,
    ELECTRICO
      };
  
  	// definimos mejorTipo como una variable de tipo POKEMON_TIPOS
    POKEMON_TIPO mejorTipo;
    
    // hacemos una asignación
    mejorTipo = ELECTRICO;
    
    // y comprobamos como haríamos con cualquier otra variable
    if(mejorTipo == ELECTRICO)
    {
        cout<<"No me parece que ese sea el mejor tipo de pokemon\n";
    }
    return 0;
}