C++
By Tomás Hernández, Posted on July 22, 2024 (1y ago)
Se asume que antes de leer esto, tiene conocimiento básico de programación y algo de Inglés pues algunos términos no tienen una referencia directa a Español.
Compilador
C++ es un lenguaje que utiliza un compilador por detrás. Es decir, cuando nosotros necesitamos ejecutar un programa, antes de hacer esto, lo ensambla pasándolo a binario y luego ejecuta la versión .exe generada por el compilador. Errores que captura el compilador- Syntax Errors: El programador tuvo un error gramático. Ej.: Olvidarse del punto y coma.
- Type Errors: Errores de tipo, se dan cuando queremos hacer una operación a un tipo que no es el correcto. Ej.: Asignar un número a un char.
- Declaration Errors: Todo nombre utilizado debe ser declarado antes. Ej.: Usar una función con nombre que todavía no fue definida.
Convención de nombre de los archivos
El código a ejecutar debe estar en uno o más archivos diferentes. Los archivos del programa son conocidos como archivos fuente o source files.El sufijo que indica que un archivo es de C++ es .cpp, .cp, .cxx, .cc y .C
Instrucciones
Se finalizan con un ;Funciones
La estructura es tipoRetorno nombre(parametros){codigo} La palabra clave de retornar algo de un tipo específico es la instrucción return. Si el tipoRetorno es void, no hace falta utilizar un return.Función main
Siempre retorna un int. En la mayoría de los sistemas, el valor retornado en main es un indicador de estados.- Retorno = 0: El proceso fue exitoso.
- Retorno != 0: El proceso tuvo algún tipo de error.
Librerías
Para cargar librerías en nuestros source files utilizamos la palabra reservada include.Ej.:
#include <iostream>
<>: Angle Brackets
Las librerías siempre deben importarse al inicio del archivo.
Librería de Entrada y Salida de Datos (IO)
IO: Input / Output.La librería se llama iostream. Llamamos stream a una secuencia de caracteres que leemos o fue escrita desde un dispositivo IO. La convención de decirle stream viene de que los caracteres son generados y consumidos secuencialmente durante el tiempo.
- cin (se pronuncia see-in): Permite ingresar información.
- cout (se pronuncia see-out): Permite mostrar información.
- cerr (se pronuncia see-err): Lo utilizamos cuando hay un error estándar como una advertencia o error grave.
- clog (se pronuncia see-log): Lo utilizamos para mostrar información general sobre la ejecución del programa.
Importante: iostream es una abreviación de input y output stream, pero es posible que en algunos lugares se los mencione como istream (input stream) y ostream (output stream).
Veamos un ejemplo para sumar dos números e imprimirlos en consola
#include <iostream>
int main()
{
std::cout << "Enter Two Numbers:" << std::endl;
int v1 = 0, v2 = 0;
std::cin >> v1 >> v2;
std::cout << "La suma de los dos numeros es:" << v1 + v2 << std::endl;
return 0;
}
Header
Un Header es el nombre que se encuentra dentro de los angle bracketsOperators
- <<: Permite mostrar en pantalla. Toma dos operandos: El de la izquierda tiene que ser un objeto ostream , en el operando de la izquierda es donde se devuelve el resultado. El de la derecha es el valor a mostrar.
- >>: Permite dejar al usuario enviar información. Toma dos operandos: El de la izquierda es un istream, en el operando de la izquierda es donde se devuelve el resultado. El de la derecha es un objeto.
Importante: Si en una línea aparece más de un <<
o >>
entonces, se dividen en operaciones atómicas.
#include <iostream>
int main()
{
std::cin >> v1 >> v2
equivale a
(std::cin >> v1) >> v2
que a su vez equivale a
std::cin >> v1
std::cin >> v2
}
Manipulator
endl: Tiene el efecto de terminar la ejecución de la línea actual y limpiando el buffer asociado a ese dispositivo.Importante: Los programadores suelen agregar print mientras debuggean. Esas instrucciones siempre deben limpiar el stream. Caso contrario, si el programa crashea la salida puede haber quedado en el buffer llevándonos a inferencias incorrectas sobre por qué el programa crasheó.
Namespaces
Nos permiten evitar colisiones con los nombres que definimos y los usos de esos mismos nombres dentro de una biblioteca.namespace::funcion
Comentarios
//: Comentario de línea/* */: Comentario de bloque
//Esto es un comentario
o
/*
* Hola esto es un comentario
* pero con múltiples líneas.
*/
Importante: No es posible meter un comentario dentro de otro.
Estructuras de Control
Antes de explicar cualquier estructura de control es necesario entender para qué sirve el operador == en C++.El operador de == indica la comparación por valor, y devuelve un valor booleano true o false.
While
while(condition) {
statement
}
Ej de uso:
int found = 0;
int i = 0;
while(found == 0 && i!=10) {
if(found){
found = 1;
}
++i;
}
Importante: Recuerde verificar que el while es correcto y efectivamente termina.
Nota: ++i equivale a decir i = i+1;
For
for(variable; limite; ++variable) {
statement
}
Ej de uso con una suma de gauss ineficiente:
int sum = 0;
for(int i = 0; i<10; ++i) {
sum += i;
}
Leyendo una desconocida cantidad de entradas
Utilizamos un while con un std::cin. El ingreso de información finaliza cuando llegamos a un end-of-file o encontramos un input que no es del tipo que esperamos.En Windows, ejecutamos el end-of-file presionando CTRL + Z (en la terminal se mostrará ^Z) y luego, enter.
En sistemas UNIX, incluyendo Mac OS X es usualmente control-d. Ej.:
int value = 0, sum = 0;
while(std::cin >> value){
sum+=value;
}
std::cout << "La suma de los numeros ingresados es " << sum << std::endl;
Estructuras para Control de Flujo
If, Else If, Else
Es la estructura de control de flujo más común. El If/Else If llevan una guarda que indica cuando debe ejecutarse mientras que el Else se ejecuta cuando no sucede ni el if ni el else if.Importante: Si sabemos que los casos no pueden pasar a la vez, es mejor utilizar un if else que if's separados pues los if se evalúan siempre.
Good To Know: No es necesario que un if tenga un else.
Ej.:
int value = 0;
if(value == 0){
std::cout << "Hola, desde el if";
}else if(value == 1){
std::cout << "Hola, desde el else if";
}else{
std::cout << "Hola desde el else";
}
En este caso, entrará al if pues value = 0.
Si value fuese uno, entraría al else if.
Si value no es ni 0, ni 1, entonces entra al else.
Clases
Definimos nuestras estructuras de datos usando clases. Una clase define un tipo con una colección de operaciones relacionadas con ese tipo.Un enfoque principal del diseño de C++ es hacer posible definir tipos de clases que se comporten tan naturalmente como los de los tipos incorporados.
Las clases, las definimos en un header. El sufijo para los archivos de headers es .h pero algunos programadores prefieren .H, .hpp o .hxx. Si bien el compilador no le importa la forma de los nombres de los headers, los IDE si le dan importancia.
Importante : Para usar una clase no necesito saber cómo está implementada sino solamente qué operaciones tiene y cómo las tengo que usar (un TAD, guiño guiño).
Cada clase define un tipo, el nombre del tipo es el mismo que el nombre de la clase.
Tipos
Pasaje por copia o referencia en C++
Todos los tipos en C++ se pasan por copia (valor). Sin embargo, hay dos formas de mandarlo por referencia.Consideremos una funcion que recibe un parámetro n de cualquier tipo.
- *n: Dirección de memoria de n.
- &n: Valor de alias al objeto n original.
- n: Copia de n.
Veamos ahora sí unos ejemplos
Pasaje por defecto (la función obtiene copia/valor): int haceAlgo(int n) {
n = 5; //n solo cambia en el ámbito de la función. No cambió en main.
return 0;
}
int main(){
int n = 4;
haceAlgo(n);
return 0;
}
En este caso, n pasa por copia/valor.
Pasaje por referencia (la función obtiene directamente el objeto original)
int haceAlgo(int& n) { //Llega alias del objeto original.
n = 5; //Cambió n de main.
return 0;
}
int main(){
int n = 4;
haceAlgo(n); //Pasa referencia de n.
return 0;
}
Cuando en la función recibimos la referencia (&), tenemos solamente el objeto. No tenemos que preocuparnos por posibles valores null.
Pasaje por referencia usando punteros (la función obtiene la dirección donde está el objeto original) int haceAlgo(n* memo) { //Llega puntero de la memoria.
*n = 5; //Cambió n de main.
return 0;
}
int main(){
int n = 4;
haceAlgo(&n); //Envía referencia de la variable.
return 0;
}
Cuando en la función recibimos el puntero (*), tenemos más que el objeto original para modificar. Tenemos que preocuparnos por posibles valores null.
¿Qué tengo que usar? & vs *
- Sintaxis: Con * si se quiere obtener el valor hay que usar *a mientras que si usamos directamente la referencia, podemos hablar de a .
- Uso: Con * pasamos la dirección de memoria, útil para arrays, por otro lado, & es más seguro ya que no puede ser nulo.
- Seguridad: Con * tenemos que tener cuidado con el nulo o que apunten a una posición de memoria válida, por otro lado, & es más seguro ya que las referencias siempre refieren a un objeto válido.
Conclusión : A menos que necesite manejar mucho la memoria a bajo nivel, utilice &.
Importancia de los tipos
Los tipos determinan el significado de la información y las operaciones en nuestros programas.Tipos Primitivos en C++ (Built-in Types)
- El tipo bool representa los valores de verdad true o false.
- El char tiene el mismo tamaño que un solo byte de máquina.
- Los tipos wchar_t , char16_t y char32_t son usados para sets de caracteres extendidos. La intención de uso de char16_t y char32_t es usarlos para caracteres unicode.
- El tipo int será al menos tan grande como el tipo short .
- El tipo long será al menos tan grande como el tipo int.
- El tipo long long será al menos tan grande como long.
- El tipo float representa números de una palabra (32 bits).
- El tipo double representa números de dos palabras (64 bits).
- El tipo long doubles representa números de tres o cuatro palabras (96 o 128 bits).
Tipos Con Signo y Sin Signo (Signed & Unsigned Types)
Recordemos que al igual que en diferentes arquitecturas como por ejemplo Risc-V, la representación en sin signo solo representa números positivos (ocupando todos los bits disponibles) mientras que en la representación con signo representa tanto números positivos como negativos (tomando el primer bit como el signo).Números
Los tipos int, short, long y long long son signed. Para poder tratarlos como tipos sin signo, tenemos que agregar antes del tipo la palabra unsigned . int main() {
unsigned int edad = 0;
}
Caracteres
Existen tres tipos diferentes: char, signed char y unsigned char.- char: no es lo mismo que signed char. Sin embargo, solo existen las representaciones de signed char y unsigned char. La que se asigne al tipo char depende del compilador.
- unsigned char: toma valores del 0 al 255 inclusive si fuese de 8 bits.
- signed char: toma valores del -128 al 127 si fuese de 8 bits.
Optimización con los tipos en C++
- Usá tipos unsigned si sabés que los valores no pueden ser negativos.
- Usá int para cualquier tipo de entero. short suele ser muy chico, y en la práctica long tiene el mismo tamaño que int . Si int no es suficiente, usar long long .
- No utilizar char o bool en expresiones aritméticas. Solamente usarlas para almacenar caracteres o valores de verdad. Utilizar caracteres son muy problemáticos porque algunas computadoras los toman como signed y otras como unsigned .
- Para cálculos muy precisos, utilizar el tipo double en vez de float . Es más, algunas computadoras funcionan más rápido calculando en double que en float.
Conversion de Tipos
Suceden automáticamente cuando usamos un objeto de un tipo donde se está esperando un objeto de otro tipo. En C++ algunos tipos están relacionados con otros. Dos tipos están relacionados cuando hay una conversión entre ellos.Conversiones Implícitas
Suceden sin el conocimiento del programador. Es decir, la hace C++ a través del compilador.La mayoría de conversiones tratan de mantener a precisión si es posible.
Las conversiones implícitas ocurren cuando
- En las guardas, las expresiones nonbool se convierten a bool.
- En las inicializaciones, las variables ocurren con el tipo que se le colocó; En asignaciones, el valor que se le quiere asignar se convierte al tipo de la izquierda.
- int i -> i es de tipo int
- int i = 4.1523 -> 4
- En expresiones aritméticas y expresiones relacionales con operandos de tipos mixtos, los tipos se convierten a un tipo común.
Conversiones Aritméticas
Asignar un tipo aritmético a otro
- bool b = 42 -> b tiene el valor de true
- int i = b; -> i tiene el valor de 1 (true es 1 en int, false es 0 en int)
- i = 3.14 -> i tiene el valor de 3 (porque i es int)
- double pi = i; -> pi tiene el valor de 3.0
- unsigned char c = -1 -> c tiene el valor de 255
- signed char c2 = 256; -> el valor de c2 es undefined pues 256 está fuera de rango