Cplusplus Es PDF
Cplusplus Es PDF
#c++
Tabla de contenido
Acerca de 1
Observaciones 2
Versiones 2
Examples 2
Hola Mundo 2
Análisis 2
Comentarios 4
Función 6
Declaración de funciones 6
Llamada de función 7
Definición de la función 8
Sobrecarga de funciones 8
Parámetros predeterminados 8
Preprocesador 12
Introducción 14
Examples 14
Capítulo 3: Alcances 17
Examples 17
Alcance de bloque simple 17
Variables globales 17
Examples 19
std :: for_each 19
std :: next_permutation 19
std :: acumular 20
std :: encontrar 22
std :: cuenta 23
std :: count_if 24
std :: find_if 26
std :: min_element 27
Capítulo 5: Alineación 30
Introducción 30
Observaciones 30
Examples 30
Controlando la alineación 31
Capítulo 6: Archivo I / O 32
Introducción 32
Examples 32
Abriendo un archivo 32
Leyendo de un archivo 33
Escribiendo en un archivo 35
Modos de apertura 36
Cerrando un archivo 37
Flushing un arroyo 38
Copiando un archivo 41
¿Revisar el final del archivo dentro de una condición de bucle, mala práctica? 42
Observaciones 45
Examples 45
Ejemplo básico 45
Archivos fuente 45
El proceso de compilación 47
Examples 49
Capítulo 9: Arrays 51
Introducción 51
Examples 51
Una matriz de matriz sin formato de tamaño fijo (es decir, una matriz sin formato 2D). 54
Inicialización de matriz 57
Sintaxis 59
Examples 59
[[sin retorno]] 59
[[caer a través]] 60
[[nodiscard]] 62
[[maybe_unused]] 62
Observaciones 64
Examples 64
Muestra auto básica 64
Introducción 68
Sintaxis 68
Observaciones 68
Examples 68
En bucle 71
Mientras bucle 73
Bucle Do-while 75
Examples 79
Observaciones 81
Examples 81
Corrientes de cuerda 81
Copiando arroyos 83
Arrays 84
Imprimiendo colecciones con iostream 84
Impresión básica 84
Generación y transformación. 85
Arrays 85
Análisis de archivos 86
Transformación 87
Introducción 88
Observaciones 88
Examples 89
Declaración y uso 89
Examples 91
prvalue 91
xvalor 92
valor 92
glvalue 93
valor 93
Sintaxis 95
Observaciones 95
Examples 95
Especificadores de acceso 96
Herencia 97
Herencia virtual 99
Fondo 103
Amistad 105
Observaciones 123
Examples 123
Capítulo 19: Comparaciones lado a lado de ejemplos clásicos de C ++ resueltos a través de 130
Examples 130
Introducción 132
Observaciones 132
Examples 132
Examples 155
Introducción 163
Observaciones 163
Examples 164
No hay declaración de retorno para una función con un tipo de retorno no nulo 164
Eliminar un objeto derivado a través de un puntero a una clase base que no tiene un destru 171
Observaciones 178
Examples 178
Introducción 184
Observaciones 184
Examples 184
OpenMP: Secciones paralelas 184
Sintaxis 188
Observaciones 188
Examples 188
Introducción 198
Observaciones 198
Examples 198
Introducción 204
Observaciones 204
Examples 204
Introducción 205
Documentación 207
Construyendo con scons 208
Ninja 208
Introducción 208
Introducción 209
Introducción 209
Introducción 211
Examples 211
Observaciones 214
Examples 214
caso 214
cambiar 214
captura 215
defecto 215
Si 216
más 216
ir 216
regreso 217
lanzar 217
tratar 218
Introducción 224
Sintaxis 224
Observaciones 224
Examples 225
nulo * a T * 230
Examples 232
Sintaxis 237
Parámetros 237
Observaciones 237
Examples 237
Introducción 241
Examples 241
Observaciones 243
Examples 243
Introducción 247
Examples 247
Observaciones 255
Examples 255
Arrays 259
Examples 260
Introducción 265
Observaciones 265
Examples 266
C ++ 11 266
Plantillas 267
Concurrencia 267
General 268
Concurrencia 268
C ++ 14 269
C ++ 17 269
C ++ 03 270
C ++ 98 270
C ++ 20 271
Examples 272
Uso de este puntero para diferenciar entre datos de miembros y parámetros 275
Sintaxis 281
Parámetros 281
Observaciones 281
Examples 281
Observaciones 294
Examples 294
Examples 296
Examples 301
Variables 301
Funciones 301
Examples 304
clase 304
estructura 305
enumerar 305
Unión 307
Introducción 308
Sintaxis 308
Observaciones 308
Examples 309
Introducción 319
Sintaxis 319
Observaciones 319
Examples 319
Introducción 321
Observaciones 321
Examples 321
mudable 321
registro 322
estático 322
auto 323
externo 324
Examples 326
Introducción 329
Examples 329
Examples 332
Mejores prácticas: tirar por valor, atrapar por referencia constante 335
Observaciones 343
Examples 343
Introducción 346
Sintaxis 346
Parámetros 346
Examples 347
Cuantificadores 349
Anclas 351
Examples 352
Capítulo 54: Función de C ++ "llamada por valor" vs. "llamada por referencia" 354
Introducción 354
Examples 354
Llamar por valor 354
Observaciones 356
Examples 356
Observaciones 358
Examples 358
Sintaxis 360
Observaciones 360
Examples 360
Introducción 367
Sintaxis 367
Observaciones 367
Examples 368
Sintaxis 378
Observaciones 378
Examples 378
Encapsulacion 379
Introducción 387
Examples 387
Observaciones 392
Examples 392
Sintaxis 395
Observaciones 395
Examples 395
Apilar 395
Introducción 400
Observaciones 400
Examples 400
Conclusión 405
Conclusión 407
Conclusión 408
Observaciones 409
Examples 409
Observaciones 411
Examples 411
Introducción 421
Examples 421
Introducción 422
Observaciones 422
Examples 422
Observaciones 424
Examples 424
Examples 426
descanso 426
continuar 426
hacer 426
para 426
mientras 427
Examples 428
Rompiendolo 428
Examples 437
Sintaxis 443
Parámetros 443
Observaciones 444
Examples 444
Introducción 461
Examples 461
cierto 461
falso 461
nullptr 461
esta 462
Examples 465
Observaciones 469
Examples 469
Introducción 475
Observaciones 475
Examples 476
Examples 486
Introducción 487
Examples 487
Introducción 489
Observaciones 489
Examples 489
Introducción 500
Examples 500
Observaciones 502
Examples 504
Examples 508
Examples 516
Observaciones 517
std :: shared_mutex procesó lectura / escritura más de 2 veces más que std :: shared_timed 522
Examples 525
Estrategias para clases de bloqueo: std :: try_to_lock, std :: adopt_lock, std :: defer_lo 526
Introducción 529
Observaciones 529
Examples 529
Punteros a funciones 529
Observaciones 531
Examples 531
Examples 537
Ejemplo 540
Introducción 543
Examples 543
Sintaxis 546
Observaciones 546
Examples 546
Examples 550
Introducción 552
Sintaxis 552
Observaciones 552
Examples 554
asm 554
explícito 555
noexcept 555
tamaño de 557
Examples 563
const 563
decltype 563
firmado 564
no firmado 564
volátil 565
Examples 566
En t 566
bool 566
carbonizarse 566
char16_t 566
char32_t 567
flotador 567
doble 567
largo 567
corto 568
vacío 568
wchar_t 568
Examples 570
Observaciones 571
Examples 571
Subclases 572
Introducción 575
Examples 575
Examples 579
Introducción 584
Sintaxis 584
Observaciones 584
Examples 586
Examples 600
Archivo vec.hh: wrapper para std :: vector, utilizado para mostrar el registro cuando se l 602
Examples 614
Observaciones 618
Examples 618
Introducción 622
Observaciones 622
Examples 622
Macros 625
Macros x 631
Introducción 635
Examples 635
Captura 635
Introducción 637
Sintaxis 637
Observaciones 637
Examples 637
Sintaxis 643
Examples 643
Sintaxis 647
Observaciones 647
Examples 647
Uso de eliminaciones personalizadas para crear una envoltura para una interfaz C 654
Observaciones 661
Examples 661
Cierre 661
Examples 667
Uso de la recursión de la cola y la recursión del estilo de Fibonnaci para resolver la sec 667
Observaciones 669
Examples 669
Examples 671
Examples 673
Observaciones 676
Examples 676
Examples 685
dynamic_cast 685
Introducción 687
Examples 687
Semáforo C ++ 11 687
Examples 689
Examples 690
enable_if 690
void_t 692
is_detected 697
Introducción 700
Observaciones 700
Examples 700
Introducción 705
Observaciones 705
Examples 705
Sintaxis 721
Parámetros 721
Observaciones 721
Examples 721
static_assert 721
Parámetros 723
Observaciones 723
Examples 723
Examples 728
Observaciones 731
Examples 731
Observaciones 732
Examples 732
Ejemplo 732
Métodos 733
Capítulo 125: std :: function: Para envolver cualquier elemento que sea llamable 735
Examples 735
Introducción 742
Examples 742
Examples 745
Observaciones 749
Examples 749
Multi-Mapa 756
Creando std :: map con tipos definidos por el usuario como clave 756
Examples 758
Introducción 758
valor_o 761
Examples 762
Introducción 764
Observaciones 764
Examples 764
Introducción 771
Sintaxis 771
Observaciones 772
Examples 772
Terrible 772
Concatenación 774
en (n) 775
frente() 775
atrás() 776
Tokenizar 776
Observaciones 788
Examples 788
Introducción 791
Observaciones 791
Examples 791
Iteradores 799
Introducción 816
Examples 816
Ir a la limpieza 818
Introducción 820
Examples 820
Observaciones 834
Examples 834
1. Ejemplo de base sin devoluciones covariantes, muestra por qué son deseables 834
2. Versión de resultado covariante del ejemplo base, comprobación de tipos estática. 835
3. Resultado del puntero inteligente covariante (limpieza automatizada). 836
Observaciones 838
Examples 838
Constantes 838
Funciones 838
Sintaxis 843
Observaciones 843
Examples 843
Sintaxis 845
Observaciones 845
Examples 845
Examples 847
Introducción 849
Sintaxis 849
Examples 849
Observaciones 852
Examples 852
Introducción 854
Observaciones 854
Examples 854
Introducción 856
Sintaxis 856
Observaciones 856
Examples 856
Volver a declarar miembros de una clase base para evitar ocultar el nombre 856
Examples 858
Definiciones 860
Introducción 862
Examples 862
Definición de un miembro de datos estáticos en la definición de clase 862
Creditos 863
Acerca de
You can share this PDF with anyone you feel could benefit from it, downloaded the latest version
from: cplusplus
It is an unofficial and free C++ ebook created for educational purposes. All the content is extracted
from Stack Overflow Documentation, which is written by many hardworking individuals at Stack
Overflow. It is neither affiliated with Stack Overflow nor official C++.
The content is released under Creative Commons BY-SA, and the list of contributors to each
chapter are provided in the credits section at the end of this book. Images may be copyright of
their respective owners unless otherwise specified. All trademarks and registered trademarks are
the property of their respective company owners.
Use the content presented in this book at your own risk; it is not guaranteed to be correct nor
accurate, please send your feedback and corrections to [email protected]
https://riptutorial.com/es/home 1
Capítulo 1: Empezando con C ++
Observaciones
El programa 'Hello World' es un ejemplo común que se puede usar simplemente para verificar la
presencia del compilador y la biblioteca. Utiliza la biblioteca estándar de C ++, con std::cout de
<iostream> , y solo tiene que compilar un archivo, lo que minimiza la posibilidad de un error de
usuario durante la compilación.
Versiones
C ++ 17 TBD 2017-01-01
C ++ 20 TBD 2020-01-01
Examples
Hola Mundo
#include <iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
}
https://riptutorial.com/es/home 2
Análisis
Examinemos cada parte de este código en detalle:
Los flujos de entrada / salida (E / S) estándar proporcionan formas para que los
programas obtengan entrada y salgan a un sistema externo, generalmente el terminal.
• int main() { ... } define una nueva función llamada main . Por convención, la función main
se llama a la ejecución del programa. Sólo debe haber una función main en un programa de
C ++, y siempre debe devolver un número del tipo int .
Aquí, el int es lo que se llama el tipo de retorno de la función. El valor devuelto por la
función main es un código de salida.
Por convención, un sistema que ejecuta el programa interpreta como exitoso un código de
salida del programa 0 o EXIT_SUCCESS . Cualquier otro código de retorno está asociado con un
error.
Si no hay ninguna declaración de return , la función main (y, por lo tanto, el propio programa)
devuelve 0 de forma predeterminada. En este ejemplo, no necesitamos escribir
explícitamente la return 0; .
Todas las demás funciones, excepto aquellas que devuelven el tipo void , deben devolver
explícitamente un valor de acuerdo con su tipo de retorno, o de lo contrario no deben
devolverlo en absoluto.
• std::cout << "Hello World!" << std::endl; grabados "¡Hola mundo!" al flujo de salida
estándar:
Hay muchos espacios de nombres. Aquí, usamos :: para mostrar que queremos usar
cout desde el std nombres std . Para obtener más información, consulte Operador de
resolución de alcance - Documentación de Microsoft .
○ << es, en este contexto , el operador de inserción de flujo , llamado así porque
inserta un objeto en el objeto de flujo .
https://riptutorial.com/es/home 3
La biblioteca estándar define el operador << para realizar la inserción de datos para
ciertos tipos de datos en flujos de salida. stream << content inserta el content en el flujo
y devuelve lo mismo, pero el flujo actualizado. Esto permite encadenar inserciones de
secuencias: std::cout << "Foo" << " Bar"; Imprime "FooBar" en la consola.
El manipulador de flujo std::endl hace dos cosas: primero inserta el carácter de fin de
línea y luego vacía el búfer del flujo para forzar que el texto aparezca en la consola.
Esto asegura que los datos insertados en la transmisión realmente aparezcan en su
consola. (Los datos de transmisión generalmente se almacenan en un búfer y luego se
"descargan" en lotes, a menos que se fuerce un vaciado de inmediato).
Comentarios
Un comentario es una forma de colocar texto arbitrario dentro del código fuente sin que el
compilador de C ++ lo interprete con un significado funcional. Los comentarios se utilizan para dar
una idea del diseño o método de un programa.
int main()
{
// This is a single-line comment.
int a; // this also is a single-line comment
int i; // this is another single-line comment
}
https://riptutorial.com/es/home 4
C-Style / Block Comentarios
La secuencia /* se usa para declarar el inicio del bloque de comentarios y la secuencia */ se usa
para declarar el final del comentario. Todo el texto entre las secuencias de inicio y finalización se
interpreta como un comentario, incluso si el texto es de otro modo una sintaxis de C ++ válida.
Estos a veces se denominan comentarios de "estilo C", ya que esta sintaxis de comentario se
hereda del lenguaje predecesor de C ++, C:
int main()
{
/*
* This is a block comment.
*/
int a;
}
int main()
{
/* A block comment with the symbol /*
Note that the compiler is not affected by the second /*
however, once the end-block-comment symbol is reached,
the comment ends.
*/
int a;
}
El ejemplo anterior es un código válido de C ++ (y C). Sin embargo, tener /* adicional dentro de
un comentario de bloque puede dar como resultado una advertencia en algunos compiladores.
Los comentarios en bloque también pueden comenzar y terminar dentro de una sola línea. Por
ejemplo:
https://riptutorial.com/es/home 5
Sin embargo, los comentarios también tienen sus desventajas:
Del mismo modo, mantener las versiones antiguas de un fragmento de código en un comentario
con fines de referencia es desagradable, ya que desordena los archivos al tiempo que ofrece
poco valor en comparación con la exploración del historial del código a través de un sistema de
versiones.
Función
Una función es una unidad de código que representa una secuencia de sentencias.
Las funciones pueden aceptar argumentos o valores y devolver un solo valor (o no). Para usar
una función, una llamada de función se usa en valores de argumento y el uso de la llamada de
función se reemplaza con su valor de retorno.
Cada función tiene una firma de tipo : los tipos de sus argumentos y el tipo de su tipo de retorno.
Las funciones están inspiradas en los conceptos del procedimiento y la función matemática.
Las funciones a menudo están destinadas a realizar una tarea específica. y puede ser llamado
desde otras partes de un programa. Una función debe ser declarada y definida antes de ser
llamada en otro lugar en un programa.
• Nota: las definiciones de funciones populares pueden estar ocultas en otros archivos
incluidos (a menudo por conveniencia y reutilización en muchos archivos). Este es un uso
común de los archivos de encabezado.
https://riptutorial.com/es/home 6
Declaración de funciones
Una declaración de función declara la existencia de una función con su nombre y tipo de firma
para el compilador. La sintaxis es la siguiente:
int add2(int i); // The function is of the type (int) -> (int)
El nombre del argumento es opcional; La declaración para la función también podría ser la
siguiente:
Según la regla de una definición , una función con un cierto tipo de firma solo se puede declarar
o definir una vez en una base de código C ++ completa visible para el compilador de C ++. En
otras palabras, las funciones con una firma de tipo específica no se pueden redefinir, solo deben
definirse una vez. Por lo tanto, lo siguiente no es válido en C ++:
int add2(int i); // The compiler will note that add2 is a function (int) -> int
int add2(int j); // As add2 already has a definition of (int) -> int, the compiler
// will regard this as an error.
Si una función no devuelve nada, su tipo de retorno se escribe como void . Si no toma
parámetros, la lista de parámetros debe estar vacía.
void do_something(); // The function takes no parameters, and does not return anything.
// Note that it can still affect variables it has access to.
Llamada de función
Una función puede ser llamada después de que haya sido declarada. Por ejemplo, el siguiente
programa llama a add2 con el valor de 2 dentro de la función de main :
#include <iostream>
https://riptutorial.com/es/home 7
// add2's definition may be LINKED in from another object file.
int main()
{
std::cout << add2(2) << "\n"; // add2(2) will be evaluated at this point,
// and the result is printed.
return 0;
}
Definición de la función
Una definición de función * es similar a una declaración, excepto que también contiene el código
que se ejecuta cuando se llama a la función dentro de su cuerpo.
int add2(int i) // Data that is passed into (int i) will be referred to by the name i
{ // while in the function's curly brackets or "scope."
Sobrecarga de funciones
Puedes crear múltiples funciones con el mismo nombre pero diferentes parámetros.
int add2(int i, int j) // However, when add2() is called with two parameters, the
{ // code from the initial declaration will be overloaded,
int k = i + j + 2 ; // and the code in this declaration will be evaluated
return k; // instead.
}
Ambas funciones se llaman con el mismo nombre add2 , pero la función real que se llama
depende directamente de la cantidad y el tipo de los parámetros en la llamada. En la mayoría de
los casos, el compilador de C ++ puede calcular qué función llamar. En algunos casos, el tipo
debe ser explícitamente establecido.
Parámetros predeterminados
https://riptutorial.com/es/home 8
Los valores predeterminados para los parámetros de función solo se pueden especificar en las
declaraciones de función.
En este ejemplo, se puede llamar a multiply() con uno o dos parámetros. Si solo se proporciona
un parámetro, b tendrá un valor predeterminado de 7. Los argumentos predeterminados se deben
colocar en los últimos argumentos de la función. Por ejemplo:
Ciertas secuencias de caracteres especiales que se reducirán a las llamadas de función del
compilador, como ! , + , - , * , % y << y muchos más. Estos caracteres especiales se asocian
normalmente con el uso no programado o se usan para la estética (por ejemplo, el carácter + se
reconoce comúnmente como el símbolo de adición tanto en la programación en C ++ como en
matemáticas elementales).
C ++ maneja estas secuencias de caracteres con una sintaxis especial; pero, en esencia, cada
aparición de un operador se reduce a una llamada de función. Por ejemplo, la siguiente expresión
en C ++:
3+3
operator+(3, 3)
https://riptutorial.com/es/home 9
Visibilidad de prototipos y declaraciones de funciones.
En C ++, el código debe ser declarado o definido antes de su uso. Por ejemplo, lo siguiente
produce un error de tiempo de compilación:
int main()
{
foo(2); // error: foo is called, but has not yet been declared
}
Hay dos formas de resolver esto: poner la definición o la declaración de foo() antes de su uso en
main() . Aquí hay un ejemplo:
int main()
{
foo(2); // OK: foo is completely defined beforehand, so it can be called here.
}
Sin embargo, también es posible "declarar hacia adelante" la función poniendo solo una
declaración "prototipo" antes de su uso y luego definiendo el cuerpo de la función más adelante:
El prototipo debe especificar el tipo de retorno ( void ), el nombre de la función ( foo ) y los tipos
de variables de la lista de argumentos ( int ), pero los nombres de los argumentos NO son
necesarios .
Una forma común de integrar esto en la organización de los archivos de origen es hacer un
archivo de encabezado que contenga todas las declaraciones de prototipo:
// foo.h
void foo(int); // prototype declaration
https://riptutorial.com/es/home 10
#include "foo.h" // foo's prototype declaration is "hidden" in here
void foo(int x) { } // foo's body definition
y luego, una vez compilado, vincule el archivo de objeto correspondiente foo.o al archivo de
objeto compilado donde se usa en la fase de enlace, main.o :
• Nota: algunos códigos compilados están vinculados entre sí, pero no para crear un
programa final. Por lo general, este código "vinculado" también se puede empaquetar en un
formato que puede ser usado por otros programas. Este "paquete de código empaquetado y
utilizable" es lo que los programadores de C ++ denominan una biblioteca.
Muchos compiladores de C ++ también pueden combinar o deshacer ciertas partes del proceso
de compilación para facilitar o para un análisis adicional. Muchos programadores de C ++ usarán
diferentes herramientas, pero todas las herramientas generalmente seguirán este proceso
generalizado cuando estén involucrados en la producción de un programa.
https://riptutorial.com/es/home 11
El siguiente enlace extiende esta discusión y proporciona un bonito gráfico para ayudar. [1]:
http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html
Preprocesador
Edita el código fuente, recorta algunos bits, cambia otros y agrega otras cosas.
En los archivos de origen, podemos incluir directivas de preprocesador. Estas directivas le indican
al preprocesador que realice acciones específicas. Una directiva comienza con un # en una nueva
línea. Ejemplo:
#define ZERO 0
#include <something>
directiva. Lo que hace es toma todos something y lo inserta en el archivo de donde estaba la
directiva. El programa hola mundo comienza con la línea.
#include <iostream>
Esta línea agrega las funciones y objetos que le permiten usar la entrada y salida estándar.
El lenguaje C, que también utiliza el preprocesador, no tiene tantos archivos de encabezado como
el lenguaje C ++, pero en C ++ puede usar todos los archivos de encabezado C.
directiva. Esto le dice al preprocesador que a medida que avanza en el archivo, debe reemplazar
cada ocurrencia de something con something_else . También puede hacer cosas similares a las
funciones, pero eso probablemente cuenta como C ++ avanzado.
El something_else no es necesario, pero si se define something como nada, entonces fuera de las
directivas de preprocesador, todas las apariciones de something se desvanecerá.
En realidad, esto es útil, debido a las #if , #else y #ifdef directivas. El formato para estos sería el
siguiente:
#if something==true
//code
#else
//more code
#endif
https://riptutorial.com/es/home 12
#ifdef thing_that_you_want_to_know_if_is_defined
//code
#endif
Estas directivas insertan el código que está en el bit verdadero y borran los bits falsos. Esto se
puede usar para tener bits de código que solo se incluyen en ciertos sistemas operativos, sin
tener que volver a escribir todo el código.
https://riptutorial.com/es/home 13
Capítulo 2: Administracion de recursos
Introducción
Una de las cosas más difíciles de hacer en C y C ++ es la administración de recursos.
Afortunadamente, en C ++, tenemos muchas maneras de diseñar el manejo de recursos en
nuestros programas. Este artículo espera explicar algunos de los modismos y métodos utilizados
para administrar los recursos asignados.
Examples
Adquisición de recursos es la inicialización
#include <memory>
#include <iostream>
using namespace std;
int main() {
{
auto_ptr ap(new int(5)); // dynamic memory is the resource
cout << *ap << endl; // prints 5
} // auto_ptr is destroyed, its resource is automatically freed
}
C ++ 11
#include <memory>
#include <iostream>
using namespace std;
int main() {
auto_ptr ap1(new int(5));
cout << *ap1 << endl; // prints 5
auto_ptr ap2(ap1); // copy ap2 from ap1; ownership now transfers to ap2
cout << *ap2 << endl; // prints 5
cout << ap1 == nullptr << endl; // prints 1; ap1 has lost ownership of resource
}
https://riptutorial.com/es/home 14
entre otras cosas. La razón por la que hace esto es para evitar que se borre la memoria dos
veces: si hay dos auto_ptrs con propiedad del mismo recurso, ambos intentan liberarla cuando se
destruyen. La liberación de un recurso ya liberado generalmente puede causar problemas, por lo
que es importante prevenirlo. Sin embargo, std::shared_ptr tiene un método para evitar esto y no
transfiere la propiedad al copiar:
#include <memory>
#include <iostream>
using namespace std;
int main() {
shared_ptr sp2;
{
shared_ptr sp1(new int(5)); // give ownership to sp1
cout << *sp1 << endl; // prints 5
sp2 = sp1; // copy sp2 from sp1; both have ownership of resource
cout << *sp1 << endl; // prints 5
cout << *sp2 << endl; // prints 5
} // sp1 goes out of scope and is destroyed; sp2 has sole ownership of resource
cout << *sp2 << endl;
} // sp2 goes out of scope; nothing has ownership, so resource is freed
Pueden surgir problemas cuando varios subprocesos intentan acceder a un recurso. Para un
ejemplo simple, supongamos que tenemos un hilo que agrega uno a una variable. Para ello,
primero lee la variable, le agrega una y luego la almacena de nuevo. Supongamos que
inicializamos esta variable a 1, luego creamos dos instancias de este hilo. Una vez que ambos
subprocesos terminan, la intuición sugiere que esta variable debería tener un valor de 3. Sin
embargo, la siguiente tabla ilustra lo que podría salir mal:
Hilo 1 Hilo 2
Como puede ver, al final de la operación, 2 está en la variable, en lugar de 3. La razón es que el
Subproceso 2 leyó la variable antes de que el Subproceso 1 terminara de actualizarla. ¿La
solución? Mutexes.
https://riptutorial.com/es/home 15
exclusión mutua del recurso. Una vez que se termina de acceder al recurso, el hilo "libera" el
mutex. Mientras se adquiere el mutex, todas las llamadas para adquirir el mutex no volverán
hasta que se libere el mutex. Para entender mejor esto, piense en un mutex como una línea de
espera en el supermercado: los hilos se alinean al tratar de adquirir el mutex y luego esperar a
que los hilos por delante terminen, luego usar el recurso, luego salir del paso. línea liberando el
mutex. Habría pandemonium completo si todos trataran de acceder al recurso a la vez.
C ++ 11
#include <thread>
#include <mutex>
#include <iostream>
using namespace std;
int main() {
int var = 1;
mutex m;
https://riptutorial.com/es/home 16
Capítulo 3: Alcances
Examples
Alcance de bloque simple
{
int x = 100;
// ^
// Scope of `x` begins here
//
} // <- Scope of `x` ends here
Si un bloque anidado comienza dentro de un bloque externo, una nueva variable declarada con el
mismo nombre que está antes en la clase externa, oculta el primero.
{
int x = 100;
{
int x = 200;
Variables globales
Para declarar una instancia única de una variable a la que se puede acceder en diferentes
archivos de origen, es posible hacerlo en el ámbito global con la palabra clave extern . Esta
palabra clave le dice al compilador que en alguna parte del código hay una definición para esta
variable, por lo que puede usarse en todas partes y toda la escritura / lectura se realizará en un
lugar de la memoria.
// File my_globals.h:
#ifndef __MY_GLOBALS_H__
#define __MY_GLOBALS_H__
#endif
https://riptutorial.com/es/home 17
// File foo1.cpp:
#include "my_globals.h"
// File main.cpp:
#include "my_globals.h"
#include <iostream>
int main()
{
std::cout << "The radius is: " << circle_radius << "\n";'
return 0;
}
Salida:
https://riptutorial.com/es/home 18
Capítulo 4: Algoritmos de la biblioteca
estándar
Examples
std :: for_each
Efectos:
Parámetros:
Valor de retorno:
Complejidad:
Ejemplo:
c ++ 11
std::vector<int> v { 1, 2, 4, 8, 16 };
std::for_each(v.begin(), v.end(), [](int elem) { std::cout << elem << " "; });
Aplica la función dada para cada elemento del vector v imprimiendo este elemento a la stdout .
std :: next_permutation
Efectos:
https://riptutorial.com/es/home 19
Tamice la secuencia de datos del rango [primero, último) en la siguiente permutación
lexicográficamente más alta. Si se proporciona cmpFun , la regla de permutación se personaliza.
Parámetros:
first - el comienzo del rango a permutar, inclusive
last - el final del rango a ser permutado, exclusivo
Valor de retorno:
Devuelve true si existe tal permutación.
De lo contrario, el rango se cambia a la permutación lexicográficamente más pequeña y devuelve
false.
Complejidad:
O (n), n es la distancia del first al last .
Ejemplo :
123
132
213
231
312
321
std :: acumular
Efectos:
std :: Se acumula realiza la operación de plegado usando la función f en el rango [first, last)
comenzando con init como valor acumulador.
https://riptutorial.com/es/home 20
Efectivamente es equivalente a:
T acc = init;
for (auto it = first; first != last; ++it)
acc = f(acc, *it);
return acc;
En la versión (1) el operator+ se usa en lugar de f , por lo que acumular sobre el contenedor es
equivalente a la suma de los elementos del contenedor.
Parámetros:
Valor de retorno:
Complejidad:
Ejemplo:
std::vector<int> v { 2, 3, 4 };
auto sum = std::accumulate(v.begin(), v.end(), 1);
std::cout << sum << std::endl;
Salida:
10
c ++ 11
class Converter {
public:
int operator()(int a, int d) const { return a * 10 + d; }
};
y después
c ++ 11
https://riptutorial.com/es/home 21
const std::vector<int> ds = {1, 2, 3};
int n = std::accumulate(ds.begin(), ds.end(),
0,
[](int a, int d) { return a * 10 + d; });
std::cout << n << std::endl;
Salida:
123
std :: encontrar
Efectos
Parámetros
first => iterador que apunta al comienzo del rango last => iterador que apunta al final del rango
val => el valor a encontrar dentro del rango
Regreso
Un iterador que apunta al primer elemento dentro del rango que es igual (==) a val, el iterador
apunta a durar si no se encuentra val.
Ejemplo
#include <vector>
#include <algorithm>
#include <iostream>
//create a vector
vector<int> intVec {4, 6, 8, 9, 10, 30, 55,100, 45, 2, 4, 7, 9, 43, 48};
//define iterators
vector<int>::iterator itr_9;
vector<int>::iterator itr_43;
vector<int>::iterator itr_50;
//calling find
itr_9 = find(intVec.begin(), intVec.end(), 9); //occurs twice
itr_43 = find(intVec.begin(), intVec.end(), 43); //occurs once
https://riptutorial.com/es/home 22
cout << "first occurence of: " << *itr_9 << endl;
cout << "only occurence of: " << *itr_43 << Lendl;
/*
let's prove that itr_9 is pointing to the first occurence
of 9 by looking at the element after 9, which should be 10
not 43
*/
cout << "element after first 9: " << *(itr_9 + 1) << ends;
/*
to avoid dereferencing intVec.end(), lets look at the
element right before the end
*/
cout << "last element: " << *(itr_50 - 1) << endl;
return 0;
}
Salida
std :: cuenta
Efectos
Parámetros
Regreso
Ejemplo
#include <vector>
#include <algorithm>
#include <iostream>
https://riptutorial.com/es/home 23
int main(int argc, const char * argv[]) {
//create vector
vector<int> intVec{4,6,8,9,10,30,55,100,45,2,4,7,9,43,48};
//print result
cout << "There are " << count_9 << " 9s"<< endl;
cout << "There is " << count_55 << " 55"<< endl;
cout << "There is " << count_101 << " 101"<< ends;
//count its occurences in the vector starting from the first one
size_t count_4 = count(itr_4, intVec.end(), *itr_4); // should be 2
cout << "There are " << count_4 << " " << *itr_4 << endl;
return 0;
}
Salida
There are 2 9s
There is 1 55
There is 0 101
There are 2 4
std :: count_if
Efectos
Cuenta el número de elementos en un rango para el que una función de predicado especificada
es verdadera
Parámetros
first => iterador que apunta al principio del rango last => iterador que apunta al final del rango
red => función de predicado (devuelve verdadero o falso)
Regreso
El número de elementos dentro del rango especificado para el cual la función de predicado
devolvió verdadero.
https://riptutorial.com/es/home 24
Ejemplo
#include <iostream>
#include <vector>
#include <algorithm>
/*
Define a few functions to use as predicates
*/
//functor that returns true if number is greater than the value of the constructor parameter
provided
class Greater {
int _than;
public:
Greater(int th): _than(th){}
bool operator()(int i){
return i > _than;
}
};
//create a vector
vector<int> myvec = {1,5,8,0,7,6,4,5,2,1,5,0,6,9,7};
//using function pointer to count odd number in the first half of the vector
size_t oddCount = count_if(myvec.begin(), myvec.end()- myvec.size()/2, isOdd);
return 0;
}
Salida
vector size: 15
even numbers: 7 found
odd numbers: 4 found
numbers > 5: 6 found
https://riptutorial.com/es/home 25
std :: find_if
Efectos
Encuentra el primer elemento en un rango para el cual la función de predicado pred devuelve true.
Parámetros
first => iterador que apunta al principio del rango last => iterador que apunta al final del rango
pred => función de predicado (devuelve verdadero o falso)
Regreso
Un iterador que apunta al primer elemento dentro del rango para el que predice la función predate
devuelve true para. El iterador apunta a durar si no se encuentra val
Ejemplo
#include <iostream>
#include <vector>
#include <algorithm>
/*
define some functions to use as predicates
*/
public:
Greater(int th):_than(th){
}
bool operator()(int data) const
{
return data > _than;
}
};
int main()
{
https://riptutorial.com/es/home 26
//with a lambda function
vector<int>::iterator gt10 = find_if(myvec.begin(), myvec.end(), [](int x){return x>10;});
// >= C++11
//with functor
vector<int>::iterator gt5 = find_if(myvec.begin(), myvec.end(), Greater(5));
//not Found
vector<int>::iterator nf = find_if(myvec.begin(), myvec.end(), Greater(1000)); // nf points
to myvec.end()
cout << "First item > 10: " << *gt10 << endl;
cout << "First Item n * 10: " << *pow10 << endl;
cout << "First Item > 5: " << *gt5 << endl;
return 0;
}
Salida
std :: min_element
Efectos
Parámetros
https://riptutorial.com/es/home 27
toma dos argumentos y devuelve verdadero o falso, lo que indica si el argumento es menor que el
argumento 2. Esta función no debe modificar las entradas
Regreso
Complejidad
Ejemplo
#include <iostream>
#include <algorithm>
#include <vector>
#include <utility> //to use make_pair
//Using pairLessThanFunction
auto minPairFunction = min_element(pairVector.begin(), pairVector.end(),
pairLessThanFunction);
return 0;
}
Salida
https://riptutorial.com/es/home 28
Usando std :: nth_element para encontrar la mediana (u otros cuantiles)
Por el bien de este ejemplo, definamos la mediana de una secuencia de longitud n como el
elemento que estaría en la posición ⌈n / 2⌉. Por ejemplo, la mediana de una secuencia de longitud 5 es
el tercer elemento más pequeño, y también lo es la mediana de una secuencia de longitud 6.
Para usar esta función para encontrar la mediana, podemos usar lo siguiente. Digamos que
empezamos con
std::vector<int>::iterator b = v.begin();
std::vector<int>::iterator e = v.end();
std::vector<int>::iterator med = b;
std::advance(med, v.size() / 2);
std::advance(nth, pos);
https://riptutorial.com/es/home 29
Capítulo 5: Alineación
Introducción
Todos los tipos en C ++ tienen una alineación. Esta es una restricción en la dirección de memoria
dentro de la cual se pueden crear objetos de ese tipo. Una dirección de memoria es válida para la
creación de un objeto si la división de esa dirección por la alineación del objeto es un número
entero.
Las alineaciones de tipo son siempre una potencia de dos (incluido 1).
Observaciones
La norma garantiza lo siguiente:
• El requisito de alineación de un tipo es un divisor de su tamaño. Por ejemplo, una clase con
un tamaño de 16 bytes podría tener una alineación de 1, 2, 4, 8 o 16, pero no 32. (Si los
miembros de una clase solo tienen un tamaño de 14 bytes, pero la clase debe tener un
requisito de alineación de 8, el compilador insertará 2 bytes de relleno para hacer que el
tamaño de la clase sea igual a 16.)
• Las versiones firmadas y sin firmar de un tipo entero tienen el mismo requisito de alineación.
• Un puntero para void tiene el mismo requisito de alineación que un puntero para char .
• Las versiones calificadas para cv y no calificadas para cv de un tipo tienen el mismo
requisito de alineación.
Tenga en cuenta que si bien existe alineación en C ++ 03, no fue hasta C ++ 11 que se hizo
posible consultar la alineación (usando alignof ) y la alineación de control (utilizando alignas ).
Examples
Consultar la alineación de un tipo.
c ++ 11
El requisito de alineación de un tipo se puede consultar utilizando la palabra clave alignof como
un operador alignof . El resultado es una expresión constante de tipo std::size_t , es decir, se
puede evaluar en tiempo de compilación.
#include <iostream>
int main() {
std::cout << "The alignment requirement of int is: " << alignof(int) << '\n';
}
Salida posible
https://riptutorial.com/es/home 30
El requisito de alineación de int es: 4
Si se aplica a una matriz, produce el requisito de alineación del tipo de elemento. Si se aplica a un
tipo de referencia, produce el requisito de alineación del tipo referenciado. (Las referencias en sí
mismas no tienen alineación, ya que no son objetos).
Controlando la alineación
C ++ 11
La palabra clave alignas se puede usar para forzar a una variable, miembro de datos de clase,
declaración o definición de una clase, o declaración o definición de una enumeración, para tener
una alineación particular, si se admite. Se presenta en dos formas:
En este ejemplo, se garantiza que el búfer buf se alineará adecuadamente para contener un
objeto int , aunque su tipo de elemento sea un unsigned char , que puede tener un requisito de
alineación más débil.
alignas no se puede usar para dar a un tipo una alineación más pequeña que la que tendría el
tipo sin esta declaración:
alignas , cuando se le da una expresión constante entera, se le debe dar una alineación válida.
Las alineaciones válidas son siempre potencias de dos y deben ser mayores que cero. Los
compiladores deben admitir todas las alineaciones válidas hasta la alineación del tipo
std::max_align_t . Pueden apoyar las alineaciones más grandes que esto, pero el soporte para la
asignación de memoria para tales objetos es limitada. El límite superior de las alineaciones
depende de la implementación.
C ++ 17 cuenta con soporte directo en operator new para asignar memoria para tipos sobre
alineados.
https://riptutorial.com/es/home 31
Capítulo 6: Archivo I / O
Introducción
El archivo de C ++ I / O se realiza a través de secuencias . Las abstracciones clave son:
Las secuencias utilizan std::locale , por ejemplo, para detalles del formato y para la traducción
entre las codificaciones externas y la codificación interna.
Examples
Abriendo un archivo
std::fstream iofs("foo.txt"); // fstream: Opens file "foo.txt" for reading and writing.
std::ifstream ifs;
ifs.open("bar.txt"); // ifstream: Opens file "bar.txt" for reading only.
std::ofstream ofs;
ofs.open("bar.txt"); // ofstream: Opens file "bar.txt" for writing only.
std::fstream iofs;
iofs.open("bar.txt"); // fstream: Opens file "bar.txt" for reading and writing.
Usted debe comprobar siempre si un archivo ha sido abierto con éxito (incluso cuando se
escribe). Las fallas pueden incluir: el archivo no existe, el archivo no tiene los derechos de acceso
https://riptutorial.com/es/home 32
correctos, el archivo ya está en uso, se produjeron errores en el disco, la unidad se desconectó ...
La verificación se puede hacer de la siguiente manera:
Cuando la ruta del archivo contenga barras diagonales inversas (por ejemplo, en el sistema
Windows), debe eliminarlas correctamente:
C ++ 11
C ++ 11
Si desea abrir un archivo con caracteres no ASCII en la ruta en Windows actualmente, puede
usar el argumento de ruta de caracteres anchos no estándar :
Leyendo de un archivo
Si sabe cómo se formatean los datos, puede usar el operador de extracción de flujo ( >> ).
Supongamos que tiene un archivo llamado foo.txt que contiene los siguientes datos:
Luego puede usar el siguiente código para leer los datos del archivo:
https://riptutorial.com/es/home 33
// Define variables.
std::ifstream is("foo.txt");
std::string firstname, lastname;
int age, bmonth, bday, byear;
// Extract firstname, lastname, age, bday month, bday day, and bday year in that order.
// Note: '>>' returns false if it reached EOF (end of file) or if the input data doesn't
// correspond to the type of the input variable (for example, the string "foo" can't be
// extracted into an 'int' variable).
while (is >> firstname >> lastname >> age >> bmonth >> bday >> byear)
// Process the data that has been read.
El operador de extracción de flujo >> extrae cada carácter y se detiene si encuentra un carácter
que no se puede almacenar o si es un carácter especial:
• Para los tipos de cadena, el operador se detiene en un espacio en blanco ( ) o en una nueva
línea ( \n ).
• Para los números, el operador se detiene en un carácter no numérico.
Esto significa que la siguiente versión del archivo foo.txt también se leerá con éxito en el código
anterior:
John
Doe 25
4 6 1987
Jane
Doe
15 5
24
1976
El operador de extracción de flujo >> siempre devuelve el flujo dado a él. Por lo tanto, se pueden
encadenar varios operadores para leer datos consecutivamente. Sin embargo, una corriente
también se puede utilizar como una expresión booleana (como se muestra en el while bucle en el
código anterior). Esto se debe a que las clases de flujo tienen un operador de conversión para el
tipo bool . Este operador bool() devolverá true siempre que la secuencia no tenga errores. Si un
flujo entra en un estado de error (por ejemplo, porque no se pueden extraer más datos), el
operador bool() devolverá el valor false . Por lo tanto, el while de bucle en el código anterior se
saldrá después del archivo de entrada se ha leído hasta el final.
Si desea leer un archivo completo como una cadena, puede usar el siguiente código:
// Opens 'foo.txt'.
std::ifstream is("foo.txt");
std::string whole_file;
https://riptutorial.com/es/home 34
// Sets position to the start of the file.
is.seekg(0, std::ios::beg);
Este código reserva espacio para la string con el fin de reducir las asignaciones de memoria
innecesarias.
Si desea leer un archivo línea por línea, puede usar la función getline() :
std::ifstream is("foo.txt");
Si desea leer un número fijo de caracteres, puede usar la función miembro de la secuencia read()
:
std::ifstream is("foo.txt");
char str[4];
if (is.fail())
// Failed to read!
Escribiendo en un archivo
Hay varias formas de escribir en un archivo. La forma más sencilla es utilizar una secuencia de
archivo de salida ( ofstream ) junto con el operador de inserción de secuencia ( << ):
std::ofstream os("foo.txt");
if(os.is_open()){
os << "Hello World!";
}
En lugar de << , también puede usar la función miembro write() la secuencia del archivo de
salida:
std::ofstream os("foo.txt");
https://riptutorial.com/es/home 35
if(os.is_open()){
char data[] = "Foo";
os << "Hello Badbit!"; // This operation might fail for any reason.
if (os.bad())
// Failed to write!
Modos de apertura
Al crear una secuencia de archivos, puede especificar un modo de apertura. Un modo de apertura
es básicamente una configuración para controlar cómo la secuencia abre el archivo.
std::ifstream is;
is.open("foo.txt", std::ios::in | std::ios::binary);
Se debe tener en cuenta que debe configurar ios::in o ios::out si desea establecer otros
indicadores, ya que no están establecidos de forma implícita por los miembros de iostream
aunque tengan un valor predeterminado correcto.
• ifstream - in
• ofstream - out
• fstream - in y out
Los modos de apertura de archivos que puede especificar por diseño son:
https://riptutorial.com/es/home 36
Modo Sentido por Descripción
Nota: la configuración del modo binary permite que los datos se lean / escriban exactamente
como están; no configurarlo permite la traducción del carácter '\n' nueva línea '\n' a / desde una
secuencia de final de línea específica de la plataforma.
Cerrando un archivo
Cerrar un archivo explícitamente rara vez es necesario en C ++, ya que una secuencia de
archivos cerrará automáticamente su archivo asociado en su destructor. Sin embargo, debe
intentar limitar la vida útil de un objeto de flujo de archivos, de modo que no mantenga abierto el
manejador de archivos más de lo necesario. Por ejemplo, esto se puede hacer poniendo todas las
operaciones de archivo en un ámbito propio ( {} ):
// Write data.
output << prepared_data;
} // The ofstream will go out of scope here.
// Its destructor will take care of closing the file properly.
Llamar a close() explícitamente solo es necesario si desea reutilizar el mismo objeto fstream más
tarde, pero no desea mantener el archivo abierto entre:
// Preparing data might take a long time. Therefore, we don't open the output file stream
// before we actually can write some data to it.
std::string const more_prepared_data = prepare_complex_data();
// Open the file "foo.txt" for the second time once we are ready for writing.
output.open("foo.txt");
https://riptutorial.com/es/home 37
// Write the data to the file "foo.txt".
output << more_prepared_data;
Flushing un arroyo
std::ofstream os("foo.txt");
os << "Hello World!" << std::flush;
Hay un manipulador de flujo std::endl que combina la escritura de una nueva línea con la
descarga de flujo:
std::ifstream f("file.txt");
if (f)
{
std::stringstream buffer;
buffer << f.rdbuf();
f.close();
El método rdbuf() devuelve un puntero a un streambuf que puede streambuf en el buffer través de
la función miembro stringstream::operator<< .
https://riptutorial.com/es/home 38
std::ifstream f("file.txt");
if (f)
{
std::string str((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
// Operations on `str`...
}
Esto es bueno porque requiere poco código (y permite leer un archivo directamente en cualquier
contenedor STL, no solo cadenas) pero puede ser lento para archivos grandes.
NOTA : los paréntesis adicionales alrededor del primer argumento del constructor de cadenas son
esenciales para evitar el problema de análisis más desconcertante .
std::ifstream f("file.txt");
if (f)
{
f.seekg(0, std::ios::end);
const auto size = f.tellg();
// Operations on `str`...
}
En el siguiente ejemplo, usamos std::string y operator>> para leer los elementos del archivo.
std::ifstream file("file3.txt");
std::vector<std::string> v;
std::string s;
while(file >> s) // keep reading until we run out
{
v.push_back(s);
}
En el ejemplo anterior, simplemente estamos iterando a través del archivo leyendo un "elemento"
a la vez utilizando el operator>> . Este mismo efecto se puede lograr utilizando el
std::istream_iterator que es un iterador de entrada que lee un "elemento" a la vez desde el flujo.
Además, la mayoría de los contenedores se pueden construir utilizando dos iteradores, por lo que
https://riptutorial.com/es/home 39
podemos simplificar el código anterior para:
std::ifstream file("file3.txt");
std::vector<std::string> v(std::istream_iterator<std::string>{file},
std::istream_iterator<std::string>{});
Podemos extender esto para leer cualquier tipo de objeto que nos guste simplemente
especificando el objeto que queremos leer como el parámetro de plantilla para
std::istream_iterator . Por lo tanto, podemos simplemente extender lo anterior para leer líneas
(en lugar de palabras) como esta:
std::ifstream file("file3.txt");
C ++ 11
struct info_type
{
std::string name;
int age;
float height;
https://riptutorial.com/es/home 40
};
void func4()
{
auto file = std::ifstream("file4.txt");
std::vector<info_type> v;
for(info_type info; file >> info;) // keep reading until we run out
{
// we only get here if the read succeeded
v.push_back(info);
}
file4.txt
Wogger Wabbit
2
6.2
Bilbo Baggins
111
81.3
Mary Poppins
29
154.8
Salida:
Copiando un archivo
C ++ 17
https://riptutorial.com/es/home 41
Con C ++ 17, la forma estándar de copiar un archivo es incluir el encabezado <filesystem> y usar
copy_file :
std::fileystem::copy_file("source_filename", "dest_filename");
¿Revisar el final del archivo dentro de una condición de bucle, mala práctica?
eof devuelve true solo después de leer el final del archivo. NO indica que la próxima lectura será
el final de la transmisión.
while (!f.eof())
{
// Everything is OK
f >> buffer;
/* Use `buffer` */
}
while (!f.eof())
{
f >> buffer >> std::ws;
if (f.fail())
break;
/* Use `buffer` */
}
pero
Otras referencias:
https://riptutorial.com/es/home 42
Si necesita escribir un archivo con una configuración regional diferente a la predeterminada,
puede usar std::locale y std::basic_ios::imbue() para hacer eso para una secuencia de archivos
específica:
Guía de uso:
Razones para las restricciones: Imbuir una secuencia de archivos con una configuración
regional tiene un comportamiento indefinido si la configuración regional actual no es
independiente del estado o no apunta al principio del archivo.
Las transmisiones UTF-8 (y otras) no son independientes del estado. Además, una secuencia de
archivos con una configuración regional UTF-8 puede intentar leer el marcador de la lista de
materiales del archivo cuando se abre; así que solo abrir el archivo puede leer los caracteres del
archivo y no estará al principio.
#include <iostream>
#include <fstream>
#include <locale>
int main()
{
std::cout << "User-preferred locale setting is "
<< std::locale("").name().c_str() << std::endl;
El cambio explícito a la configuración regional clásica "C" es útil si su programa utiliza una
configuración regional predeterminada diferente y desea garantizar un estándar fijo para leer y
escribir archivos. Con una configuración regional preferida de "C", el ejemplo escribe
78,123.456
78,123.456
78123.456
https://riptutorial.com/es/home 43
Si, por ejemplo, el entorno local preferido es alemán y, por lo tanto, utiliza un formato de número
diferente, el ejemplo escribe
78 123,456
78,123.456
78123.456
https://riptutorial.com/es/home 44
Capítulo 7: Archivos de encabezado
Observaciones
En C ++, como en C, el compilador de C ++ y el proceso de compilación hacen uso del
preprocesador de C. Según lo especificado en el manual del preprocesador GNU C, un archivo de
encabezado se define de la siguiente manera:
• Los archivos de encabezado del sistema declaran las interfaces a partes del
sistema operativo. Los incluye en su programa para proporcionar las definiciones
y declaraciones que necesita para invocar llamadas de sistema y bibliotecas.
• Sus propios archivos de encabezado contienen declaraciones de interfaces entre
los archivos de origen de su programa. Cada vez que tenga un grupo de
declaraciones relacionadas y definiciones de macro, todas o la mayoría de las
cuales son necesarias en varios archivos de origen diferentes, es una buena
idea crear un archivo de encabezado para ellos.
Tenga en cuenta que los archivos de encabezado pueden ser reemplazados como una
convención de estructura de archivos de proyecto por la próxima característica de los módulos,
que aún debe considerarse para su inclusión en un futuro estándar de C ++ en el momento de la
escritura (por ejemplo, C ++ 20).
Examples
Ejemplo básico
El siguiente ejemplo contendrá un bloque de código que se debe dividir en varios archivos de
origen, como se indica en los comentarios de // filename .
https://riptutorial.com/es/home 45
Archivos fuente
// my_function.h
/* Also, usually header files include preprocessor guards so that every header
* is never included twice.
*
* The guard is implemented by checking if a header-file unique preprocessor
* token is defined, and only including the header if it hasn't been included
* once before.
*/
#ifndef MY_FUNCTION_H
#define MY_FUNCTION_H
#endif // MY_FUNCTION_H
// my_function.cpp
/* Note how the corresponding source file for the header includes the interface
* defined in the header so that the compiler is aware of what the source file is
* implementing.
*
* In this case, the source file requires knowledge of the global constant
* global_value only defined in my_function.h. Without inclusion of the header
* file, this source file would not compile.
*/
#include "my_function.h" // or #include "my_function.hpp"
int my_function() {
return global_value; // return 42;
}
Los archivos de encabezado luego se incluyen en otros archivos de origen que desean utilizar la
funcionalidad definida por la interfaz del encabezado, pero no requieren conocimiento de su
implementación (por lo tanto, reduciendo el acoplamiento de código). El siguiente programa hace
uso del encabezado my_function.h como se definió anteriormente:
// main.cpp
https://riptutorial.com/es/home 46
}
El proceso de compilación
Dado que los archivos de encabezado a menudo forman parte de un flujo de trabajo del proceso
de compilación, un proceso de compilación típico que hace uso de la convención de archivo de
encabezado / fuente generalmente hará lo siguiente.
Alternativamente, si uno desea compilar main.cpp en un archivo de objeto primero, y luego vincular
solo los archivos de objeto como el paso final:
g++ -c my_function.cpp
g++ -c main.cpp
Esto significa que la función de plantilla, la función de miembro y las definiciones de clase no
pueden delegarse a un archivo de código fuente separado, ya que cualquier código que utilizará
cualquier construcción con plantilla requiere conocimiento de su definición para generar
generalmente cualquier código derivado.
// templated_function.h
https://riptutorial.com/es/home 47
Lea Archivos de encabezado en línea: https://riptutorial.com/es/cplusplus/topic/7211/archivos-de-
encabezado
https://riptutorial.com/es/home 48
Capítulo 8: Aritmética de punto flotante
Examples
Los números de punto flotante son raros
El primer error que casi todos los programadores cometen es suponer que este código funcionará
según lo previsto:
float total = 0;
for(float a = 0; a != 2; a += 0.01f) {
total += a;
}
El programador novato asume que esto resumirá cada número en el rango 0, 0.01, 0.02, 0.03,
..., 1.97, 1.98, 1.99 , para obtener el resultado 199 la respuesta matemáticamente correcta.
1. El programa como está escrito nunca concluye. a nunca se vuelve igual a 2 , y el bucle
nunca termina.
2. Si reescribimos la lógica del bucle para verificar a < 2 lugar, el bucle termina, pero el total
termina siendo algo diferente de 199 . En las máquinas compatibles con IEEE754, a menudo
suman aproximadamente 201 lugar.
La razón por la que esto sucede es que los números de punto flotante representan
aproximaciones de sus valores asignados .
double a = 0.1;
double b = 0.2;
double c = 0.3;
if(a + b == c)
//This never prints on IEEE754-compliant machines
std::cout << "This Computer is Magic!" << std::endl;
else
std::cout << "This Computer is pretty normal, all things considered." << std::endl;
Si bien lo que vemos el programador son tres números escritos en base10, lo que ven el
compilador (y el hardware subyacente) son números binarios. Debido a que 0.1 , 0.2 y 0.3
requieren una división perfecta por 10 cual es bastante fácil en un sistema base-10, pero
imposible en un sistema base-2, estos números deben almacenarse en formatos imprecisos, de
manera similar a como se muestra el número 1/3 tiene que ser almacenado en la forma imprecisa
0.333333333333333... en base-10.
https://riptutorial.com/es/home 49
representation of 0.1
double b = 0011111111001001100110011001100110011001100110011001100110011010; //imperfect
representation of 0.2
double c = 0011111111010011001100110011001100110011001100110011001100110011; //imperfect
representation of 0.3
double a + b = 0011111111010011001100110011001100110011001100110011001100110100; //Note that
this is not quite equal to the "canonical" 0.3!
https://riptutorial.com/es/home 50
Capítulo 9: Arrays
Introducción
Las matrices son elementos del mismo tipo que se colocan en ubicaciones de memoria contiguas.
Los elementos pueden ser referenciados individualmente por un identificador único con un índice
agregado.
Esto le permite declarar múltiples valores de variables de un tipo específico y acceder a ellos
individualmente sin necesidad de declarar una variable para cada valor.
Examples
Tamaño de matriz: tipo seguro en tiempo de compilación.
//----------------------------------- Machinery:
//----------------------------------- Usage:
#include <iostream>
using namespace std;
auto main()
-> int
{
int const a[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
Size const n = n_items( a );
int b[n] = {}; // An array of the same size as a.
(void) b;
cout << "Size = " << n << "\n";
}
Para C ++ 11
std::extent<decltype(MyArray)>::value;
https://riptutorial.com/es/home 51
Ejemplo:
Con C ++ 17 y versiones posteriores, se puede usar std::size , que está especializado para
arreglos.
// Example of raw dynamic size array. It's generally better to use std::vector.
#include <algorithm> // std::sort
#include <iostream>
using namespace std;
auto main()
-> int
{
cout << "Sorting n integers provided by you.\n";
cout << "n? ";
int const n = int_from( cin );
int* a = new int[n]; // ← Allocation of array of n items.
sort( a, a + n );
for( int i = 0; i < n; ++i ) { cout << a[i] << ' '; }
cout << '\n';
delete[] a;
}
Un programa que declara una matriz T a[n]; donde n se determina un tiempo de ejecución, se
puede compilar con ciertos compiladores que admiten matrices de longitud variable ( VLA) C99
como una extensión de lenguaje. Pero los VLA no son compatibles con C ++ estándar. Este
ejemplo muestra cómo asignar manualmente una matriz de tamaño dinámico a través de una
new[] expresión new[] ,
https://riptutorial.com/es/home 52
... luego utilícelo, y finalmente deséchelo mediante una delete[] -expresión:
delete[] a;
La matriz asignada aquí tiene valores indeterminados, pero se puede inicializar con cero
simplemente agregando un paréntesis vacío () , así: new int[n]() . Más generalmente, para un
tipo de elemento arbitrario, esto realiza una inicialización de valores .
Como parte de una función en una jerarquía de llamadas, este código no sería seguro de
excepción, ya que una excepción antes de la expresión delete[] (y después de la new[] ) causaría
una pérdida de memoria. Una forma de abordar ese problema es automatizar la limpieza, por
ejemplo, mediante un puntero inteligente std::unique_ptr . Pero una forma generalmente mejor de
abordarlo es simplemente usar std::vector : para eso está std::vector .
int main()
{
cout << "Sorting integers provided by you.\n";
cout << "You can indicate EOF via F6 in Windows or Ctrl+D in Unix-land.\n";
vector<int> a; // ← Zero size by default.
while( cin )
{
cout << "One number, please, or indicate EOF: ";
int const x = int_from( cin );
if( !cin.fail() ) { a.push_back( x ); } // Expands as necessary.
}
std::vector es una plantilla de clase de biblioteca estándar que proporciona la noción de una
matriz de tamaño variable. Se encarga de toda la administración de la memoria, y el búfer es
contiguo, por lo que se puede pasar un puntero al búfer (por ejemplo, &v[0] o v.data() ) a las
funciones de API que requieren una matriz sin formato. Incluso se puede expandir un vector en
tiempo de ejecución, por ejemplo, a través de la función miembro push_back que agrega un
elemento.
https://riptutorial.com/es/home 53
Internamente, esto generalmente se logra cuando el vector duplica su tamaño de búfer, su
capacidad, cuando se necesita un búfer más grande. Por ejemplo, para un búfer que comienza
como tamaño 1 y se duplica repetidamente según sea necesario para n = 17 llamadas push_back ,
esto implica 1 + 2 + 4 + 8 + 16 = 31 operaciones de copia, que es menos de 2 × n = 34. Y más
generalmente, la suma de esta secuencia no puede exceder de 2 × n .
En comparación con el ejemplo de matriz sin formato de tamaño dinámico, este código basado en
vector no requiere que el usuario suministre (y sepa) el número de elementos por adelantado. En
su lugar, el vector se expande según sea necesario, para cada nuevo valor de elemento
especificado por el usuario.
Una matriz de matriz sin formato de tamaño fijo (es decir, una matriz sin
formato 2D).
Salida:
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
C ++ no admite sintaxis especial para indexar una matriz multidimensional. En su lugar, una
matriz de este tipo se ve como una matriz de matrices (posiblemente de matrices, etc.), y se
utiliza la notación de índice único ordinario [ i ] para cada nivel. En el ejemplo anterior, m[y]
refiere a la fila y de m , donde y es un índice basado en cero. Entonces esta fila puede ser
indexado a su vez, por ejemplo, m[y][x] , que se refiere a la x ésimo elemento - o columna - de fila
y.
https://riptutorial.com/es/home 54
Es decir, el último índice varía más rápidamente, y en la declaración, el rango de este índice, que
es el número de columnas por fila, es el último y el tamaño "más interno" especificado.
Si no desea una dependencia en Boost o alguna otra biblioteca, entonces la matriz de tamaño
dinámico de un hombre pobre en C ++ es como
vector<vector<int>> m( 3, vector<int>( 7 ) );
… Donde vector es std::vector . La matriz se crea aquí copiando un vector de fila n veces donde
n es el número de filas, aquí 3. Tiene la ventaja de proporcionar la misma notación de indexación
m[y][x] que para una matriz de matriz sin formato de tamaño fijo, pero es un poco ineficiente
porque implica una asignación dinámica para cada fila, y es un poco inseguro porque es posible
cambiar inadvertidamente el tamaño de una fila.
Un enfoque más seguro y eficiente es usar un solo vector como almacenamiento para la matriz y
asignar el código del cliente ( x , y ) a un índice correspondiente en ese vector:
//--------------------------------------------- Machinery:
#include <algorithm> // std::copy
#include <assert.h> // assert
#include <initializer_list> // std::initializer_list
#include <vector> // std::vector
#include <stddef.h> // ptrdiff_t
namespace my {
using Size = ptrdiff_t;
using std::initializer_list;
using std::vector;
https://riptutorial.com/es/home 55
vector<Item> items_;
Size n_cols_;
public:
auto n_rows() const -> Size { return items_.size()/n_cols_; }
auto n_cols() const -> Size { return n_cols_; }
Matrix(): n_cols_( 0 ) {}
//--------------------------------------------- Usage:
using my::Matrix;
auto some_matrix()
-> Matrix<int>
{
return
{
{ 1, 2, 3, 4, 5, 6, 7 },
{ 8, 9, 10, 11, 12, 13, 14 },
{ 15, 16, 17, 18, 19, 20, 21 }
};
}
#include <iostream>
#include <iomanip>
using namespace std;
auto main() -> int
{
Matrix<int> const m = some_matrix();
assert( m.n_cols() == 7 );
assert( m.n_rows() == 3 );
https://riptutorial.com/es/home 56
for( int y = 0, y_end = m.n_rows(); y < y_end; ++y )
{
for( int x = 0, x_end = m.n_cols(); x < x_end; ++x )
{
cout << setw( 4 ) << m.item( x, y ); // ← Note: not `m[y][x]`!
}
cout << '\n';
}
}
Salida:
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
El código anterior no es de grado industrial: está diseñado para mostrar los principios básicos y
satisfacer las necesidades de los estudiantes que aprenden C ++.
Por ejemplo, uno puede definir sobrecargas de operator() para simplificar la notación de
indexación.
Inicialización de matriz
Una matriz es solo un bloque de ubicaciones de memoria secuencial para un tipo específico de
variable. Las matrices se asignan de la misma manera que las variables normales, pero con
corchetes anexados a su nombre [] que contienen el número de elementos que caben en la
memoria de la matriz.
El siguiente ejemplo de una matriz utiliza el tipo int , el nombre variable arrayOfInts y el número
de elementos [5] que la matriz tiene espacio:
int arrayOfInts[5];
Al inicializar una matriz al enumerar todos sus miembros, no es necesario incluir el número de
elementos dentro de los corchetes. Será calculado automáticamente por el compilador. En el
siguiente ejemplo, es 5:
También es posible inicializar solo los primeros elementos mientras se asigna más espacio. En
este caso, es obligatorio definir la longitud entre paréntesis. Lo siguiente asignará una matriz de
longitud 5 con inicialización parcial, el compilador inicializa todos los elementos restantes con el
valor estándar del tipo de elemento, en este caso cero.
https://riptutorial.com/es/home 57
int arrayOfInts[5] = {10,20}; // means 10, 20, 0, 0, 0
Las matrices de otros tipos de datos básicos pueden inicializarse de la misma manera.
char arrayOfChars[5]; // declare the array and allocate the memory, don't initialize
char arrayOfChars[5] = { 'a', 'b', 'c', 'd', 'e' } ; //declare and initialize
También es importante tener en cuenta que al acceder a los elementos de la matriz, el índice de
elementos de la matriz (o posición) comienza desde 0.
int array[5] = { 10/*Element no.0*/, 20/*Element no.1*/, 30, 40, 50/*Element no.4*/};
std::cout << array[4]; //outputs 50
std::cout << array[0]; //outputs 10
https://riptutorial.com/es/home 58
Capítulo 10: Atributos
Sintaxis
• [[detalles]]: atributo simple sin argumentos
Examples
[[sin retorno]]
C ++ 11
C ++ 11 introdujo el atributo [[noreturn]] . Se puede usar para que una función indique que la
función no regresa a la persona que llama, ya sea ejecutando una declaración de retorno , o
llegando al final si es cuerpo (es importante tener en cuenta que esto no se aplica a las funciones
void , ya que devuelva a la persona que llama, simplemente no devuelven ningún valor). Dicha
función puede terminar llamando a std::terminate o std::exit , o lanzando una excepción.
También vale la pena señalar que una función de este tipo puede regresar ejecutando longjmp .
Por ejemplo, la función a continuación siempre lanzará una excepción o llamará std::terminate ,
por lo que es un buen candidato para [[noreturn]] :
Este tipo de funcionalidad permite al compilador finalizar una función sin una declaración de
retorno si sabe que el código nunca se ejecutará. Aquí, debido a que la llamada a
ownAssertFailureHandler (definida anteriormente) en el código a continuación nunca regresará, el
compilador no necesita agregar código debajo de esa llamada:
https://riptutorial.com/es/home 59
ownAssertFailureHandler("Negative number passed to createSequence()"s);
// return std::vector<int>{}; //< Not needed because of [[noreturn]]
}
Tenga en cuenta que [[noreturn]] se utiliza principalmente en las funciones void. Sin embargo,
esto no es un requisito, permitiendo que las funciones se usen en la programación genérica:
template<class InconsistencyHandler>
double fortyTwoDivideBy(int i) {
if (i == 0)
i = InconsistencyHandler::correct(i);
return 42. / i;
}
struct InconsistencyThrower {
static [[noreturn]] int correct(int i) { ownAssertFailureHandler("Unknown
inconsistency"s); }
}
struct InconsistencyChangeToOne {
static int correct(int i) { return 1; }
}
• std :: abortar
• std :: exit
• std :: quick_exit
• std :: inesperado
• std :: terminar
• std :: rethrow_exception
• std :: throw_with_nested
• std :: nested_exception :: rethrow_nested
[[caer a través]]
C ++ 17
Cada vez que se termina un case en un switch , se ejecutará el código del siguiente caso. Este
último se puede prevenir usando la declaración 'break'. Dado que este llamado comportamiento
https://riptutorial.com/es/home 60
fallido puede introducir errores cuando no está previsto, varios compiladores y analizadores
estáticos emiten una advertencia al respecto.
switch(input) {
case 2011:
case 2014:
case 2017:
std::cout << "Using modern C++" << std::endl;
[[fallthrough]]; // > No warning
case 1998:
case 2003:
standard = input;
}
Consulte la propuesta para obtener ejemplos más detallados sobre cómo se puede usar
[[fallthrough]] .
C ++ 14
// Provides specific message which helps other programmers fixing there code
[[deprecated("Use the variant with unique_ptr instead, this function will be removed in the
next release")]]
void function(std::auto_ptr<A> a);
https://riptutorial.com/es/home 61
[[nodiscard]]
C ++ 17
El atributo [[nodiscard]] se puede usar para indicar que el valor de retorno de una función no
debe ignorarse cuando se realiza una llamada de función. Si se ignora el valor de retorno, el
compilador debe dar una advertencia sobre esto. El atributo se puede agregar a:
Agregar el atributo a un tipo tiene el mismo comportamiento que agregar el atributo a cada
función que devuelve este tipo.
template<typename Function>
[[nodiscard]] Finally<std::decay_t<Function>> onExit(Function &&f);
Consulte la propuesta para obtener ejemplos más detallados sobre cómo se puede usar
[[nodiscard]] .
Nota: Los detalles de la implementación de Finally / onExit se omiten en el ejemplo, vea Finally /
ScopeExit .
[[maybe_unused]]
El atributo [[maybe_unused]] se crea para indicar en el código que cierta lógica podría no ser
utilizada. Esto se vincula a menudo con las condiciones del preprocesador, donde se puede usar
o no. Como los compiladores pueden dar advertencias sobre variables no utilizadas, esta es una
forma de suprimirlas indicando la intención.
Un ejemplo típico de las variables que se necesitan en las construcciones de depuración cuando
no se necesitan en producción son los valores de retorno que indican el éxito. En las
compilaciones de depuración, la condición debe ser confirmada, aunque en producción, estas
afirmaciones se han eliminado.
Un ejemplo más complejo son diferentes tipos de funciones de ayuda que se encuentran en un
https://riptutorial.com/es/home 62
espacio de nombres sin nombre. Si estas funciones no se utilizan durante la compilación, un
compilador puede dar una advertencia sobre ellas. Idealmente, le gustaría protegerlos con las
mismas etiquetas de preprocesador que la persona que llama, aunque como esto podría volverse
complejo, el atributo [[maybe_unused]] es una alternativa más [[maybe_unused]] mantener.
namespace {
[[maybe_unused]] std::string createWindowsConfigFilePath(const std::string &relativePath);
// TODO: Reuse this on BSD, MAC ...
[[maybe_unused]] std::string createLinuxConfigFilePath(const std::string &relativePath);
}
Consulte la propuesta para obtener ejemplos más detallados sobre cómo se puede usar
[[maybe_unused]] .
https://riptutorial.com/es/home 63
Capítulo 11: auto
Observaciones
La palabra clave auto es un nombre de tipo que representa un tipo deducido automáticamente.
Ya era una palabra clave reservada en C ++ 98, heredada de C. En versiones anteriores de C ++,
se podía usar para indicar explícitamente que una variable tiene una duración de almacenamiento
automática:
int main()
{
auto int i = 5; // removing auto has no effect
}
Examples
Muestra auto básica
La palabra clave auto proporciona la deducción automática del tipo de una variable.
con lambdas :
https://riptutorial.com/es/home 64
auto myMap = std::map<int,float>();
myMap.emplace(1,3.14);
auto también puede causar problemas cuando las plantillas de expresión entran en juego:
auto mult(int c) {
return c * std::valarray<int>{1};
}
auto v = mult(3);
std::cout << v[0]; // some value that could be, but almost certainly is not, 3.
La razón es que el operator* en valarray le proporciona un objeto proxy que se refiere al valarray
como un medio de evaluación perezosa. Al usar auto , estás creando una referencia que cuelga.
En lugar de mult ha devuelto un std::valarray<int> , entonces el código definitivamente se
imprimiría 3.
La palabra clave auto por sí misma representa un tipo de valor, similar a int o char . Se puede
modificar con la palabra clave const y el símbolo & para representar un tipo const o un tipo de
referencia, respectivamente. Estos modificadores se pueden combinar.
En este ejemplo, s es un tipo de valor (su tipo se deducirá como std::string ), por lo que cada
iteración del bucle for copia una cadena del vector en s .
Si el cuerpo del bucle modifica s (por ejemplo, al llamar a s.append(" and stuff") ), solo se
modificará esta copia, no el miembro original de las strings .
Por otro lado, si s se declara con auto& será un tipo de referencia (se inferirá que es std::string& ),
por lo que en cada iteración del bucle se le asignará una referencia a una cadena en el vector:
for(auto& s : strings) {
std::cout << s << std::endl;
}
https://riptutorial.com/es/home 65
Finalmente, si s se declara const auto& , será un tipo de referencia const, lo que significa que en
cada iteración del bucle se le asignará una referencia const a una cadena en el vector:
Dentro del cuerpo de este bucle, s no se puede modificar (es decir, no hay métodos no const
pueden ser llamados en él).
Cuando se utiliza el modo auto con el rango for bucles, generalmente es una buena práctica usar
const auto& si el cuerpo del bucle no modifica la estructura que se está repitiendo, ya que esto
evita copias innecesarias.
que es equivalente a
int main() {}
Mayormente útil combinado con decltype para usar parámetros en lugar de std::declval<T> :
C ++ 14
auto print = [](const auto& arg) { std::cout << arg << std::endl; };
print(42);
print("hello world");
struct lambda {
template <typename T>
auto operator ()(const T& arg) const {
std::cout << arg << std::endl;
}
};
https://riptutorial.com/es/home 66
y entonces
lambda print;
print(42);
print("hello world");
A veces, el auto puede comportarse de la forma no esperada por un programador. El tipo deduce
la expresión, incluso cuando la deducción de tipo no es lo correcto.
Aquí la flag no sería bool , pero std::vector<bool>::reference , ya que para la especialización bool
del vector de plantilla el operator [] devuelve un objeto proxy con el operador de conversión
operator bool definido.
std::vector<bool> getFlags();
En casos como este, puede declarar una variable con auto e inicializarla mediante la conversión al
tipo que desea deducir:
pero en ese punto, simplemente reemplazar el auto por bool tiene más sentido.
Otro caso donde los objetos proxy pueden causar problemas son las plantillas de expresión . En
ese caso, las plantillas a veces no están diseñadas para durar más allá de la expresión completa
actual por razones de eficiencia, y el uso del objeto proxy en la siguiente causa un
comportamiento indefinido.
https://riptutorial.com/es/home 67
Capítulo 12: Bucles
Introducción
Una instrucción de bucle ejecuta un grupo de instrucciones repetidamente hasta que se cumple
una condición. Hay 3 tipos de bucles primitivos en C ++: para, while y do ... while.
Sintaxis
• sentencia while ( condición );
• hacer enunciado while ( expresión );
• para ( para-init-sentencia ; condición ; expresión ) sentencia ;
• para (para-gama-declaración: Con fines de gama-inicializador) Declaración;
• rotura ;
• continuar
Observaciones
algorithm llamadas de algorithm son generalmente preferibles a los bucles escritos a mano.
Si desea algo que ya hace un algoritmo (o algo muy similar), la llamada al algoritmo es más clara,
a menudo más eficiente y menos propensa a errores.
Si necesita un bucle que haga algo bastante simple (pero requeriría una confusa maraña de
carpetas y adaptadores si estuviera usando un algoritmo), simplemente escriba el bucle.
Examples
Basado en rango para
C ++ 11
for bucles pueden utilizarse para recorrer en iteración los elementos de un rango basado en
iteradores, sin usar un índice numérico o acceder directamente a los iteradores:
for(auto val: v)
{
std::cout << val << " ";
}
Esto iterará sobre cada elemento en v , con val obteniendo el valor del elemento actual. La
siguiente declaración:
https://riptutorial.com/es/home 68
for (for-range-declaration : for-range-initializer ) statement
es equivalente a:
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr, __end = end-expr;
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
C ++ 17
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr;
auto __end = end-expr; // end is allowed to be a different type than begin in C++17
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
{
auto&& __range = v;
auto __begin = v.begin(), __end = v.end();
for (; __begin != __end; ++__begin) {
auto val = *__begin;
std::cout << val << " ";
}
}
Tenga en cuenta que auto val declara un tipo de valor, que será una copia de un valor
almacenado en el rango (lo estamos inicializando desde el iterador). Si los valores almacenados
en el rango son costosos de copiar, es posible que desee utilizar const auto &val . Tampoco está
obligado a utilizar auto ; puede usar un nombre de tipo apropiado, siempre que sea implícitamente
convertible del tipo de valor del rango.
Si necesita acceso al iterador, basado en rango no puede ayudarlo (no sin esfuerzo, al menos).
for(float &val: v)
{
std::cout << val << " ";
}
https://riptutorial.com/es/home 69
Podría iterar en la referencia const si tiene un contenedor const :
vector<bool> v(10);
for(auto&& val: v)
{
val = true;
}
El tipo de "gama" proporcionada al intervalo basado- for puede ser uno de los siguientes:
• Matrices de idiomas:
• Cualquier tipo que tenga funciones miembro begin() y end() , que devuelve los iteradores a
los elementos del tipo. Los contenedores de la biblioteca estándar califican, pero los tipos
definidos por el usuario también se pueden usar:
struct Rng
{
float arr[3];
https://riptutorial.com/es/home 70
};
int main()
{
Rng rng = {{0.4f, 12.5f, 16.234f}};
• Cualquier tipo que tenga funciones de begin(type) y end(type) que no sean miembros que se
pueden encontrar mediante búsqueda dependiente de argumento, según el type . Esto es
útil para crear un tipo de rango sin tener que modificar el tipo de clase en sí:
namespace Mine
{
struct Rng {float arr[3];};
int main()
{
Mine::Rng rng = {{0.4f, 12.5f, 16.234f}};
En bucle
Un bucle for ejecuta instrucciones en el loop body del loop body , mientras que la condition del
bucle es verdadera. Antes de que la initialization statement bucle se ejecute exactamente una
vez. Después de cada ciclo, se ejecuta la parte de iteration execution .
• initialization statement: esta sentencia se ejecuta solo una vez, al comienzo del bucle for
. Puede ingresar una declaración de múltiples variables de un tipo, como int i = 0, a = 2, b
= 3
https://riptutorial.com/es/home 71
. Estas variables solo son válidas en el ámbito del bucle. Las variables definidas antes del
bucle con el mismo nombre se ocultan durante la ejecución del bucle.
• condition : esta declaración se evalúa antes de cada ejecución del cuerpo del bucle y
cancela el bucle si se evalúa como false .
• iteration execution : esta instrucción se ejecuta después del cuerpo del bucle, antes de la
siguiente evaluación de la condición , a menos que el bucle for sea abortado en el cuerpo
(por break , goto , return o una excepción lanzada). Puede ingresar varias declaraciones en
la parte de iteration execution , como a++, b+=10, c=b+a .
/*initialization*/
while (/*condition*/)
{
// body of the loop; using 'continue' will skip to increment part below
/*iteration execution*/
}
El caso más común para usar un bucle for es ejecutar sentencias un número específico de veces.
Por ejemplo, considere lo siguiente:
Notas:
https://riptutorial.com/es/home 72
razones de legibilidad, es una buena práctica realizar solo operaciones directamente
relevantes para el bucle.
• Una variable declarada en la declaración de inicialización es visible solo dentro del alcance
del bucle for y se libera al finalizar el bucle.
• No olvide que la variable que se declaró en la initialization statement se puede modificar
durante el bucle, así como la variable verificada en la condition .
• int counter = 0 inicializa la variable counter a 0. (Esta variable solo se puede usar dentro del
bucle for ).
• counter <= 10es una condición booleana que verifica si el counter es menor o igual a 10. Si
es true , el bucle se ejecuta. Si es false , el bucle termina.
• ++counter es una operación de incremento que incrementa el valor de counter en 1 antes de
la siguiente verificación de condición.
// infinite loop
for (;;)
std::cout << "Never ending!\n";
// infinite loop
while (true)
std::cout << "Never ending!\n";
Sin embargo, aún se puede dejar un bucle infinito utilizando las instrucciones break , goto o return
o lanzando una excepción.
El siguiente ejemplo común de iteración sobre todos los elementos de una colección STL (por
ejemplo, un vector ) sin usar el encabezado <algorithm> es:
Mientras bucle
Un while de bucle ejecuta las instrucciones varias veces hasta que la condición dada se evalúa
https://riptutorial.com/es/home 73
como false . Esta declaración de control se usa cuando no se sabe, de antemano, cuántas veces
se debe ejecutar un bloque de código.
Por ejemplo, para imprimir todos los números del 0 al 9, se puede usar el siguiente código:
int i = 0;
while (i < 10)
{
std::cout << i << " ";
++i; // Increment counter
}
std::cout << std::endl; // End of line; "0 1 2 3 4 5 6 7 8 9" is printed to the console
C ++ 17
Tenga en cuenta que desde C ++ 17, las 2 primeras declaraciones pueden combinarse
while (true)
{
// Do something forever (however, you can exit the loop by calling 'break'
}
Hay otra variante de while bucles, a saber, do...while construct. Vea el ejemplo del bucle do-while
para más información.
while(std::shared_ptr<Object> p = get_object()) {
p->do_something();
}
// p is no longer in scope.
Sin embargo, no está permitido hacer lo mismo con un bucle do...while while; en su lugar, declare
la variable antes del bucle, y (opcionalmente) encierre tanto la variable como el bucle dentro de
un ámbito local si desea que la variable salga del alcance después de que finalice el bucle:
https://riptutorial.com/es/home 74
//This doesn't compile
do {
s = do_something();
} while (short s > 0);
// Good
short s;
do {
s = do_something();
} while (s > 0);
Esto se debe a que la parte de instrucción de un bucle do...while while (el cuerpo del bucle) se
evalúa antes de llegar a la parte de expresión ( while ), y por lo tanto, cualquier declaración en la
expresión no será visible durante la primera iteración de lazo.
Bucle Do-while
Un bucle do-while es muy similar a un bucle while, excepto que la condición se comprueba al final
de cada ciclo, no al principio. Por lo tanto, se garantiza que el bucle se ejecuta al menos una vez.
El siguiente código imprimirá 0 , ya que la condición se evaluará como false al final de la primera
iteración:
int i =0;
do
{
std::cout << i;
++i; // Increment counter
}
while (i < 0);
std::cout << std::endl; // End of line; 0 is printed to the console
En contraste con el bucle do- while, lo siguiente no imprimirá nada, porque la condición se evalúa
como false al comienzo de la primera iteración:
int i =0;
while (i < 0)
{
std::cout << i;
++i; // Increment counter
}
std::cout << std::endl; // End of line; nothing is printed to the console
Nota: Un bucle while se puede salir sin la condición de convertirse en falsa utilizando una break ,
goto , o return comunicado.
int i = 0;
do
{
std::cout << i;
https://riptutorial.com/es/home 75
++i; // Increment counter
if (i > 5)
{
break;
}
}
while (true);
std::cout << std::endl; // End of line; 0 1 2 3 4 5 is printed to the console
Un bucle do-while trivial también es ocasionalmente usado para escribir macros que requieren su
propio ámbito (en cuyo caso el punto y coma final se omite de la definición de la macro y requiere
ser proporcionado por el usuario):
Las instrucciones de control de bucle se utilizan para cambiar el flujo de ejecución desde su
secuencia normal. Cuando la ejecución deja un ámbito, se destruyen todos los objetos
automáticos que se crearon en ese ámbito. El break y continue son instrucciones de control de
bucle.
1
2
3
La declaración de continue no sale inmediatamente del bucle, sino que se salta el resto del cuerpo
del bucle y va a la parte superior del bucle (incluida la verificación de la condición).
https://riptutorial.com/es/home 76
does not execute */
std::cout << i << " is an odd number\n";
}
1 is an odd number
3 is an odd number
5 is an odd number
Debido a que tales cambios en el flujo de control a veces son difíciles de entender para los
humanos, los break y los continue se usan con moderación. Una implementación más sencilla
suele ser más fácil de leer y entender. Por ejemplo, el primer bucle for con la break anterior podría
reescribirse como:
Usando bucles de rango de base, puede recorrer una subparte de un contenedor u otro rango
dado generando un objeto proxy que califica para bucles basados en rango.
https://riptutorial.com/es/home 77
}
};
template<class C>
auto except_first( C& c ) {
auto r = range(c);
if (r.empty()) return r;
return r.without_front();
}
std::vector<int> v = {1,2,3,4};
e imprimir
2
3
4
Tenga en cuenta que los objetos intermedios generados en la parte for(:range_expression) del
bucle for habrán caducado antes for comience el bucle for .
https://riptutorial.com/es/home 78
Capítulo 13: Búsqueda de nombre
dependiente del argumento
Examples
Que funciones se encuentran
Todas las funciones y plantillas dentro de todos los espacios de nombres asociados se
encuentran por búsqueda dependiente del argumento. Además, se encuentran funciones de
amigo en el ámbito del espacio de nombres declaradas en clases asociadas , que normalmente
no son visibles. Sin embargo, se ignoran las directivas de uso.
Todas las siguientes llamadas de ejemplo son válidas, sin calificar f por el nombre del espacio de
nombres en la llamada.
namespace A {
https://riptutorial.com/es/home 79
struct Z { };
namespace I { void g(Z); }
using namespace I;
// example calls
f(A::X());
f(A::X::Y());
f(std::make_shared<A::X>());
https://riptutorial.com/es/home 80
Capítulo 14: C ++ Streams
Observaciones
El constructor predeterminado de std::istream_iterator construye un iterador que representa el
final de la secuencia. Por lo tanto, std::copy(std::istream_iterator<int>(ifs),
std::istream_iterator<int>(), .... significa copiar desde la posición actual en ifs hasta el final.
Examples
Corrientes de cuerda
std::ostringstream es una clase cuyos objetos se parecen a un flujo de salida (es decir, puede
escribir en ellos a través del operator<< ), pero en realidad almacena los resultados de escritura y
los proporciona en forma de un flujo.
#include <sstream>
#include <string>
int main()
{
ostringstream ss;
ss << "the answer to everything is " << 42;
const string result = ss.str();
}
La línea
ostringstream ss;
crea tal objeto. Este objeto se manipula primero como un flujo regular:
Después de eso, sin embargo, el flujo resultante se puede obtener de esta manera:
Esto es principalmente útil cuando tenemos una clase para la cual se ha definido la serialización
de flujo y para la que queremos una forma de cadena. Por ejemplo, supongamos que tenemos
https://riptutorial.com/es/home 81
alguna clase
class foo
{
// All sort of stuff here.
};
foo f;
podríamos usar
ostringstream ss;
ss << f;
const string result = ss.str();
ifstream tiene el operator bool() , que devuelve verdadero cuando una secuencia no tiene errores
y está lista para leer. Además, ifstream::operator >> devuelve una referencia al flujo mismo, de
modo que podemos leer y verificar EOF (así como errores) en una línea con una sintaxis muy
elegante:
std::ifstream ifs("1.txt");
std::string s;
while(ifs >> s) {
std::cout << s << std::endl;
}
https://riptutorial.com/es/home 82
ifstream::operator >> lee la secuencia hasta que ifstream::operator >> cualquier carácter de
espacio en blanco, por lo que el código anterior imprimirá las palabras de una línea en líneas
separadas. Para leer todo hasta el final de la línea, use std::getline lugar de ifstream::operator
>> . getline devuelve la referencia al hilo con el que trabajó, por lo que está disponible la misma
sintaxis:
while(std::getline(ifs, s)) {
std::cout << s << std::endl;
}
Obviamente, std::getline también debe usarse para leer un archivo de una sola línea hasta el
final.
s.resize(100);
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
s.begin());
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::back_inserter(s));
Alternativamente, es posible inicializar una colección con datos de flujo, usando un constructor
con argumentos de rango de iterador:
std::vector v(std::istreambuf_iterator<char>(ifs),
std::istreambuf_iterator<char>());
Tenga en cuenta que estos ejemplos también son aplicables si ifs se abre como archivo binario:
Copiando arroyos
Un archivo se puede copiar en otro archivo con secuencias e iteradores:
std::ofstream ofs("out.file");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
https://riptutorial.com/es/home 83
std::ostream_iterator<char>(ofs));
ofs.close();
o redirigido a cualquier otro tipo de flujo con una interfaz compatible. Por ejemplo, el flujo de red
Boost.Asio:
boost::asio::ip::tcp::iostream stream;
stream.connect("example.com", "http");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::ostream_iterator<char>(stream));
stream.close();
Arrays
Como los iteradores pueden considerarse como una generalización de los punteros, los
contenedores STL en los ejemplos anteriores pueden reemplazarse con matrices nativas. Aquí es
cómo analizar números en una matriz:
int arr[100];
std::copy(std::istream_iterator<char>(ifs), std::istream_iterator<char>(), arr);
Tenga cuidado con el desbordamiento del búfer, ya que las matrices no se pueden redimensionar
sobre la marcha después de que se asignaron. Por ejemplo, si el código anterior se alimenta con
un archivo que contiene más de 100 números enteros, intentará escribir fuera de la matriz y se
ejecutará en un comportamiento indefinido.
Impresión básica
std::ostream_iterator permite imprimir el contenido de un contenedor STL en cualquier flujo de
salida sin ciclos explícitos. El segundo argumento del constructor std::ostream_iterator establece
el delimitador. Por ejemplo, el siguiente código:
std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ! "));
imprimirá
1 ! 2 ! 3 ! 4 !
https://riptutorial.com/es/home 84
ejemplo, sintonicemos std::cout para imprimir valores de punto flotante con 3 dígitos después del
punto decimal:
e instanciar std::ostream_iterator con float , mientras que los valores contenidos permanecen int
:
std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<float>(std::cout, " ! "));
Generación y transformación.
std::generatefunciones std::generate , std::generate_n y std::transform proporcionan una
herramienta muy poderosa para la manipulación de datos sobre la marcha. Por ejemplo, tener un
vector:
std::vector<int> v = {1,2,3,4,8,16};
podemos imprimir fácilmente el valor booleano de "x es par" para cada elemento:
https://riptutorial.com/es/home 85
Arrays
Al igual que en la sección sobre la lectura de archivos de texto, casi todas estas consideraciones
pueden aplicarse a matrices nativas. Por ejemplo, imprimamos valores cuadrados de una matriz
nativa:
Análisis de archivos
std::vector<int> v(100);
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
v.begin());
std::vector<int> v;
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
std::back_inserter(v));
Tenga en cuenta que los números en el archivo de entrada se pueden dividir por cualquier
número de caracteres de espacio en blanco y líneas nuevas.
https://riptutorial.com/es/home 86
std::string s;
double a, b;
while(ifs >> a >> b >> s) {
std::cout << a << " " << b << " " << s << std::endl;
}
Transformación
Cualquier función de manipulación de rangos puede usarse con rangos std::istream_iterator .
Uno de ellos es std::transform , que permite procesar datos sobre la marcha. Por ejemplo,
leamos valores enteros, multiplíquelos por 3.14 y almacenemos el resultado en un contenedor de
punto flotante:
std::vector<double> v(100);
std::transform(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
v.begin(),
[](int val) {
return val * 3.14;
});
https://riptutorial.com/es/home 87
Capítulo 15: Campos de bits
Introducción
Los campos de bits empaquetan las estructuras C y C ++ para reducir el tamaño. Esto parece
indoloro: especifique el número de bits para los miembros, y el compilador hace el trabajo de
mezclar bits. La restricción es la incapacidad de tomar la dirección de un miembro de campo de
bit, ya que se almacena de forma combinada. sizeof() también está deshabilitado.
El costo de los campos de bits es un acceso más lento, ya que la memoria debe recuperarse y las
operaciones a nivel de bits deben aplicarse para extraer o modificar los valores de los miembros.
Estas operaciones también se agregan al tamaño ejecutable.
Observaciones
¿Qué tan caras son las operaciones bitwise? Supongamos una estructura de campo no bit
simple:
struct foo {
unsigned x;
unsigned y;
}
static struct foo my_var;
my_var.y = 5;
Esto es sencillo porque x no está mezclado con y. Pero imagina redefinir la estructura con
campos de bits:
struct foo {
unsigned x : 4; /* Range 0-0x0f, or 0 through 15 */
unsigned y : 4;
}
Tanto a x como a y se les asignarán 4 bits, compartiendo un solo byte. Por lo tanto, la estructura
ocupa 1 byte, en lugar de 8. Considere el ensamblaje para establecer y ahora, asumiendo que
termina en el mordisco superior:
https://riptutorial.com/es/home 88
loada register1,#myvar ; get the address of the structure
loadb register2,register1[0] ; get the byte from memory
andb register2,#0x0f ; zero out y in the byte, leaving x alone
orb register2,#0x50 ; put the 5 into the 'y' portion of the byte
stb register1[0],register2 ; put the modified byte back into memory
Esto puede ser una buena compensación si tenemos miles o millones de estas estructuras, y
ayuda a mantener la memoria en la memoria caché o evita el intercambio, o podría inflar el
ejecutable para empeorar estos problemas y demorar el procesamiento. Como con todas las
cosas, usa el buen juicio.
Uso del controlador de dispositivo: evite los campos de bits como una estrategia de
implementación inteligente para los controladores de dispositivo. Los diseños de almacenamiento
de campo de bits no son necesariamente coherentes entre los compiladores, por lo que tales
implementaciones no son portátiles. La lectura-modificación-escritura para establecer valores
puede no hacer lo que los dispositivos esperan, causando comportamientos inesperados.
Examples
Declaración y uso
struct FileAttributes
{
unsigned int ReadOnly: 1;
unsigned int Hidden: 1;
};
Aquí, cada uno de estos dos campos ocupará 1 bit en la memoria. Se especifica mediante : 1
expresión después de los nombres de las variables. El tipo de base del campo de bits podría ser
cualquier tipo integral (int de 8 bits a int de 64 bits). Se recomienda usar el tipo unsigned , de lo
contrario pueden surgir sorpresas.
Si se requieren más bits, reemplace "1" con la cantidad de bits requeridos. Por ejemplo:
struct Date
{
unsigned int Year : 13; // 2^13 = 8192, enough for "year" representation for long time
unsigned int Month: 4; // 2^4 = 16, enough to represent 1-12 month values.
unsigned int Day: 5; // 32
};
Toda la estructura está utilizando tan solo 22 bits, y con la configuración normal del compilador,
sizeof esta estructura sería de 4 bytes.
El uso es bastante simple. Solo declara la variable, y úsala como una estructura ordinaria.
Date d;
d.Year = 2016;
d.Month = 7;
https://riptutorial.com/es/home 89
d.Day = 22;
https://riptutorial.com/es/home 90
Capítulo 16: Categorías de valor
Examples
Significados de la categoría de valor
A las expresiones en C ++ se les asigna una categoría de valor particular, en función del
resultado de esas expresiones. Las categorías de valor para las expresiones pueden afectar la
resolución de sobrecarga de la función C ++.
Las categorías de valor determinan dos propiedades importantes pero separadas de una
expresión. Una propiedad es si la expresión tiene identidad. Una expresión tiene identidad si se
refiere a un objeto que tiene un nombre de variable. Es posible que el nombre de la variable no
esté involucrado en la expresión, pero el objeto aún puede tener uno.
C ++ define otras dos categorías de valores, cada una basada únicamente en una de estas
propiedades: glvalue (expresiones con identidad) y rvalue (expresiones desde las que se puede
mover). Estos actúan como agrupaciones útiles de las categorías anteriores.
prvalue
Una expresión prvalue (pure-rvalue) es una expresión que carece de identidad, cuya evaluación
https://riptutorial.com/es/home 91
se usa normalmente para inicializar un objeto y que se puede mover de forma implícita. Estos
incluyen, pero no se limitan a:
xvalor
Una expresión xvalue (valor eXpiring) es una expresión que tiene identidad y representa un objeto
desde el cual se puede mover implícitamente. La idea general con las expresiones xvalue es que
el objeto que representan se destruirá pronto (de ahí la parte "inspiradora"), y por lo tanto,
mudarse de forma implícita está bien.
Dado:
struct X { int n; };
extern X x;
valor
Una expresión lvalue es una expresión que tiene identidad, pero no se puede mover
implícitamente. Entre ellas se encuentran las expresiones que consisten en un nombre de
variable, un nombre de función, expresiones que son usos de operadores de desreferencia
incorporados y expresiones que se refieren a referencias de valores.
El lvalue típico es simplemente un nombre, pero los lvalues también pueden venir en otros
sabores:
struct X { ... };
X x; // x is an lvalue
X* px = &x; // px is an lvalue
*px = X{}; // *px is also an lvalue, X{} is a prvalue
Además, mientras que la mayoría de los literales (por ejemplo, 4 , 'x' , etc.) son prvalores, los
https://riptutorial.com/es/home 92
literales de cadenas son valores l.
glvalue
Una expresión glvalue (un "lvalue generalizado") es cualquier expresión que tiene identidad,
independientemente de si se puede mover o no. Esta categoría incluye lvalues (expresiones que
tienen identidad pero no se pueden mover) y xvalues (expresiones que tienen identidad y se
pueden mover desde), pero excluye prvalues (expresiones sin identidad).
struct X { int n; };
X foo();
X x;
x; // has a name, so it's a glvalue
std::move(x); // has a name (we're moving from "x"), so it's a glvalue
// can be moved from, so it's an xvalue not an lvalue
valor
Más precisamente, las expresiones rvalue se pueden usar como argumento para una función que
toma un parámetro de tipo T && (donde T es el tipo de expr ). Solo se pueden dar expresiones de
valor como argumentos a tales parámetros de función; Si se usa una expresión sin valor, la
resolución de sobrecarga seleccionará cualquier función que no use un parámetro de referencia
de valor. Y si no existe ninguno, entonces obtienes un error.
La categoría de expresiones rvalue incluye todas las expresiones xvalue y prvalue, y solo esas
expresiones.
La función de biblioteca estándar std::move existe para transformar explícitamente una expresión
sin valor en un valor. Más específicamente, convierte la expresión en un xvalor, ya que incluso si
antes era una expresión de prvalor sin identidad, al pasarla como parámetro a std::move , gana
identidad (el nombre del parámetro de la función) y se convierte en un xvalor.
Considera lo siguiente:
https://riptutorial.com/es/home 93
std::string tiene un constructor que toma un solo parámetro de tipo std::string&& , comúnmente
llamado "constructor de movimiento". Sin embargo, la categoría de valor de la expresión str no es
un rvalue (específicamente es un lvalue), por lo que no puede llamar a esa sobrecarga del
constructor. En su lugar, llama a const std::string& overload, el constructor de copia.
La línea 3 cambia las cosas. El valor de retorno de std::move es un T&& , donde T es el tipo base
del parámetro pasado. Así que std::move(str) devuelve std::string&& . Una llamada de función
cuyo valor de retorno es una referencia rvalue es una expresión rvalue (específicamente un valor
x), por lo que puede llamar al constructor de movimiento de std::string . Después de la línea 3,
str se ha movido desde (los contenidos de quién ahora están indefinidos).
La línea 4 pasa un operador temporal al operador de asignación de std::string . Esto tiene una
sobrecarga que lleva un std::string&& . La expresión std::string("new value") es una expresión
rvalue (específicamente un prvalue), por lo que puede llamar a esa sobrecarga. Por lo tanto, el
temporal se mueve a str , reemplazando los contenidos indefinidos con contenidos específicos.
La línea 5 crea una referencia str_ref llamada str_ref que se refiere a str . Aquí es donde las
categorías de valor se vuelven confusas.
Vea, mientras que str_ref es una referencia de rvalue a std::string , la categoría de valor de la
expresión str_ref no es un valor de r . Es una expresión lvalue. Sí, en serio. Debido a esto, no se
puede llamar al constructor de movimiento de std::string con la expresión str_ref . La línea 6,
por lo tanto, copia el valor de str en test3 .
https://riptutorial.com/es/home 94
Capítulo 17: Clases / Estructuras
Sintaxis
• variable.member_var = constante;
• variable.member_function ();
• variable_pointer-> member_var = constante;
• variable_pointer-> miembro_función ();
Observaciones
Tenga en cuenta que la única diferencia entre las palabras clave struct y class es que, de forma
predeterminada, las variables miembro, las funciones miembro y las clases base de una struct
son public , mientras que en una class son private . Los programadores de C ++ tienden a
llamarlo una clase si tienen constructores y destructores, y la capacidad de imponer sus propios
invariantes; o una estructura si es solo una simple colección de valores, pero el lenguaje C ++ en
sí mismo no hace distinción.
Examples
Conceptos básicos de clase
Una clase es un tipo definido por el usuario. Se introduce una clase con la palabra clave class ,
struct o union . En el uso coloquial, el término "clase" generalmente se refiere solo a las clases no
sindicalizadas.
Las palabras clave class y struct , llamadas claves de clase , son en gran medida
intercambiables, excepto que el especificador de acceso predeterminado para miembros y bases
es "privado" para una clase declarada con la clave de class y "público" para una clase declarada
con la clave struct o union (cf. modificadores de acceso ).
struct Vector
{
int x;
int y;
int z;
};
https://riptutorial.com/es/home 95
// are equivalent to
class Vector
{
public:
int x;
int y;
int z;
};
Al declarar una clase` se agrega un nuevo tipo a su programa, y es posible crear una instancia de
los objetos de esa clase mediante
Vector my_vector;
my_vector.x = 10;
my_vector.y = my_vector.x + 1; // my_vector.y = 11;
my_vector.z = my_vector.y - 4; // my:vector.z = 7;
Especificadores de acceso
Hay tres palabras clave que actúan como especificadores de acceso . Estos limitan el acceso a
los miembros de la clase siguiendo el especificador, hasta que otro especificador cambie de
nuevo el nivel de acceso:
protected Sólo la clase en sí, las clases derivadas y los amigos tienen acceso.
MyStruct s;
s.x = 9; // well formed, because x is public
MyClass c;
c.x = 9; // ill-formed, because x is private
Los especificadores de acceso se usan principalmente para limitar el acceso a los campos y
métodos internos, y obligan al programador a usar una interfaz específica, por ejemplo, para
forzar el uso de captadores y definidores en lugar de hacer referencia a una variable
https://riptutorial.com/es/home 96
directamente:
class MyClass {
public: /* Methods: */
private: /* Fields: */
int m_x;
};
Usar protected es útil para permitir que cierta funcionalidad del tipo solo sea accesible para las
clases derivadas, por ejemplo, en el siguiente código, el método calculateValue() solo es
accesible para las clases derivadas de la clase base Plus2Base , como FortyTwo :
struct Plus2Base {
int value() noexcept { return calculateValue() + 2; }
protected: /* Methods: */
virtual int calculateValue() noexcept = 0;
};
struct FortyTwo: Plus2Base {
protected: /* Methods: */
int calculateValue() noexcept final override { return 40; }
};
Tenga en cuenta que la palabra clave friend se puede usar para agregar excepciones de acceso
a funciones o tipos para acceder a miembros protegidos y privados.
Las palabras clave public , protected y private también se pueden usar para otorgar o limitar el
acceso a subobjetos de clase base. Ver el ejemplo de herencia .
Herencia
Si una clase / estructura B hereda de una clase / estructura A , esto significa que B tiene como
padre A Decimos que B es una clase / estructura derivada de A , y A es la clase / estructura base.
struct A
{
public:
int p1;
protected:
int p2;
private:
int p3;
};
https://riptutorial.com/es/home 97
{
};
• public
• private
• protected
Incluso es posible tener una class derivada de una struct (o viceversa). En este caso, la herencia
predeterminada está controlada por el hijo, por lo que una struct que se deriva de una class se
establecerá de forma predeterminada como herencia pública, y una class que se deriva de una
struct tendrá herencia privada de forma predeterminada.
herencia public :
B b;
b.p1 = 1; //well formed, p1 is public
b.p2 = 1; //ill formed, p2 is protected
b.p3 = 1; //ill formed, p3 is inaccessible
herencia private
struct B : private A
{
void foo()
{
p1 = 0; //well formed, p1 is private in B
p2 = 0; //well formed, p2 is private in B
p3 = 0; //ill formed, p3 is private in A
}
};
B b;
b.p1 = 1; //ill formed, p1 is private
b.p2 = 1; //ill formed, p2 is private
b.p3 = 1; //ill formed, p3 is inaccessible
herencia protected :
struct B : protected A
{
https://riptutorial.com/es/home 98
void foo()
{
p1 = 0; //well formed, p1 is protected in B
p2 = 0; //well formed, p2 is protected in B
p3 = 0; //ill formed, p3 is private in A
}
};
B b;
b.p1 = 1; //ill formed, p1 is protected
b.p2 = 1; //ill formed, p2 is protected
b.p3 = 1; //ill formed, p3 is inaccessible
Tenga en cuenta que aunque se permite la herencia protected , el uso real de la misma es raro.
Una instancia de cómo se usa la herencia protected en la aplicación es en la especialización de
clase base parcial (generalmente denominada "polimorfismo controlado").
Cuando la POO era relativamente nueva, se decía con frecuencia que la herencia (pública)
modelaba una relación "IS-A". Es decir, la herencia pública es correcta solo si una instancia de la
clase derivada es también una instancia de la clase base.
Más tarde, esto se refinó en el Principio de Sustitución de Liskov : la herencia pública solo debe
usarse cuando / si una instancia de la clase derivada puede sustituirse por una instancia de la
clase base bajo cualquier circunstancia posible (y aún tiene sentido).
En general, se dice que la herencia privada incorpora una relación completamente diferente: "se
implementa en términos de" (a veces se denomina relación "HAS-A"). Por ejemplo, una clase de
Stack podría heredar de forma privada de una clase Vector . La herencia privada tiene una
similitud mucho mayor con la agregación que con la herencia pública.
La herencia protegida casi nunca se usa, y no hay un acuerdo general sobre qué tipo de relación
abarca.
Herencia virtual
struct A{};
struct B: public virtual A{};
Cuando la clase B tiene una base virtual A , significa que A residirá en la mayoría de la clase
derivada del árbol de herencia y, por lo tanto, la mayoría de la clase derivada también es
responsable de inicializar esa base virtual:
struct A
{
int member;
A(int param)
{
member = param;
}
};
https://riptutorial.com/es/home 99
struct B: virtual A
{
B(): A(5){}
};
struct C: B
{
C(): /*A(88)*/ {}
};
void f()
{
C object; //error since C is not initializing it's indirect virtual base `A`
}
También tenga en cuenta que cuando estamos creando un object variable, la mayoría de la clase
derivada es C , por lo que C es responsable de crear (llamar al constructor de) A y, por lo tanto, el
valor de A::member es 88 , no 5 (como lo sería si fuésemos creando objeto de tipo B ).
A A A
/ \ | |
B C B C
\ / \ /
D D
virtual inheritance normal inheritance
La herencia virtual resuelve este problema: como la base virtual reside solo en la mayoría de los
objetos derivados, solo habrá una instancia de A en D
struct A
{
void foo() {}
};
https://riptutorial.com/es/home 100
Eliminar los comentarios resuelve la ambigüedad.
Herencia múltiple
class A {};
class B : public A {};
class A {};
class B {};
class C : public A, public B {};
Nota: esto puede generar ambigüedad si se usan los mismos nombres en varias class o
struct heredadas. ¡Ten cuidado!
La herencia múltiple puede ser útil en ciertos casos pero, a veces, algún tipo de problema se
encuentra mientras se usa la herencia múltiple.
Por ejemplo: dos clases base tienen funciones con el mismo nombre que no se anulan en la clase
derivada y si escribe código para acceder a esa función utilizando el objeto de la clase derivada,
el compilador muestra error porque no puede determinar a qué función llamar. Aquí hay un código
para este tipo de ambigüedad en herencia múltiple.
class base1
{
public:
void funtion( )
{ //code for base1 function }
};
class base2
{
void function( )
{ // code for base2 function }
};
};
int main()
{
derived obj;
https://riptutorial.com/es/home 101
}
Pero, este problema se puede resolver utilizando la función de resolución de alcance para
especificar qué función se debe clasificar como base1 o base2:
int main()
{
obj.base1::function( ); // Function of class base1 is called.
obj.base2::function( ); // Function of class base2 is called.
}
Para acceder a las variables y funciones miembro de un objeto de una clase, el . el operador es
usado:
struct SomeStruct {
int a;
int b;
void foo() {}
};
SomeStruct var;
// Accessing member variable a in var.
std::cout << var.a << std::endl;
// Assigning member variable b in var.
var.b = 1;
// Calling a member function.
var.foo();
Cuando se accede a los miembros de una clase a través de un puntero, el operador -> se usa
comúnmente. Alternativamente, la instancia puede ser anulada y la . Operador utilizado, aunque
esto es menos común:
struct SomeStruct {
int a;
int b;
void foo() {}
};
SomeStruct var;
SomeStruct *p = &var;
// Accessing member variable a in var via pointer.
std::cout << p->a << std::endl;
std::cout << (*p).a << std::endl;
// Assigning member variable b in var via pointer.
p->b = 1;
(*p).b = 1;
// Calling a member function via a pointer.
p->foo();
(*p).foo();
https://riptutorial.com/es/home 102
miembro estático desde una instancia o un puntero a una instancia usando el . o -> operador,
respectivamente, con la misma sintaxis que para acceder a miembros no estáticos.
struct SomeStruct {
int a;
int b;
void foo() {}
static int c;
static void bar() {}
};
int SomeStruct::c;
SomeStruct var;
SomeStruct* p = &var;
// Assigning static member variable c in struct SomeStruct.
SomeStruct::c = 5;
// Accessing static member variable c in struct SomeStruct, through var and p.
var.a = var.c;
var.b = p->c;
// Calling a static member function.
SomeStruct::bar();
var.bar();
p->bar();
Fondo
El operador -> es necesario porque el operador de acceso miembro . Tiene prioridad sobre el
operador de desreferenciación * .
Uno esperaría que *pa desreferenciara p (resultando en una referencia al objeto p que apunta) y
luego accediendo a su miembro a . Pero, de hecho, intenta acceder al miembro a de p y luego
desreferenciarlo. Ie *pa es equivalente a *(pa) . En el ejemplo anterior, esto daría como resultado
un error del compilador debido a dos hechos: Primero, p es un puntero y no tiene un miembro a .
En segundo lugar, a es un número entero y, por lo tanto, no se puede eliminar la referencia.
La solución poco común a este problema sería controlar explícitamente la prioridad: (*p).a
En cambio, el operador -> casi siempre se usa. Es una mano corta para desreferenciar primero el
puntero y luego acceder a él. Ie (*p).a es exactamente lo mismo que p->a .
El operador :: es el operador de alcance, que se utiliza de la misma manera que para acceder a
un miembro de un espacio de nombres. Esto se debe a que se considera que un miembro de la
clase estática está dentro del alcance de esa clase, pero no se considera un miembro de las
instancias de esa clase. El uso de lo normal . y -> también está permitido para miembros
estáticos, a pesar de que no sean miembros de instancia, por razones históricas; esto es útil para
escribir código genérico en plantillas, ya que la persona que llama no necesita preocuparse de si
una función miembro dada es estática o no estática.
https://riptutorial.com/es/home 103
class A {
public:
int move();
int turn();
};
class B : private A {
public:
using A::turn;
};
B b;
b.move(); // compile error
b.turn(); // OK
Este enfoque evita de manera eficiente el acceso a los métodos públicos A mediante el envío al
puntero o referencia A:
B b;
A& a = static_cast<A&>(b); // compile error
En el caso de la herencia pública, dicha conversión proporcionará acceso a todos los métodos
públicos A, a pesar de que existen formas alternativas de evitar esto en B derivada, como la
ocultación:
class B : public A {
private:
int move();
};
o privado utilizando:
class B : public A {
private:
using A::move;
};
B b;
A& a = static_cast<A&>(b); // OK for public inheritance
a.move(); // OK
C ++ 11
Derivar una clase puede estar prohibido con especificador final . Vamos a declarar una clase
final:
class A final {
};
https://riptutorial.com/es/home 104
Ahora cualquier intento de subclase causará un error de compilación:
class A {
};
// OK.
class B final : public A {
};
Amistad
La palabra clave friend se utiliza para otorgar acceso a otras clases y funciones a miembros
privados y protegidos de la clase, incluso a través de su definición fuera del alcance de la clase.
class Animal{
private:
double weight;
double height;
public:
friend void printWeight(Animal animal);
friend class AnimalPrinter;
// A common use for a friend function is to overload the operator<< for streaming.
friend std::ostream& operator<<(std::ostream& os, Animal animal);
};
class AnimalPrinter
{
public:
void print(const Animal& animal)
{
// Because of the `friend class AnimalPrinter;" declaration, we are
// allowed to access private members here.
std::cout << animal.weight << ", " << animal.height << std::endl;
}
}
https://riptutorial.com/es/home 105
int main() {
Animal animal = {10, 5};
printWeight(animal);
AnimalPrinter aPrinter;
aPrinter.print(animal);
10
10, 5
Animal height: 5
Una class o struct también puede contener otra definición de class / struct dentro de sí misma,
que se denomina "clase anidada"; en esta situación, la clase contenedora se conoce como la
"clase adjunta". La definición de clase anidada se considera un miembro de la clase adjunta, pero
por lo demás es separada.
struct Outer {
struct Inner { };
};
Desde fuera de la clase adjunta, se accede a las clases anidadas mediante el operador de
alcance. Sin embargo, desde el interior de la clase adjunta, las clases anidadas se pueden usar
sin calificadores:
struct Outer {
struct Inner { };
Inner in;
};
// ...
Outer o;
Outer::Inner i = o.in;
Al igual que con una class / struct no anidada, las funciones miembro y las variables estáticas se
pueden definir ya sea dentro de una clase anidada o en el espacio de nombres adjunto. Sin
embargo, no se pueden definir dentro de la clase adjunta, debido a que se considera que es una
clase diferente a la clase anidada.
// Bad.
struct Outer {
struct Inner {
void do_something();
};
void Inner::do_something() {}
https://riptutorial.com/es/home 106
};
// Good.
struct Outer {
struct Inner {
void do_something();
};
};
void Outer::Inner::do_something() {}
Al igual que con las clases no anidadas, las clases anidadas se pueden declarar y definir
posteriormente, siempre que se definan antes de usarlas directamente.
class Outer {
class Inner1;
class Inner2;
Inner1 in1;
Inner2* in2p;
public:
Outer();
~Outer();
};
C ++ 11
Antes de C ++ 11, las clases anidadas solo tenían acceso a nombres de tipo, miembros static y
enumeradores de la clase adjunta; todos los demás miembros definidos en la clase adjunta
estaban fuera de los límites.
C ++ 11
A partir de C ++ 11, las clases anidadas y sus miembros se tratan como si fueran friend de la
clase adjunta y pueden acceder a todos sus miembros, de acuerdo con las reglas de acceso
habituales; si los miembros de la clase anidada requieren la capacidad de evaluar uno o más
miembros no estáticos de la clase adjunta, por lo tanto, se les debe pasar una instancia:
class Outer {
struct Inner {
int get_sizeof_x() {
return sizeof(x); // Legal (C++11): x is unevaluated, so no instance is required.
}
https://riptutorial.com/es/home 107
int get_x() {
return x; // Illegal: Can't access non-static member without an instance.
}
int get_x(Outer& o) {
return o.x; // Legal (C++11): As a member of Outer, Inner can access private
members.
}
};
int x;
};
A la inversa, la clase adjunta no se trata como un amigo de la clase anidada y, por lo tanto, no
puede acceder a sus miembros privados sin un permiso explícito.
class Outer {
class Inner {
// friend class Outer;
int x;
};
Inner in;
public:
int get_x() {
return in.x; // Error: int Outer::Inner::x is private.
// Uncomment "friend" line above to fix.
}
};
Los amigos de una clase anidada no se consideran automáticamente amigos de la clase adjunta;
Si también necesitan ser amigos de la clase adjunta, esto debe declararse por separado. A la
inversa, como la clase adjunta no se considera automáticamente un amigo de la clase anidada,
tampoco se considerarán amigos de la clase anexa amigos de la clase anidada.
class Outer {
friend void barge_out(Outer& out, Inner& in);
class Inner {
friend void barge_in(Outer& out, Inner& in);
int i;
};
int o;
};
https://riptutorial.com/es/home 108
int o = out.o; // Good.
}
Al igual que con todos los demás miembros de la clase, las clases anidadas solo pueden
nombrarse desde fuera de la clase si tienen acceso público. Sin embargo, puede acceder a ellos
sin importar el modificador de acceso, siempre y cuando no los nombre explícitamente.
class Outer {
struct Inner {
void func() { std::cout << "I have no private taboo.\n"; }
};
public:
static Inner make_Inner() { return Inner(); }
};
// ...
También puede crear un alias de tipo para una clase anidada. Si un alias de tipo está contenido
en la clase adjunta, el tipo anidado y el alias de tipo pueden tener diferentes modificadores de
acceso. Si el alias de tipo está fuera de la clase adjunta, requiere que la clase anidada, o un
typedef misma, sea pública.
class Outer {
class Inner_ {};
public:
typedef Inner_ Inner;
};
// ...
Al igual que con otras clases, las clases anidadas pueden derivar o derivarse de otras clases.
struct Outer {
struct Inner : Base {};
};
https://riptutorial.com/es/home 109
Esto puede ser útil en situaciones en las que la clase adjunta se deriva de otra clase, al permitir
que el programador actualice la clase anidada según sea necesario. Esto se puede combinar con
un typedef para proporcionar un nombre coherente para cada clase anidada de la clase
envolvente:
class BaseOuter {
struct BaseInner_ {
virtual void do_something() {}
virtual void do_something_else();
} b_in;
public:
typedef BaseInner_ Inner;
void BaseOuter::BaseInner_::do_something_else() {}
// ---
public:
typedef DerivedInner_ Inner;
void DerivedOuter::DerivedInner_::do_something_else() {}
// ...
// Calls BaseOuter::BaseInner_::do_something();
BaseOuter* b = new BaseOuter;
BaseOuter::Inner& bin = b->getInner();
bin.do_something();
b->getInner().do_something();
// Calls DerivedOuter::DerivedInner_::do_something();
BaseOuter* d = new DerivedOuter;
BaseOuter::Inner& din = d->getInner();
din.do_something();
d->getInner().do_something();
En el caso anterior, tanto BaseOuter como DerivedOuter suministran el tipo de miembro Inner , como
BaseInner_ y DerivedInner_ , respectivamente. Esto permite que los tipos anidados se deriven sin
romper la interfaz de la clase envolvente, y permite que el tipo anidado se utilice de forma
polimórfica.
https://riptutorial.com/es/home 110
Tipos de miembros y alias
Una class o struct también puede definir alias de tipo de miembro, que son alias de tipo
contenidos dentro de la clase y tratados como miembros de la misma clase.
struct IHaveATypedef {
typedef int MyTypedef;
};
struct IHaveATemplateTypedef {
template<typename T>
using MyTemplateTypedef = std::vector<T>;
};
Al igual que los miembros estáticos, se accede a estos typedefs usando el operador de alcance,
:: .
IHaveATypedef::MyTypedef i = 5; // i is an int.
IHaveATemplateTypedef::MyTemplateTypedef<int> v; // v is a std::vector<int>.
Al igual que con los alias de tipo normal, cada alias de tipo miembro puede referirse a cualquier
tipo definido o con alias antes, pero no después, de su definición. Del mismo modo, un typedef
fuera de la definición de clase puede referirse a cualquier typedef accesible dentro de la definición
de clase, siempre que venga después de la definición de clase.
template<typename T>
struct Helper {
T get() const { return static_cast<T>(42); }
};
struct IHaveTypedefs {
// typedef MyTypedef NonLinearTypedef; // Error if uncommented.
typedef int MyTypedef;
typedef Helper<MyTypedef> MyTypedefHelper;
};
Los alias de tipo de miembro se pueden declarar con cualquier nivel de acceso y respetarán el
modificador de acceso apropiado.
class TypedefAccessLevels {
typedef int PrvInt;
protected:
typedef int ProInt;
public:
typedef int PubInt;
https://riptutorial.com/es/home 111
};
Esto se puede usar para proporcionar un nivel de abstracción, permitiendo que el diseñador de
una clase cambie su funcionamiento interno sin romper el código que se basa en él.
class Something {
friend class SomeComplexType;
short s;
// ...
public:
typedef SomeComplexType MyHelper;
// ...
};
// ...
Something s;
Something::MyHelper hlp = s.get_helper();
En esta situación, si la clase auxiliar se cambia de SomeComplexType a algún otro tipo, solo será
necesario modificar el typedef y la declaración de friend ; Siempre que la clase auxiliar
proporcione la misma funcionalidad, cualquier código que lo use como Something::MyHelper lugar
de especificarlo por su nombre, por lo general funcionará sin ninguna modificación. De esta
manera, minimizamos la cantidad de código que debe modificarse cuando se cambia la
implementación subyacente, de modo que el nombre de tipo solo necesita cambiarse en una
ubicación.
class SomethingElse {
AnotherComplexType<bool, int, SomeThirdClass> helper;
public:
typedef decltype(helper) MyHelper;
private:
InternalVariable<MyHelper> ivh;
// ...
https://riptutorial.com/es/home 112
public:
MyHelper& get_helper() const { return helper; }
// ...
};
Como con todo, sin embargo, esto puede ser llevado demasiado lejos. Si el nombre de tipo solo
se usa una o dos veces internamente y cero veces externamente, por ejemplo, no es necesario
proporcionar un alias para él. Si se usa cientos o miles de veces a lo largo de un proyecto, o si
tiene un nombre lo suficientemente largo, entonces puede ser útil proporcionarlo como un typedef
en lugar de usarlo siempre en términos absolutos. Hay que equilibrar la compatibilidad y la
conveniencia con la cantidad de ruido innecesario creado.
Esto también se puede utilizar con clases de plantilla, para proporcionar acceso a los parámetros
de la plantilla desde fuera de la clase.
template<typename T>
class SomeClass {
// ...
public:
typedef T MyParam;
MyParam getParam() { return static_cast<T>(42); }
};
template<typename T>
typename T::MyParam some_func(T& t) {
return t.getParam();
}
SomeClass<int> si;
int i = some_func(si);
Esto se usa comúnmente con los contenedores, que normalmente proporcionarán su tipo de
elemento, y otros tipos de ayuda, como alias de tipo de miembro. La mayoría de los contenedores
en la biblioteca estándar de C ++, por ejemplo, proporcionan los siguientes 12 tipos de ayuda,
junto con cualquier otro tipo especial que puedan necesitar.
template<typename T>
class SomeContainer {
// ...
public:
// Let's provide the same helper types as most standard containers.
typedef T value_type;
typedef std::allocator<value_type> allocator_type;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef value_type* pointer;
https://riptutorial.com/es/home 113
typedef const value_type* const_pointer;
typedef MyIterator<value_type> iterator;
typedef MyConstIterator<value_type> const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
};
Antes de C ++ 11, también se usaba comúnmente para proporcionar un tipo de "plantilla typedef ",
ya que la característica aún no estaba disponible; se han vuelto un poco menos comunes con la
introducción de plantillas de alias, pero siguen siendo útiles en algunas situaciones (y se
combinan con plantillas de alias en otras situaciones, lo que puede ser muy útil para obtener
componentes individuales de un tipo complejo, como un puntero de función). ). Comúnmente
usan el type nombre para su alias de tipo.
template<typename T>
struct TemplateTypedef {
typedef T type;
}
TemplateTypedef<int>::type i; // i is an int.
Esto se usaba a menudo con tipos con múltiples parámetros de plantilla, para proporcionar un
alias que define uno o más de los parámetros.
template<typename T>
struct MonoDisplayLine {
typedef Array<T, 80, 1> type;
};
Una clase también puede tener miembros static , que pueden ser variables o funciones. Se
considera que estos están dentro del alcance de la clase, pero no se tratan como miembros
normales; tienen una duración de almacenamiento estático (existen desde el inicio del programa
hasta el final), no están vinculados a una instancia particular de la clase y solo existe una copia
https://riptutorial.com/es/home 114
para toda la clase.
class Example {
static int num_instances; // Static data member (static member variable).
int i; // Non-static member variable.
public:
static std::string static_str; // Static data member (static member variable).
static int static_func(); // Static member function.
int Example::num_instances;
std::string Example::static_str = "Hello.";
// ...
Las variables miembro estáticas no se consideran definidas dentro de la clase, solo se declaran, y
por lo tanto tienen su definición fuera de la definición de la clase; el programador puede, pero no
es obligatorio, inicializar variables estáticas en su definición. Al definir las variables miembro, se
omite la palabra clave static .
class Example {
static int num_instances; // Declaration.
public:
static std::string static_str; // Declaration.
// ...
};
Debido a esto, las variables estáticas pueden ser tipos incompletos (aparte del void ), siempre
que se definan más adelante como un tipo completo.
struct ForwardDeclared;
class ExIncomplete {
static ForwardDeclared fd;
static ExIncomplete i_contain_myself;
static int an_array[];
};
https://riptutorial.com/es/home 115
struct ForwardDeclared {};
ForwardDeclared ExIncomplete::fd;
ExIncomplete ExIncomplete::i_contain_myself;
int ExIncomplete::an_array[5];
Las funciones miembro estáticas se pueden definir dentro o fuera de la definición de clase, como
ocurre con las funciones miembro normales. Al igual que con las variables miembro estáticas, la
palabra clave static se omite al definir funciones miembro estáticas fuera de la definición de
clase.
public:
static int static_func() { return num_instances; }
// ...
// Or...
Si una variable miembro estática se declara const pero no es volatile , y es de tipo integral o de
enumeración, se puede inicializar en declaración, dentro de la definición de clase.
enum E { VAL = 5 };
struct ExConst {
const static int ci = 5; // Good.
static const E ce = VAL; // Good.
const static double cd = 5; // Error.
static const volatile int cvi = 5; // Error.
C ++ 11
A partir de C ++ 11, las variables miembro estáticas de tipos LiteralType (tipos que pueden
construirse en tiempo de compilación, de acuerdo con constexpr reglas constexpr ) también
pueden declararse como constexpr ; si es así, deben inicializarse dentro de la definición de clase.
https://riptutorial.com/es/home 116
struct ExConstexpr {
constexpr static int ci = 5; // Good.
static constexpr double cd = 5; // Good.
constexpr static int carr[] = { 1, 1, 2 }; // Good.
static constexpr ConstexprConstructibleClass c{}; // Good.
constexpr static int bad_ci; // Error.
};
Si una variable de miembro estática const o constexpr se usa de forma estándar (de manera
informal, si tiene su dirección tomada o está asignada a una referencia), todavía debe tener una
definición separada, fuera de la definición de clase. Esta definición no puede contener un
inicializador.
struct ExODR {
static const int odr_used = 5;
};
const int* odr_user = & ExODR::odr_used; // Error; uncomment above line to resolve.
Como los miembros estáticos no están vinculados a una instancia determinada, se puede acceder
a ellos utilizando el operador de alcance, :: .
También se puede acceder a ellos como si fueran miembros normales, no estáticos. Esto es de
importancia histórica, pero se usa con menos frecuencia que el operador de alcance para evitar
confusiones sobre si un miembro es estático o no estático.
Example ex;
std::string rts = ex.static_str;
Los miembros de la clase pueden acceder a miembros estáticos sin calificar su alcance, al igual
que con los miembros de clase no estáticos.
class ExTwo {
static int num_instances;
int my_num;
public:
ExTwo() : my_num(num_instances++) {}
int ExTwo::num_instances;
https://riptutorial.com/es/home 117
No pueden ser mutable , ni deberían serlo; como no están vinculados a ninguna instancia dada, si
una instancia es o no const no afecta a los miembros estáticos.
struct ExDontNeedMutable {
int immuta;
mutable int muta;
static int i;
// ...
Los miembros estáticos respetan los modificadores de acceso, al igual que los miembros no
estáticos.
class ExAccess {
static int prv_int;
protected:
static int pro_int;
public:
static int pub_int;
};
int ExAccess::prv_int;
int ExAccess::pro_int;
int ExAccess::pub_int;
// ...
Como no están vinculados a una instancia determinada, las funciones miembro estáticas no
tienen this puntero; Debido a esto, no pueden acceder a las variables miembro no estáticas a
menos que se pase una instancia.
class ExInstanceRequired {
int i;
public:
ExInstanceRequired() : i(0) {}
https://riptutorial.com/es/home 118
static void good_mutate(ExInstanceRequired& e) { ++e.i *= 5; } // Good.
};
struct ExPointer {
void nsfunc() {}
static void sfunc() {}
};
Debido a no tener un this puntero, sino que también no pueden ser const o volatile , ni pueden
tener Ref-calificadores. Tampoco pueden ser virtuales.
struct ExCVQualifiersAndVirtual {
static void func() {} // Good.
static void cfunc() const {} // Error.
static void vfunc() volatile {} // Error.
static void cvfunc() const volatile {} // Error.
static void rfunc() & {} // Error.
static void rvfunc() && {} // Error.
Como no están vinculados a una instancia determinada, las variables miembro estáticas se tratan
efectivamente como variables globales especiales; se crean cuando se inicia el programa y se
destruyen cuando se sale, independientemente de si existe alguna instancia de la clase. Solo
existe una copia de cada variable miembro estática (a menos que la variable se declare
thread_local (C ++ 11 o posterior), en cuyo caso hay una copia por subproceso).
Las variables miembro estáticas tienen el mismo enlace que la clase, ya sea que la clase tenga
enlace externo o interno. Las clases locales y las clases sin nombre no tienen permitido tener
miembros estáticos.
Una clase puede tener funciones miembro no estáticas , que operan en instancias individuales de
la clase.
class CL {
public:
void member_function() {}
};
https://riptutorial.com/es/home 119
Estas funciones se llaman en una instancia de la clase, como así:
CL instance;
instance.member_function();
struct ST {
void defined_inside() {}
void defined_outside();
};
void ST::defined_outside() {}
Pueden ser calificados para CV y / o ref , lo que afecta la forma en que ven la instancia a la que
se les solicita; la función considerará que la instancia tiene el calificador (es) cv especificado, si
corresponde. La versión que se llame se basará en los calificadores cv de la instancia. Si no hay
una versión con los mismos calificadores de CV que la instancia, se llamará una versión más
calificada de CV, si está disponible.
struct CVQualifiers {
void func() {} // 1: Instance is non-cv-qualified.
void func() const {} // 2: Instance is const.
CVQualifiers non_cv_instance;
const CVQualifiers c_instance;
C ++ 11
struct RefQualifiers {
void func() & {} // 1: Called on normal instances.
void func() && {} // 2: Called on rvalue (temporary) instances.
};
RefQualifiers rf;
rf.func(); // Calls #1.
RefQualifiers{}.func(); // Calls #2.
struct BothCVAndRef {
https://riptutorial.com/es/home 120
void func() const& {} // Called on normal instances. Sees instance as const.
void func() && {} // Called on temporary instances.
};
También pueden ser virtuales ; esto es fundamental para el polimorfismo, y permite que una (s)
clase (s) infantil (es) proporcione la misma interfaz que la clase primaria, al tiempo que
proporciona su propia funcionalidad.
struct Base {
virtual void func() {}
};
struct Derived {
virtual void func() {}
};
void foo()
{
struct /* No name */ {
float x;
float y;
} point;
point.x = 42;
}
struct Circle
{
struct /* No name */ {
float x;
float y;
} center; // but a member name
float radius;
};
y después
Circle circle;
circle.center.x = 42.f;
https://riptutorial.com/es/home 121
struct InvalidCircle
{
struct /* No name */ {
float centerX;
float centerY;
}; // No member either.
float radius;
};
C ++ 11
• lamdba puede ser visto como una struct especial sin nombre .
decltype(circle.point) otherPoint;
• La instancia de struct sin nombre puede ser un parámetro del método de plantilla:
void print_square_coordinates()
{
const struct {float x; float y;} points[] = {
{-1, -1}, {-1, 1}, {1, -1}, {1, 1}
};
https://riptutorial.com/es/home 122
Capítulo 18: Clasificación
Observaciones
La familia de funciones std::sort se encuentra en la biblioteca de algorithm .
Examples
Clasificación de contenedores de secuencia con orden específico
C ++ 11
#include <vector>
#include <algorithm>
#include <functional>
std::vector<int> v = {5,1,2,4,3};
// Or just:
std::sort(v.begin(), v.end());
//Or just:
std::sort(v.rbegin(), v.rend());
C ++ 14
https://riptutorial.com/es/home 123
Podemos sobrecargar a este operador para que la llamada de sort predeterminada funcione en
tipos definidos por el usuario.
class Base {
public:
int variable;
};
int main() {
std::vector <Base> vector;
std::deque <Base> deque;
std::list <Base> list;
deque.push_back(a);
deque.push_back(b);
list.push_back(a);
list.push_back(b);
return 0;
}
https://riptutorial.com/es/home 124
// Include sequence containers
#include <vector>
#include <deque>
#include <list>
class Base {
public:
int variable;
};
int main() {
std::vector <Base> vector;
std::deque <Base> deque;
std::list <Base> list;
deque.push_back(a);
deque.push_back(b);
list.push_back(a);
list.push_back(b);
return 0;
}
C ++ 11
https://riptutorial.com/es/home 125
#include <forward_list>
class Base {
public:
int variable;
};
int main() {
// Create 2 elements to sort
Base a(10);
Base b(5);
return 0;
}
La función de comparación debe imponer un ordenamiento estricto y débil en los elementos. Una
simple comparación menor que (o mayor que) será suficiente.
C ++ 11
https://riptutorial.com/es/home 126
#include <vector>
#include <algorithm>
std::sort requiere que sus iteradores sean iteradores de acceso aleatorio. Los contenedores de
secuencias std::list y std::forward_list (que requieren C ++ 11) no proporcionan iteradores de
acceso aleatorio, por lo que no pueden usarse con std::sort . Sin embargo, tienen funciones de
miembro de sort que implementan un algoritmo de clasificación que funciona con sus propios
tipos de iteradores.
C ++ 11
#include <list>
#include <algorithm>
Sus funciones de sort miembros siempre ordenan la lista completa, por lo que no pueden ordenar
un sub-rango de elementos. Sin embargo, dado que list y forward_list tienen operaciones de
empalme rápidas, puede extraer los elementos que se ordenarán de la lista, ordenarlos y luego
volver a guardarlos donde fueron más eficientemente así:
Este ejemplo ordena los elementos en orden ascendente de una clave utilizando un mapa.
Puede usar cualquier tipo, incluida la clase, en lugar de std::string , en el siguiente ejemplo.
#include <iostream>
#include <utility>
#include <map>
int main()
{
std::map<double, std::string> sorted_map;
// Sort the names of the planets according to their size
sorted_map.insert(std::make_pair(0.3829, "Mercury"));
https://riptutorial.com/es/home 127
sorted_map.insert(std::make_pair(0.9499, "Venus"));
sorted_map.insert(std::make_pair(1, "Earth"));
sorted_map.insert(std::make_pair(0.532, "Mars"));
sorted_map.insert(std::make_pair(10.97, "Jupiter"));
sorted_map.insert(std::make_pair(9.14, "Saturn"));
sorted_map.insert(std::make_pair(3.981, "Uranus"));
sorted_map.insert(std::make_pair(3.865, "Neptune"));
Salida:
Si son posibles entradas con claves iguales, use el multimap lugar del map (como en el siguiente
ejemplo).
Para ordenar los elementos de forma descendente , declare el mapa con un funtor de
comparación adecuado ( std::greater<> ):
#include <iostream>
#include <utility>
#include <map>
int main()
{
std::multimap<int, std::string, std::greater<int>> sorted_map;
// Sort the names of animals in descending order of the number of legs
sorted_map.insert(std::make_pair(6, "bug"));
sorted_map.insert(std::make_pair(4, "cat"));
sorted_map.insert(std::make_pair(100, "centipede"));
sorted_map.insert(std::make_pair(2, "chicken"));
sorted_map.insert(std::make_pair(0, "fish"));
sorted_map.insert(std::make_pair(4, "horse"));
sorted_map.insert(std::make_pair(8, "spider"));
Salida
https://riptutorial.com/es/home 128
spider (has 8 legs)
bug (has 6 legs)
cat (has 4 legs)
horse (has 4 legs)
chicken (has 2 legs)
fish (has 0 legs)
El algoritmo de sort ordena una secuencia definida por dos iteradores. Esto es suficiente para
ordenar una matriz integrada (también conocida como c-style).
C ++ 11
C ++ 11
https://riptutorial.com/es/home 129
Capítulo 19: Comparaciones lado a lado de
ejemplos clásicos de C ++ resueltos a través
de C ++ vs C ++ 11 vs C ++ 14 vs C ++ 17
Examples
Buceando a través de un contenedor
Aunque simples, tales escritos están sujetos a errores semánticos comunes, como un operador
de comparación incorrecto o una variable de indexación incorrecta:
El bucle también se puede lograr para todos los contenedores que utilizan iteradores, con
inconvenientes similares:
C ++ 11 introdujo rango basado en bucles y palabras clave auto , permitiendo que el código se
convierta en:
for(auto& x : c) x = 0;
Aquí los únicos parámetros son el contenedor c y una variable x para mantener el valor actual.
Esto evita los errores semánticos apuntados previamente.
En dicha implementación, la expresión auto begin = c.begin(), end = c.end(); las fuerzas begin y
end para ser del mismo tipo, mientras que el end nunca se incrementa, ni se elimina la referencia.
Por lo tanto, el bucle for basado en rango solo funciona para contenedores definidos por un par
iterador / iterador. El estándar C ++ 17 relaja esta restricción al cambiar la implementación a:
https://riptutorial.com/es/home 130
auto begin = c.begin();
auto end = c.end();
for(; begin != end; ++begin)
{
// ...
}
Aquí se permite que el begin y el end sean de diferentes tipos, siempre que se puedan comparar
por desigualdad. Esto permite recorrer más contenedores, por ejemplo, un contenedor definido
por un par iterador / centinela.
https://riptutorial.com/es/home 131
Capítulo 20: Compilando y construyendo
Introducción
Los programas escritos en C ++ deben compilarse antes de que puedan ejecutarse. Hay una gran
variedad de compiladores disponibles dependiendo de su sistema operativo.
Observaciones
La mayoría de los sistemas operativos se envían sin un compilador, y deben instalarse más
adelante. Algunas opciones comunes de compiladores son:
Por favor, consulte el manual del compilador apropiado, sobre cómo compilar un programa C ++.
Otra opción para usar un compilador específico con su propio sistema de compilación específico,
es posible permitir que los sistemas de compilación genéricos configuren el proyecto para un
compilador específico o para el instalado por defecto.
Examples
Compilando con GCC
Asumiendo un único archivo fuente llamado main.cpp , el comando para compilar y vincular un
ejecutable no optimizado es el siguiente (Compilar sin optimización es útil para el desarrollo inicial
y la depuración, aunque oficialmente se recomienda -Og para las versiones más recientes de
GCC).
Para producir un ejecutable optimizado para su uso en la producción, utilizar una de las -O
opciones (véase: -O1 , -O2 , -O3 , -Os , -Ofast ):
Si se omite la opción -O, se utiliza -O0, que significa que no hay optimizaciones, como valor
predeterminado (la especificación de -O sin un número se resuelve en -O1).
https://riptutorial.com/es/home 132
g++ -o app -Wall -O2 -ftree-partial-pre main.cpp
Cualquiera de los anteriores producirá un archivo binario que puede ejecutarse con .\app.exe en
Windows y ./app en Linux, Mac OS, etc.
La bandera -o también se puede omitir. En este caso, GCC creará una salida ejecutable
predeterminada a.exe en Windows y a.out en sistemas similares a Unix. Para compilar un archivo
sin vincularlo, use la opción -c :
Esto produce un archivo de objeto llamado file.o que luego puede vincularse con otros archivos
para producir un binario:
La bandera -Wall habilita advertencias para muchos errores comunes y siempre debe usarse.
Para mejorar la calidad del código, a menudo también se recomienda utilizar -Wextra y otros
indicadores de advertencia que no están habilitados automáticamente por -Wall y -Wextra .
GCC incluye algunas extensiones específicas del compilador que están deshabilitadas cuando
entran en conflicto con un estándar especificado por el -std= . Para compilar con todas las
extensiones habilitadas, se puede utilizar el valor gnu++XX , donde XX es cualquiera de los años
utilizados por los valores de c++ enumerados anteriormente.
Tenga en cuenta que debido a errores en GCC, el indicador -pthread debe estar presente en la
https://riptutorial.com/es/home 133
compilación y enlace para que GCC admita la funcionalidad de subprocesamiento estándar C ++
introducida con C ++ 11, como std::thread y std::wait_for . Omitirlo cuando se usan funciones de
subprocesamiento puede generar advertencias pero resultados no válidos en algunas
plataformas.
O deje que el enlazador determine el pedido a través de --start-group y --end-group (nota: esto
tiene un costo de rendimiento significativo):
Para los programadores que vienen de GCC o Clang a Visual Studio, o los programadores que se
sienten más cómodos con la línea de comandos en general, puede usar el compilador de Visual C
++ desde la línea de comandos así como el IDE.
Si desea compilar su código desde la línea de comandos en Visual Studio, primero debe
configurar el entorno de línea de comandos. Esto se puede hacer abriendo la línea de Visual
Studio Command Prompt / Developer Command Prompt / x86 Native Tools Command Prompt / x64 Native
Tools Command Prompt o similar (según lo provisto por su versión de Visual Studio), o en la línea de
comandos, navegando a el subdirectorio VC directorio de instalación del compilador (normalmente
\Program Files (x86)\Microsoft Visual Studio x\VC , donde x es el número de versión (como 10.0
para 2010 o 14.0 para 2015) y ejecuta el archivo por lotes VCVARSALL con una parámetro de línea
de comando especificado aquí .
Tenga en cuenta que a diferencia de GCC, Visual Studio no proporciona un front-end para el
https://riptutorial.com/es/home 134
vinculador ( link.exe ) a través del compilador ( cl.exe ), sino que proporciona el vinculador como
un programa separado, al que el compilador llama cuando sale. cl.exe y link.exe se pueden usar
por separado con diferentes archivos y opciones, o se puede indicar a cl que pase archivos y
opciones para link si ambas tareas se realizan juntas. Cualquier opción de enlace especificada
para cl se convertirá en opciones para link , y cualquier archivo que no sea procesado por cl
pasará directamente al link . Como esta es principalmente una guía simple para compilar con la
línea de comandos de Visual Studio, los argumentos para el link no se describirán en este
momento; Si necesita una lista, vea aquí .
Tenga en cuenta que los argumentos para cl distinguen entre mayúsculas y minúsculas, mientras
que los argumentos para link no lo son.
[Tenga en cuenta que algunos de los siguientes ejemplos utilizan la variable "directorio actual" del
shell de Windows, %cd% , al especificar nombres de ruta absolutos. Para cualquier persona que no
esté familiarizada con esta variable, se expande al directorio de trabajo actual. Desde la línea de
comandos, será el directorio en el que estaba cuando ejecutó cl , y se especifica en el símbolo
del sistema de manera predeterminada (si su símbolo del sistema es C:\src> , por ejemplo,
entonces %cd% es C:\src\ ).]
Suponiendo que un solo archivo de origen denominado main.cpp en la carpeta actual, el comando
para compilar y vincular un ejecutable no optimizado (útil para el desarrollo inicial y la depuración)
es (use uno de los siguientes):
cl main.cpp
// Generates object file "main.obj".
// Performs linking with "main.obj".
// Generates executable "main.exe".
cl /Od main.cpp
// Same as above.
// "/Od" is the "Optimisation: disabled" option, and is the default when no /O is specified.
cl main.cpp niam.cpp
// Generates object files "main.obj" and "niam.obj".
// Performs linking with "main.obj" and "niam.obj".
// Generates executable "main.exe".
cl main.cpp src\*.cpp
// Generates object file "main.obj", plus one object file for each ".cpp" file in folder
// "%cd%\src".
// Performs linking with "main.obj", and every additional object file generated.
// All object files will be in the current folder.
// Generates executable "main.exe".
https://riptutorial.com/es/home 135
cl /o name main.cpp
// Generates executable named "name.exe".
cl /o folder\ main.cpp
// Generates executable named "main.exe", in folder "%cd%\folder".
cl /o folder\name main.cpp
// Generates executable named "name.exe", in folder "%cd%\folder".
cl /Fename main.cpp
// Same as "/o name".
cl /Fefolder\ main.cpp
// Same as "/o folder\".
cl /Fefolder\name main.cpp
// Same as "/o folder\name".
Tanto /o como /Fe pasan su parámetro (llamémoslo o-param ) para link como /OUT:o-param ,
agregando la extensión apropiada (generalmente .exe o .dll ) para "nombrar" o-param según sea
necesario. Mientras tanto /o y /Fe son, en mi opinión, idénticas en funcionalidad, esta última es la
preferida para Visual Studio. /o está marcado como obsoleto y parece que se proporciona
principalmente para programadores más familiarizados con GCC o Clang.
De manera similar, para producir un ejecutable optimizado (para uso en producción), use:
cl /O1 main.cpp
// Optimise for executable size. Produces small programs, at the possible expense of slower
// execution.
cl /O2 main.cpp
// Optimise for execution speed. Produces fast programs, at the possible expense of larger
// file size.
// If compiling for x64, and LINK doesn't automatically detect target platform:
https://riptutorial.com/es/home 136
cl main.cpp /link /machine:X64
Cualquiera de los anteriores producirá un ejecutable con el nombre especificado por /o o /Fe , o si
no se proporciona ninguno, con un nombre idéntico al primer archivo de origen u objeto
especificado para el compilador.
cl /o yo zp.obj pz.cpp
// Generates "yo.exe".
cl /c main.cpp
// Generates object file "main.obj".
Esto le dice a cl que salga sin llamar al link , y produce un archivo objeto, que luego puede
vincularse con otros archivos para producir un binario.
cl main.obj niam.cpp
// Generates object file "niam.obj".
// Performs linking with "main.obj" and "niam.obj".
// Generates executable "main.exe".
También hay otros parámetros de línea de comando valiosos, que sería muy útil para los usuarios
saber:
cl /EHsc main.cpp
// "/EHsc" specifies that only standard C++ ("synchronous") exceptions will be caught,
// and `extern "C"` functions will not throw exceptions.
// This is recommended when writing portable, platform-independent code.
cl /clr main.cpp
// "/clr" specifies that the code should be compiled to use the common language runtime,
// the .NET Framework's virtual machine.
// Enables the use of Microsoft's C++/CLI language in addition to standard ("native") C++,
// and creates an executable that requires .NET to run.
cl /Za main.cpp
// "/Za" specifies that Microsoft extensions should be disabled, and code should be
// compiled strictly according to ISO C++ specifications.
// This is recommended for guaranteeing portability.
https://riptutorial.com/es/home 137
cl /Zi main.cpp
// "/Zi" generates a program database (PDB) file for use when debugging a program, without
// affecting optimisation specifications, and passes the option "/DEBUG" to LINK.
cl /LD dll.cpp
// "/LD" tells CL to configure LINK to generate a DLL instead of an executable.
// LINK will output a DLL, in addition to an LIB and EXP file for use when linking.
// To use the DLL in other programs, pass its associated LIB to CL or LINK when compiling
those
// programs.
Para cualquiera que esté más familiarizado con los sistemas * nix y / o GCC / Clang, cl , link y
otras herramientas de línea de comandos de Visual Studio puede aceptar parámetros
especificados con un guión (como -c ) en lugar de una barra (como /c ). Además, Windows
reconoce una barra diagonal o una barra diagonal inversa como un separador de ruta válido, por
lo que también se pueden usar las rutas estilo * nix. Esto facilita la conversión de líneas de
comando del compilador simple de g++ o clang++ a cl , o viceversa, con cambios mínimos.
Por supuesto, cuando se transfieren líneas de comando que usan opciones más complejas de g++
o clang++ , debe buscar comandos equivalentes en la documentación del compilador
correspondiente y / o en los sitios de recursos, pero esto hace que sea más fácil comenzar con un
tiempo mínimo de aprendizaje. Nuevos compiladores.
En caso de que necesite funciones de idioma específicas para su código, se requiere una versión
específica de MSVC. Desde Visual C ++ 2015 Update 3 , es posible elegir la versión del estándar
para compilar a través de la bandera /std . Los valores posibles son /std:c++14 y /std:c++latest (
/std:c++17 seguirá pronto).
https://riptutorial.com/es/home 138
4. Haga clic en Plantillas -> Visual C ++ -> Aplicación de consola Win32 y luego nombre el
proyecto MyFirstProgram .
https://riptutorial.com/es/home 139
7. Marque la casilla Empty project y luego haga clic en Finalizar:
https://riptutorial.com/es/home 140
8. Haga clic derecho en la carpeta Archivo de origen y luego -> Agregar -> Nuevo elemento:
https://riptutorial.com/es/home 141
9. Seleccione Archivo C ++ y nombre el archivo main.cpp, luego haga clic en Agregar:
https://riptutorial.com/es/home 142
10: Copie y pegue el siguiente código en el nuevo archivo main.cpp:
#include <iostream>
int main()
{
std::cout << "Hello World!\n";
return 0;
}
https://riptutorial.com/es/home 143
11. Haga clic en Depurar -> Iniciar sin depurar (o presione ctrl + F5):
https://riptutorial.com/es/home 144
Compilando con Clang
Como la interfaz de Clang está diseñada para ser compatible con GCC, la mayoría de los
programas que se pueden compilar a través de GCC se compilarán cuando intercambies g++ por
clang++ en los scripts de compilación. Si no se -std=version , se utilizará gnu11.
Los usuarios de Windows que están acostumbrados a MSVC pueden intercambiar cl.exe con
clang-cl.exe . De forma predeterminada, clang intenta ser compatible con la versión más alta de
MSVC que se ha instalado.
En el caso de compilar con visual studio, clang-cl puede usarse cambiando el Platform toolset de
Platform toolset la Platform toolset en las propiedades del proyecto.
En ambos casos, clang solo es compatible a través de su interfaz, aunque también intenta
generar archivos de objetos compatibles binarios. Los usuarios de clang-cl deben tener en cuenta
que la compatibilidad con MSVC aún no está completa .
Para usar clang o clang-cl, uno podría usar la instalación predeterminada en ciertas distribuciones
de Linux o las que vienen con IDE (como XCode en Mac). Para otras versiones de este
compilador o en plataformas que no lo tienen instalado, se puede descargar desde la página de
descarga oficial .
Si está utilizando CMake para compilar su código, generalmente puede cambiar el compilador
configurando las variables de entorno CC y CXX esta manera:
mkdir build
cd build
CC=clang CXX=clang++ cmake ..
cmake --build .
https://riptutorial.com/es/home 145
Compiladores en linea
Varios sitios web proporcionan acceso en línea a compiladores de C ++. El conjunto de funciones
del compilador en línea varía significativamente de un sitio a otro, pero generalmente permiten
hacer lo siguiente:
El comportamiento del sitio web del compilador en línea suele ser bastante restrictivo, ya que
permite que cualquier persona ejecute compiladores y ejecute código arbitrario en su servidor,
mientras que la ejecución de código arbitrario a distancia se considera una vulnerabilidad.
Los compiladores en línea pueden ser útiles para los siguientes propósitos:
• Ejecute un pequeño fragmento de código desde una máquina que carece de compilador de
C ++ (teléfonos inteligentes, tabletas, etc.).
• Asegúrese de que el código se compile correctamente con diferentes compiladores y se
ejecute de la misma manera, independientemente del compilador con el que se compiló.
• Aprender o enseñar conceptos básicos de C ++.
• Conozca las funciones modernas de C ++ (C ++ 14 y C ++ 17 en un futuro próximo) cuando
el compilador C ++ actualizado no esté disponible en la máquina local.
• Detecta un error en tu compilador en comparación con un gran conjunto de otros
compiladores. Compruebe si se solucionó un error del compilador en futuras versiones, que
no están disponibles en su máquina.
• Resolver problemas de jueces en línea.
• Desarrolle aplicaciones completas (incluso pequeñas) utilizando C ++. Por lo general, los
compiladores en línea no permiten enlazar con bibliotecas de terceros o descargar
artefactos de compilación.
• Realizar cálculos intensivos. Los recursos informáticos del lado del servidor son limitados,
por lo que cualquier programa proporcionado por el usuario se eliminará después de unos
segundos de ejecución. El tiempo de ejecución permitido suele ser suficiente para la prueba
y el aprendizaje.
• Atacar el servidor del compilador o cualquier otro host de terceros en la red.
Ejemplos:
https://riptutorial.com/es/home 146
comando. Proporciona compiladores de GCC y Clang para su uso.
• http://cpp.sh/ - Compilador en línea con soporte para C ++ 14. No le permite editar la línea
de comandos del compilador, pero algunas opciones están disponibles a través de los
controles GUI.
• https://gcc.godbolt.org/ : proporciona una amplia lista de versiones de compilador,
arquitecturas y resultados de desensamblaje. Muy útil cuando necesitas inspeccionar lo que
compila tu código por diferentes compiladores. GCC, Clang, MSVC ( CL ), compilador Intel (
icc ), ELLCC y Zapcc están presentes, con uno o más de estos compiladores disponibles
para ARM, ARMv8 (como ARM64), AVR de Atmel, MIPS, MIPS64, MSP430, PowerPC ,
x86, y x64 architecutres. Los argumentos de la línea de comando del compilador pueden ser
editados.
• https://ideone.com/ : se utiliza ampliamente en la red para ilustrar el comportamiento del
fragmento de código. Proporciona GCC y Clang para su uso, pero no le permite editar la
línea de comandos del compilador.
• http://melpon.org/wandbox - Admite numerosas versiones de compilador Clang y GNU /
GCC.
• http://onlinegdb.com/ : un IDE extremadamente minimalista que incluye un editor, un
compilador (gcc) y un depurador (gdb).
• http://rextester.com/ : proporciona compiladores de Clang, GCC y Visual Studio para C y C
++ (junto con compiladores para otros idiomas), con la biblioteca Boost disponible para su
uso.
• http://tutorialspoint.com/compile_cpp11_online.php : shell UNIX con todas las funciones con
GCC y un explorador de proyectos fácil de usar.
• http://webcompiler.cloudapp.net/ - Compilador en línea de Visual Studio 2015,
proporcionado por Microsoft como parte de RiSE4fun.
https://riptutorial.com/es/home 147
4. El archivo de código de objeto producido por el ensamblador está vinculado entre sí
con los archivos de código de objeto para cualquier función de biblioteca utilizada para
producir una biblioteca o un archivo ejecutable.
Preprocesamiento
Después de todo esto, el preprocesador produce una salida única que es una secuencia de
tokens resultantes de las transformaciones descritas anteriormente. También agrega algunos
marcadores especiales que le dicen al compilador de dónde proviene cada línea para que pueda
usarlos para producir mensajes de error razonables.
Algunos errores pueden producirse en esta etapa con un uso inteligente de las directivas #if y
#error.
g++ -E prog.cpp
Compilacion
Los archivos de objetos pueden referirse a símbolos que no están definidos. Este es el caso
cuando usa una declaración y no proporciona una definición para ella. Al compilador no le importa
esto, y felizmente producirá el archivo de objeto siempre que el código fuente esté bien formado.
Los compiladores generalmente le permiten detener la compilación en este punto. Esto es muy
útil porque con él puede compilar cada archivo de código fuente por separado. La ventaja que
esto proporciona es que no necesita recompilar todo si solo cambia un solo archivo.
https://riptutorial.com/es/home 148
Los archivos de objetos producidos se pueden colocar en archivos especiales denominados
bibliotecas estáticas, para luego reutilizarlos más fácilmente.
Es en esta etapa que se informan los errores "regulares" del compilador, como los errores de
sintaxis o los errores de resolución de sobrecarga fallidos.
Para detener el proceso después del paso de compilación, podemos usar la opción -S:
Montaje
El ensamblador crea código objeto. En un sistema UNIX puede ver archivos con un sufijo .o (.OBJ
en MSDOS) para indicar archivos de código de objeto. En esta fase, el ensamblador convierte
esos archivos de objeto de código de ensamblaje en instrucciones a nivel de máquina y el archivo
creado es un código de objeto reubicable. Por lo tanto, la fase de compilación genera el programa
de objeto reubicable y este programa se puede utilizar en diferentes lugares sin tener que
compilar nuevamente.
Para detener el proceso después del paso de ensamblaje, puede usar la opción -c:
Enlace
El enlazador es lo que produce la salida de compilación final de los archivos de objetos que
produjo el ensamblador. Esta salida puede ser una biblioteca compartida (o dinámica) (y aunque
el nombre es similar, no tienen mucho en común con las bibliotecas estáticas mencionadas
anteriormente) o un archivo ejecutable.
Vincula todos los archivos de objetos reemplazando las referencias a símbolos no definidos con
las direcciones correctas. Cada uno de estos símbolos se puede definir en otros archivos de
objetos o en bibliotecas. Si están definidas en bibliotecas distintas de la biblioteca estándar, debe
informar al vinculador sobre ellas.
En esta etapa, los errores más comunes son las definiciones faltantes o las definiciones
duplicadas. Lo primero significa que las definiciones no existen (es decir, no están escritas), o que
los archivos de objetos o las bibliotecas donde residen no se entregaron al vinculador. Lo último
es obvio: el mismo símbolo se definió en dos archivos de objetos o bibliotecas diferentes.
1. Descargue e instale Code :: Blocks aquí . Si está en Windows, tenga cuidado de seleccionar
un archivo cuyo nombre contenga mingw , los otros archivos no instalarán ningún compilador.
https://riptutorial.com/es/home 149
https://riptutorial.com/es/home 150
3. Seleccione "Aplicación de consola" y haga clic en "Ir":
4. Haga clic en "Siguiente", seleccione "C ++", haga clic en "Siguiente", seleccione un nombre
para su proyecto y elija una carpeta para guardarlo, haga clic en "Siguiente" y luego haga
clic en "Finalizar".
5. Ahora puedes editar y compilar tu código. Un código predeterminado que imprime "¡Hola
mundo!" En la consola ya está ahí. Para compilar y / o ejecutar su programa, presione uno
de los tres botones de compilar / ejecutar en la barra de herramientas:
https://riptutorial.com/es/home 151
https://riptutorial.com/es/home 152
Para compilar sin correr, pulsa , para correr sin compilar nuevamente, presione y
para compilar y luego correr, presiona .
https://riptutorial.com/es/home 153
Lea Compilando y construyendo en línea:
https://riptutorial.com/es/cplusplus/topic/4708/compilando-y-construyendo
https://riptutorial.com/es/home 154
Capítulo 21: Comportamiento definido por la
implementación
Examples
Char puede estar sin firmar o firmado
El estándar no especifica si char debe estar firmado o sin firmar. Diferentes compiladores lo
implementan de manera diferente, o podrían permitir cambiarlo usando un interruptor de línea de
comando.
• char
• Tipos enteros firmados
• Tipos de enteros sin signo
• char16_t y char32_t
• bool
• wchar_t
Tamaño de char
Todas las versiones de estándar el C ++ especifican, en § 5.3.3.1, que sizeof rendimientos 1 para
unsigned char , signed char , y char (es definido por la implementación si el char tipo se signed o
unsigned ).
C ++ 14
chares lo suficientemente grande como para representar 256 valores diferentes, para ser
adecuado para almacenar unidades de código UTF-8.
https://riptutorial.com/es/home 155
El estándar especifica, en el § 3.9.1.2, que en la lista de tipos enteros con signo estándar , que
constan de caracteres con signed char , short int , int , long int long long int , cada tipo
proporcionará al menos tanto almacenamiento como los anteriores en la lista. Además, como se
especifica en el § 3.9.1.3, cada uno de estos tipos tiene un tipo de entero sin signo estándar
correspondiente, unsigned char unsigned short int unsigned int , unsigned short int unsigned int ,
unsigned int unsigned long int , unsigned long int unsigned long long int , que tiene el mismo
tamaño y alineación que Su correspondiente tipo firmado. Además, como se especifica en el §
3.9.1.1, char tiene los mismos requisitos de tamaño y alineación que el signed char y el unsigned
char .
C ++ 11
Antes de C ++ 11, long long y unsigned long long no formaban parte oficialmente del estándar C
++. Sin embargo, después de su introducción a C, en C99, muchos compiladores admitieron long
long como un tipo entero con signo extendido , y unsigned long long como un tipo entero sin signo
extendido , con las mismas reglas que los tipos C.
C ++ 11
Los tamaños mínimos específicos para cada tipo no están dados por la norma. En su lugar, cada
tipo tiene un rango mínimo de valores que puede admitir, que, como se especifica en § 3.9.1.3, se
hereda del estándar C, en §5.2.4.2.1. El tamaño mínimo de cada tipo se puede inferir
aproximadamente de este rango, al determinar el número mínimo de bits requeridos; tenga en
cuenta que para cualquier plataforma dada, el rango real admitido de cualquier tipo puede ser
mayor que el mínimo. Tenga en cuenta que para los tipos con signo, los rangos corresponden al
complemento de uno, no al complemento de dos de uso más común; esto es para permitir que
una gama más amplia de plataformas cumpla con el estándar.
Bits mínimos
Tipo Rango mínimo
requeridos
unsigned
short 0 a 65,535 (0 a 2 16 - 1) dieciséis
https://riptutorial.com/es/home 156
Bits mínimos
Tipo Rango mínimo
requeridos
-2,147,483,647 a 2,147,483,647 (- (2 31 - 1) a (2
signed long
31 - 1)) 32
unsigned long
long 0 a 18,446,744,073,709,551,615 (0 a 2 64 - 1) 64
Como se permite que cada tipo sea mayor que su requisito de tamaño mínimo, los tipos pueden
diferir en tamaño entre las implementaciones. El ejemplo más notable de esto es con los modelos
de datos de 64 bits LP64 y LLP64, donde los sistemas LLP64 (tales como Windows de 64 bits)
tiene 32 bits ints y long s, y sistemas de LP64 (tales como Linux de 64 bits) tienen int s de 32 bits
y s de 64 bits de long . Debido a esto, no se puede suponer que los tipos de enteros tengan un
ancho fijo en todas las plataformas.
C ++ 11
Si se requieren tipos de enteros con ancho fijo, use tipos del encabezado <cstdint> , pero tenga
en cuenta que el estándar hace que las implementaciones sean compatibles con los tipos de
ancho exacto int8_t , int16_t , int32_t , int64_t , intptr_t , uint8_t , uint16_t , uint32_t , uint64_t y
uintptr_t .
C ++ 11
Los tamaños de char16_t y char32_t están definidos por la implementación, como se especifica en
el § 5.3.3.1, con las estipulaciones que figuran en el § 3.9.1.5:
https://riptutorial.com/es/home 157
Tamaño de bool
Tamaño de wchar_t
wchar_t , como se especifica en § 3.9.1.5, es un tipo distinto, cuyo rango de valores puede
representar cada unidad de código distinta del conjunto de caracteres extendido más grande
entre los locales admitidos. Tiene el mismo tamaño, firmeza y alineación que uno de los otros
tipos integrales, que se conoce como su tipo subyacente . El tamaño de este tipo está definido por
la implementación, como se especifica en el § 5.3.3.1, y puede ser, por ejemplo, al menos 8, 16 o
32 bits; si un sistema admite Unicode, por ejemplo, se requiere que wchar_t tenga al menos 32
bits (una excepción a esta regla es Windows, donde wchar_t es de 16 bits por motivos de
compatibilidad). Se hereda de la norma C90, ISO 9899: 1990 § 4.1.5, con solo una pequeña
redacción.
• En sistemas similares a Unix y Unix, wchar_t es de 32 bits, y generalmente se usa para UTF-
32.
• En Windows, wchar_t es de 16 bits y se usa para UTF-16.
• En un sistema que solo tiene soporte de 8 bits, wchar_t es de 8 bits.
C ++ 11
Si se desea compatibilidad con Unicode, se recomienda usar char para UTF-8, char16_t para UTF-
16 o char32_t para UTF-32, en lugar de usar wchar_t .
Modelos de datos
Como se mencionó anteriormente, los anchos de los tipos de enteros pueden diferir entre
plataformas. Los modelos más comunes son los siguientes, con tamaños especificados en bits:
ILP32 (4/4/4) 32 32 32
LLP64 (4/4/8) 32 32 64
LP64 (4/8/8) 32 64 64
https://riptutorial.com/es/home 158
Fuera de estos modelos:
Tenga en cuenta, sin embargo, que estos modelos no se mencionan específicamente en la norma
en sí.
En C ++, un byte es el espacio ocupado por un objeto char . La cantidad de bits en un byte viene
dada por CHAR_BIT , que se define en climits y se requiere que sea al menos 8. Mientras que la
mayoría de los sistemas modernos tienen bytes de 8 bits, y POSIX requiere que CHAR_BIT sea
exactamente 8, hay algunos sistemas donde CHAR_BIT es mayor que 8, es decir, un solo byte
puede estar compuesto por 8, 16, 32 o 64 bits.
int x = 42;
int* p = &x;
long addr = reinterpret_cast<long>(p);
std::cout << addr << "\n"; // prints some numeric address,
// probably in the architecture's native address format
Del mismo modo, el puntero obtenido por conversión de un entero también está definido por la
implementación.
La forma correcta de almacenar un puntero como un entero es usando los tipos uintptr_t o
intptr_t :
uintptr_t uip;
C ++ 11
std::uintptr_t uip;
https://riptutorial.com/es/home 159
un tipo de entero sin signo con la propiedad de que cualquier puntero válido para void
se puede convertir a este tipo, luego se puede volver a convertir en puntero a void , y
el resultado se comparará igual al puntero original.
Si bien, para la mayoría de las plataformas modernas, puede asumir un espacio de direcciones
plano y que la aritmética en uintptr_t es equivalente a la aritmética en char * , es totalmente
posible que una implementación realice una transformación al uintptr_t void * a uintptr_t
siempre que la transformación pueda se invierte cuando se devuelve desde uintptr_t a void * .
Tecnicismos
• En los sistemas intptr_t XSI (X / Open System Interfaces), se requieren los tipos intptr_t y
uintptr_t , de lo contrario son opcionales .
• En el sentido del estándar C, las funciones no son objetos; el estándar C no garantiza que
uintptr_t pueda contener un puntero de función. De todos modos, la conformidad con
POSIX (2.12.3) requiere que:
• C99 §7.18.1:
uintptr_tpodría tener sentido si quiere hacer cosas a los bits del puntero que no puede
hacer con sensatez con un entero con signo.
Los rangos de los tipos de enteros están definidos por la implementación. El encabezado <limits>
proporciona la plantilla std::numeric_limits<T> que proporciona los valores mínimo y máximo de
todos los tipos fundamentales. Los valores satisfacen las garantías proporcionadas por el
estándar C a través de los <climits> y (> = C ++ 11) <cinttypes> .
https://riptutorial.com/es/home 160
• std::numeric_limits<int>::min() es igual a INT_MIN , que es menor o igual que -32767.
• std::numeric_limits<int>::max() es igual a INT_MAX , que es mayor o igual a 32767.
• std::numeric_limits<unsigned int>::max() es igual a UINT_MAX , que es mayor o igual a 65535.
• std::numeric_limits<long>::min() es igual a LONG_MIN , que es menor o igual a -2147483647.
• std::numeric_limits<long>::max() es igual a LONG_MAX , que es mayor o igual que 2147483647.
• std::numeric_limits<unsigned long>::max() es igual a ULONG_MAX , que es mayor o igual que
4294967295.
C ++ 11
Para los tipos de punto flotante T , max() es el valor finito máximo, mientras que min() es el valor
normalizado positivo mínimo. Se proporcionan miembros adicionales para los tipos de punto
flotante, que también están definidos por la implementación pero satisfacen ciertas garantías
proporcionadas por el estándar C a través del encabezado <cfloat> .
El estándar requiere que el long double proporcione al menos la misma precisión que el double , lo
que proporciona al menos la misma precisión que el float ; y que un long double puede
representar cualquier valor que un double pueda representar, mientras que un double puede
https://riptutorial.com/es/home 161
representar cualquier valor que un float pueda representar. Los detalles de la representación
están, sin embargo, definidos por la implementación.
Cuando un entero con signo o sin signo se convierte en un tipo de entero con signo y su valor no
se puede representar en el tipo de destino, el valor producido se define por la implementación.
Ejemplo:
// Suppose that on this implementation, the range of signed char is -128 to +127 and
// the range of unsigned char is 0 to 255
int x = 12345;
signed char sc = x; // sc has an implementation-defined value
unsigned char uc = x; // uc is initialized to 57 (i.e., 12345 modulo 256)
enum E {
RED,
GREEN,
BLUE,
};
using T = std::underlying_type<E>::type; // implementation-defined
Sin embargo, el estándar requiere que el tipo subyacente de una enumeración no sea mayor que
int menos que int y unsigned int no puedan representar todos los valores de la enumeración. Por
lo tanto, en el código anterior, T podría ser int , unsigned int , o short , pero no long long , para dar
algunos ejemplos.
Tenga en cuenta que una enumeración tiene el mismo tamaño (según lo devuelto por sizeof ) que
su tipo subyacente.
https://riptutorial.com/es/home 162
Capítulo 22: Comportamiento indefinido
Introducción
¿Qué es el comportamiento indefinido (UB)? De acuerdo con la norma ISO C ++ (§1.3.24,
N4296), es un "comportamiento por el que esta norma internacional no impone requisitos".
Esto significa que cuando un programa se encuentra con UB, se le permite hacer lo que quiera. A
menudo, esto significa un choque, pero puede que simplemente no haga nada, haga que los
demonios salgan volando por tu nariz , ¡o incluso parece que funciona correctamente!
No hace falta decir que debes evitar escribir código que invoque a UB.
Observaciones
Si un programa contiene un comportamiento indefinido, el estándar de C ++ no impone
restricciones a su comportamiento.
• Puede parecer que funciona según lo previsto por el desarrollador, pero también puede
fallar o producir resultados extraños.
• El comportamiento puede variar entre ejecuciones del mismo programa.
• Cualquier parte del programa puede funcionar mal, incluidas las líneas que vienen antes de
la línea que contiene un comportamiento indefinido.
• La implementación no es necesaria para documentar el resultado de un comportamiento
indefinido.
• La mayoría de los compiladores tienen marcas de advertencia para advertir sobre algunos
casos de comportamiento indefinido en tiempo de compilación.
https://riptutorial.com/es/home 163
• Las versiones más recientes de gcc y clang incluyen un indicador denominado
"Desinfectante de comportamiento indefinido" ( -fsanitize=undefined ) que verificará el
comportamiento indefinido en el tiempo de ejecución, a un costo de rendimiento.
• lint herramientas similares a lint pueden realizar un análisis de comportamiento indefinido
más completo.
De la sección 1.9 (Ejecución del programa) de la norma C ++ 14 (ISO / IEC 14882: 2014):
Examples
Leer o escribir a través de un puntero nulo.
Aunque esto causa con mayor frecuencia un fallo de segmentación, no está definido y puede
pasar cualquier cosa.
https://riptutorial.com/es/home 164
nulo
Omitir la declaración de return en una función que tiene un tipo de retorno que no es void es un
comportamiento indefinido .
int function() {
// Missing return statement
}
int main() {
function(); //Undefined Behavior
}
Nota: main es la única excepción a la regla. Si main no tiene una declaración de return , el
compilador inserta automáticamente return 0; Para ti, para que puedas dejarlo fuera de forma
segura.
C ++ 11
"hello world" es una cadena literal, por lo que modificarlo da un comportamiento indefinido.
C ++ 11
Es un comportamiento indefinido acceder a un índice que está fuera de los límites de una
matriz (o el contenedor de la biblioteca estándar para esa materia, ya que todos se implementan
utilizando una matriz sin procesar ):
https://riptutorial.com/es/home 165
int array[] = {1, 2, 3, 4, 5};
array[5] = 0; // Undefined behavior
Se permite tener un puntero que apunta al final de la matriz (en este caso, array + 5 ),
simplemente no se puede eliminar la referencia, ya que no es un elemento válido.
const int *end = array + 5; // Pointer to one past the last index
for (int *p = array; p != end; ++p)
// Do something with `p`
La división por 0 está definida matemáticamente y, como tal, tiene sentido que se trate de un
comportamiento indefinido.
Sin embargo:
int x = INT_MAX + 1;
Este es uno de los más desagradables, ya que por lo general produce un comportamiento
reproducible y sin interrupciones, por lo que los desarrolladores pueden verse tentados a confiar
en gran medida en el comportamiento observado.
https://riptutorial.com/es/home 166
// x is 0
Los enteros sin signo, declarados sin firmar, obedecerán las leyes del módulo
aritmético 2^n donde n es el número de bits en la representación del valor de ese
tamaño particular de entero.
(C ++ 11 párrafo 3.9.1 / 4)
signed int x ;
if(x > x + 1)
{
//do something
}
Aquí, dado que no se define un desbordamiento de enteros con signo, el compilador es libre de
asumir que nunca puede suceder y, por lo tanto, puede optimizar el bloque "if"
int a;
std::cout << a; // Undefined behavior!
Aunque es muy poco probable en la práctica (ya que depende del soporte de hardware
específico) el compilador podría electrocutar al programador al compilar el ejemplo de código
anterior. Con un compilador y un soporte de hardware de este tipo, tal respuesta al
comportamiento indefinido aumentaría notablemente el entendimiento promedio (vivo) del
programador del verdadero significado del comportamiento indefinido, que es que el estándar no
impone ninguna restricción al comportamiento resultante.
C ++ 14
https://riptutorial.com/es/home 167
• el operando derecho del operador de asignación, si el operando izquierdo también es de
tipo unsigned char ;
• el inicializador para un objeto unsigned char ;
Tenga en cuenta que una variable static siempre se inicializa con cero (si es posible):
static int a;
std::cout << a; // Defined behavior, 'a' is 0
Si una clase, enumeración, función en línea, plantilla o miembro de una plantilla tiene un enlace
externo y se define en múltiples unidades de traducción, todas las definiciones deben ser
idénticas o el comportamiento no está definido según la Regla de una definición (ODR) .
foo.h :
class Foo {
public:
double x;
private:
int y;
};
Foo get_foo();
foo.cpp :
#include "foo.h"
Foo get_foo() { /* implementation */ }
main.cpp :
// I want access to the private member, so I am going to replace Foo with my own type
class Foo {
public:
double x;
int y;
};
Foo get_foo(); // declare this function ourselves since we aren't including foo.h
int main() {
Foo foo = get_foo();
// do something with foo.y
}
https://riptutorial.com/es/home 168
Emparejamiento incorrecto de la asignación de memoria y desasignación
Un objeto solo puede ser desasignado por delete si fue asignado por new y no es una matriz. Si el
argumento para delete no fue devuelto por new o es una matriz, el comportamiento no está
definido.
Un objeto solo puede ser desasignado por delete[] si fue asignado por new y es una matriz. Si el
argumento para delete[] no fue devuelto por new o no es una matriz, el comportamiento no está
definido.
int* p3 = static_cast<int*>(malloc(sizeof(int)));
free(p3); // correct
// delete p3; // undefined
// delete[] p3; // undefined
Dichos problemas se pueden evitar evitando completamente malloc y programas free en C ++,
prefiriendo los punteros inteligentes de la biblioteca estándar sobre new y delete en bruto, y
prefiriendo std::vector y std::string sobre new y delete[] .
En la mayoría de los casos, es ilegal acceder a un objeto de un tipo como si fuera un tipo
diferente (sin tener en cuenta los calificadores cv). Ejemplo:
float x = 42;
int y = reinterpret_cast<int&>(x);
• Se puede acceder a un objeto de tipo de clase como si fuera de un tipo que es una clase
base del tipo de clase real.
• Se puede acceder a cualquier tipo como char o unsigned char , pero lo contrario no es cierto:
no se puede acceder a una matriz char como si fuera un tipo arbitrario.
• Se puede acceder a un tipo entero con signo como el tipo sin signo correspondiente y
viceversa .
Una regla relacionada es que si se llama a una función miembro no estática en un objeto que en
https://riptutorial.com/es/home 169
realidad no tiene el mismo tipo que la clase que define la función, o una clase derivada, entonces
ocurre un comportamiento indefinido. Esto es cierto incluso si la función no accede al objeto.
struct Base {
};
struct Derived : Base {
void f() {}
};
struct Unrelated {};
Unrelated u;
Derived& r1 = reinterpret_cast<Derived&>(u); // ok
r1.f(); // UB
Base b;
Derived& r2 = reinterpret_cast<Derived&>(b); // ok
r2.f(); // UB
Si una operación aritmética que produce un tipo de punto flotante produce un valor que no está en
el rango de valores representables del tipo de resultado, el comportamiento no está definido de
acuerdo con el estándar C ++, pero puede definirse por otros estándares que la máquina pueda
cumplir. tales como IEEE 754.
float x = 1.0;
for (int i = 0; i < 10000; i++) {
x *= 10.0; // will probably overflow eventually; undefined behavior
}
De manera más general, algunas autoridades de C ++, por ejemplo, Scott Meyers, sugieren
nunca llamar a funciones virtuales (incluso no puras) de constructores y constructores.
class transaction
{
public:
transaction(){ log_it(); }
virtual void log_it() const = 0;
};
https://riptutorial.com/es/home 170
};
sell_transaction s;
class base { };
class derived: public base { };
int main() {
base* p = new derived();
delete p; // The is undefined behavior!
}
En la sección [expr.delete] §5.3.5 / 3, el estándar dice que si se llama a delete en un objeto cuyo
tipo estático no tiene un destructor virtual :
Es ilegal acceder a una referencia a un objeto que ha quedado fuera del alcance o ha sido
destruido. Se dice que dicha referencia está colgando ya que ya no se refiere a un objeto válido.
#include <iostream>
int& getX() {
int x = 42;
return x;
}
int main() {
int& r = getX();
https://riptutorial.com/es/home 171
std::cout << r << "\n";
}
En este ejemplo, la variable local x queda fuera del alcance cuando retorna getX . (Tenga en
cuenta que la extensión de vida útil no puede extender la vida útil de una variable local más allá
del alcance del bloque en el que está definida). Por lo tanto, r es una referencia pendiente. Este
programa tiene un comportamiento indefinido, aunque puede parecer que funciona e imprime 42
en algunos casos.
Considera lo siguiente:
#include <algorithm>
namespace std
{
int foo(){}
}
Nada en el estándar prohíbe el algorithm (o uno de los encabezados que incluye) la definición de
la misma definición, por lo que este código violaría la Regla de una definición .
Entonces, en general, esto está prohibido. Sin embargo, hay excepciones específicas permitidas .
Tal vez lo más útil es que está permitido agregar especializaciones para los tipos definidos por el
usuario. Entonces, por ejemplo, supongamos que su código tiene
class foo
{
// Stuff
};
namespace std
{
template<>
struct hash<foo>
{
https://riptutorial.com/es/home 172
public:
size_t operator()(const foo &f) const;
};
}
El valor de origen está fuera del rango de valores que se pueden representar en el tipo de
destino, el resultado es un comportamiento indefinido. Ejemplo:
double x = 1e100;
int y = x; // int probably cannot hold numbers that large, so this is UB
Si se utiliza static_cast para convertir un puntero (referencia de referencia) en una clase base en
un puntero (referencia de referencia) en una clase derivada, pero el operando no apunta (referido
de referencia) a un objeto del tipo de clase derivada, el comportamiento es indefinido. Ver Base a
conversión derivada .
Para llamar a una función mediante un puntero de función, el tipo de puntero de función debe
coincidir exactamente con el tipo de función. De lo contrario, el comportamiento es indefinido.
Ejemplo:
int f();
void (*p)() = reinterpret_cast<void(*)()>(f);
p(); // undefined
https://riptutorial.com/es/home 173
Un compilador usualmente alineará el valor de un objeto const int , por lo que es posible que este
código compile e imprima 123 . Los compiladores también pueden colocar valores const objetos en
la memoria de solo lectura, por lo que puede ocurrir una falla de segmentación. En cualquier
caso, el comportamiento no está definido y el programa puede hacer cualquier cosa.
#include <iostream>
class Foo {
public:
int get_x() const { return m_x; }
void set_x(int x) { m_x = x; }
private:
Foo(int x, Foo*& this_ref): m_x(x) {
this_ref = this;
}
int m_x;
friend const Foo& getFoo();
};
void do_evil(int x) {
instance->set_x(x);
}
int main() {
const Foo& foo = getFoo();
do_evil(456);
std::cout << foo.get_x() << '\n';
}
En este código, getFoo crea un singleton de tipo const Foo y su miembro m_x se inicializa en 123 .
Luego se llama do_evil y el valor de foo.m_x aparentemente se cambia a 456. ¿Qué salió mal?
A pesar de su nombre, do_evil no hace nada particularmente malo; todo lo que hace es llamar a
un setter a través de un Foo* . Pero ese puntero apunta a un objeto const Foo aunque no se usó
const_cast . Este puntero se obtuvo a través del constructor de Foo . Un objeto const no se
convierte en const hasta que se completa su inicialización, por this tiene el tipo Foo* , no const
Foo* , dentro del constructor.
https://riptutorial.com/es/home 174
definido. (Este puntero a miembro se puede obtener a través de static_cast ).
Cuando static_cast se usa para convertir TD::* a TB::* , el miembro apuntado debe pertenecer a
una clase que sea una clase base o una clase derivada de B De lo contrario el comportamiento es
indefinido. Consulte Conversión derivada a base para punteros a miembros.
int a[10];
int* p1 = &a[5];
int* p2 = p1 + 4; // ok; p2 points to a[9]
int* p3 = p1 + 5; // ok; p2 points to one past the end of a
int* p4 = p1 + 6; // UB
int* p5 = p1 - 5; // ok; p2 points to a[0]
int* p6 = p1 - 6; // UB
int* p7 = p3 - 5; // ok; p7 points to a[5]
• Resta dos punteros si ambos no pertenecen al mismo objeto de matriz. (De nuevo, se
considera que el elemento uno más allá del final pertenece a la matriz.) La excepción es que
se pueden restar dos punteros nulos, lo que arroja 0.
int a[10];
int b[10];
int *p1 = &a[8], *p2 = &a[3];
int d1 = p1 - p2; // yields 5
int *p3 = p1 + 2; // ok; p3 points to one past the end of a
int d2 = p3 - p2; // yields 7
int *p4 = &b[0];
int d3 = p4 - p1; // UB
• Cualquier aritmética de punteros en la que el tipo de punto del operando no coincida con el
tipo dinámico del objeto apuntado (ignorando la calificación cv). De acuerdo con el estándar,
https://riptutorial.com/es/home 175
"[en] en particular, un puntero a una clase base no se puede usar para la aritmética de
punteros cuando la matriz contiene objetos de un tipo de clase derivada".
C ++ 11
En este ejemplo, se invoca explícitamente un destructor para un objeto que luego se destruirá
automáticamente.
struct S {
~S() { std::cout << "destroying S\n"; }
};
int main() {
https://riptutorial.com/es/home 176
S s;
s.~S();
} // UB: s destroyed a second time here
Otra forma de destruir un objeto dos veces es tener dos shared_ptr gestionen el objeto sin
compartir la propiedad entre ellos.
https://riptutorial.com/es/home 177
Capítulo 23: Comportamiento no
especificado
Observaciones
Si el comportamiento de una construcción no se especifica, entonces el estándar establece
algunas restricciones en el comportamiento, pero deja cierta libertad a la implementación, que no
es necesaria para documentar lo que sucede en una situación determinada. Contrasta con el
comportamiento definido por la implementación, en el que se requiere que la implementación
documente lo que sucede, y el comportamiento indefinido, en el que puede ocurrir cualquier cosa.
Examples
Orden de inicialización de globales a través de TU
Mientras que dentro de una unidad de traducción, se especifica el orden de inicialización de las
variables globales, el orden de inicialización entre unidades de traducción no está especificado.
• foo.cpp
#include <iostream>
• bar.cpp
#include <iostream>
• main.cpp
int main() {}
foobar
barfoo
https://riptutorial.com/es/home 178
Valor de una enumeración fuera de rango
Si una enumeración de ámbito se convierte en un tipo integral que es demasiado pequeño para
mantener su valor, el valor resultante no se especifica. Ejemplo:
enum class E {
X = 1,
Y = 1000,
};
// assume 1000 does not fit into a char
char c1 = static_cast<char>(E::X); // c1 is 1
char c2 = static_cast<char>(E::Y); // c2 has an unspecified value
Además, si un entero se convierte en una enumeración y el valor del entero está fuera del rango
de los valores de la enumeración, el valor resultante no se especifica. Ejemplo:
enum Color {
RED = 1,
GREEN = 2,
BLUE = 3,
};
Color c = static_cast<Color>(4);
Sin embargo, en el siguiente ejemplo, el comportamiento no está sin especificar, ya que el valor
de origen está dentro del rango de la enumeración, aunque es desigual para todos los
enumeradores:
enum Scale {
ONE = 1,
TWO = 2,
FOUR = 4,
};
Scale s = static_cast<Scale>(3);
El valor de p3 no está especificado porque p2 no puede apuntar a un objeto de tipo int ; su valor
no es una dirección correctamente alineada.
https://riptutorial.com/es/home 179
Resultado de algunas conversiones reinterpret_cast
int f();
auto fp = reinterpret_cast<int(*)(int)>(&f); // fp has unspecified value
C ++ 03
int x = 42;
char* p = reinterpret_cast<char*>(&x); // p has unspecified value
Si se comparan dos punteros utilizando < , > , <= o >= , el resultado no se especifica en los
siguientes casos:
• Los punteros apuntan a diferentes matrices. (Un objeto sin matriz se considera una matriz
de tamaño 1).
int x;
int y;
const bool b1 = &x < &y; // unspecified
int a[10];
const bool b2 = &a[0] < &a[1]; // true
const bool b3 = &a[0] < &x; // unspecified
const bool b4 = (a + 9) < (a + 10); // true
// note: a+10 points past the end of the array
• Los punteros apuntan al mismo objeto, pero a los miembros con un control de acceso
diferente.
class A {
public:
int x;
int y;
bool f1() { return &x < &y; } // true; x comes before y
bool f2() { return &x < &z; } // unspecified
private:
int z;
};
https://riptutorial.com/es/home 180
Espacio ocupado por una referencia.
• Si se aplica sizeof a una referencia, devuelve el tamaño del tipo referenciado, por lo que no
proporciona información sobre si la referencia ocupa algún almacenamiento.
• Las matrices de referencias son ilegales, por lo que no es posible examinar las direcciones
de dos elementos consecutivos de una referencia hipotética de matrices para determinar el
tamaño de una referencia.
• Si se toma la dirección de una referencia, el resultado es la dirección del referente, por lo
que no podemos obtener un puntero a la referencia en sí.
• Si una clase tiene un miembro de referencia, el intento de extraer la dirección de ese
miembro usando offsetof produce un comportamiento indefinido ya que dicha clase no es
una clase de diseño estándar.
• Si una clase tiene un miembro de referencia, la clase ya no es un diseño estándar, por lo
tanto, los intentos de acceder a los datos utilizados para almacenar los resultados de
referencia en un comportamiento no definido o no especificado.
void f() {
int x;
int& r = x;
// do something with r
}
el compilador es libre de simplemente tratar r como un alias para x y reemplazar todas las
apariciones de r en el resto de la función f con x , y no asignar ningún almacenamiento para
mantener r .
Si una función tiene múltiples argumentos, no se especifica en qué orden se evalúan. El siguiente
código podría imprimir x = 1, y = 2 o x = 2, y = 1 pero no se especifica cuál.
https://riptutorial.com/es/home 181
}
C ++ 17
Sin embargo, cada argumento de función se evalúa por completo, y se garantiza la evaluación del
objeto llamante antes de que lo sean los argumentos de función.
struct from_int {
from_int(int x) { std::cout << "from_int (" << x << ")\n"; }
};
int make_int(int x){ std::cout << "make_int (" << x << ")\n"; return x; }
bar
make_int(1)
from_int(1)
make_int(2)
from_int(2)
bar
make_int(2)
from_int(2)
make_int(1)
from_int(1)
que no se imprima bar después de cualquiera de la make o from 's, y que no se imprima:
bar
make_int(2)
make_int(1)
from_int(2)
from_int(1)
o similar. Antes de C ++ 17, la bar impresión después de make_int s era legal, al igual que hacer
https://riptutorial.com/es/home 182
ambos make_int s antes de hacer cualquier from_int s.
C ++ 11
Todos los contenedores de biblioteca estándar se dejan en un estado válido pero no especificado
después de ser movidos. Por ejemplo, en el siguiente código, v2 contendrá {1, 2, 3, 4} después
del movimiento, pero no se garantiza que v1 esté vacío.
int main() {
std::vector<int> v1{1, 2, 3, 4};
std::vector<int> v2 = std::move(v1);
}
Algunas clases tienen un estado movido desde exactamente definido. El caso más importante es
el de std::unique_ptr<T> , que se garantiza que será nulo después de ser movido.
https://riptutorial.com/es/home 183
Capítulo 24: Concurrencia con OpenMP
Introducción
Este tema cubre los conceptos básicos de la concurrencia en C ++ utilizando OpenMP. OpenMP
se documenta con más detalle en la etiqueta OpenMP .
Observaciones
OpenMP no requiere encabezados ni bibliotecas especiales, ya que es una característica de
compilador integrada. Sin embargo, si usa alguna de las funciones de la API de OpenMP como
omp_get_thread_num() , deberá incluir omp.h su biblioteca.
Las instrucciones pragma OpenMP se ignoran cuando la opción OpenMP no está habilitada
durante la compilación. Es posible que desee consultar la opción del compilador en el manual de
su compilador.
Examples
OpenMP: Secciones paralelas
Este ejemplo ilustra los conceptos básicos de la ejecución de secciones de código en paralelo.
Código de muestra
https://riptutorial.com/es/home 184
{
std::cout << "hello " << std::endl;
/** Do something **/
}
#pragma omp section
{
std::cout << "world " << std::endl;
/** Do something **/
}
}
// This line will not be executed until all the
// sections defined above terminates
std::cout << "end" << std::endl;
Salidas
Este ejemplo produce 2 salidas posibles y depende del sistema operativo y el hardware. La salida
también ilustra un problema de condición de carrera que se produciría a partir de dicha
implementación.
SALIDA A SALIDA B
Salida
https://riptutorial.com/es/home 185
• comienza el mundo hola para siempre termina
• comience hola para siempre fin del mundo
• comienza por siempre hola fin del mundo
Como el orden de ejecución no está garantizado, puede observar cualquiera de los resultados
anteriores.
Este ejemplo muestra cómo dividir un bucle en partes iguales y ejecutarlas en paralelo.
// Continue process
// Only when all threads completed their allocated
// loop job
...
* Tenga mucho cuidado de no modificar el tamaño del vector utilizado en paralelo para los bucles,
ya que los índices de rango asignado no se actualizan automáticamente .
Este ejemplo ilustra un concepto para realizar una reducción o recopilación utilizando std::vector
y OpenMP.
Suponemos que tenemos un escenario en el que queremos que varios subprocesos nos ayuden
a generar un montón de cosas, int se usa aquí para simplificar y se puede reemplazar con otros
tipos de datos.
Esto es particularmente útil cuando necesita combinar resultados de esclavos para evitar fallas de
segmentos o violaciones de acceso a la memoria y no desea usar bibliotecas o bibliotecas
personalizadas de contenedores de sincronización.
https://riptutorial.com/es/home 186
// slave thread, in this case a vector to hold each of their results
// We don't have to worry about how many threads were spawn or if we need
// to repeat this declaration or not.
std::vector<int> Slave;
// Tell the compiler to use all threads allocated for this parallel region
// to perform this loop in parts. Actual load appx = 1000000 / Thread Qty
// The nowait keyword tells the compiler that the slave threads don't
// have to wait for all other slaves to finish this for loop job
#pragma omp for nowait
for (size_t i = 0; i < 1000000; ++i
{
/* Do something */
....
Slave.push_back(...);
}
https://riptutorial.com/es/home 187
Capítulo 25: Const Correccion
Sintaxis
• class ClassOne {public: bool non_modifying_member_function () const {/ * ... * /}};
• int ClassTwo :: non_modifying_member_function () const {/ * ... * /}
• void ClassTwo :: modifying_member_function () {/ * ... * /}
• char non_param_modding_func (const ClassOne & one, const ClassTwo * two) {/ * ... * /}
• float parameters_modifying_function (ClassTwo & one, ClassOne * two) {/ * ... * /}
• short ClassThree :: non_modding_non_param_modding_f (const ClassOne &) const {/ * ... *
/}
Observaciones
const corrección de const es una herramienta de solución de problemas muy útil, ya que le permite
al programador determinar rápidamente qué funciones podrían estar modificando
inadvertidamente el código. También evita que los errores involuntarios, como el que se muestra
en Const Correct Function Parameters , se compilen correctamente y pasen desapercibidos.
Es mucho más fácil diseñar una clase para la corrección const , que luego agregar la corrección
const a una clase preexistente. Si es posible, diseñar cualquier clase que puede ser const correcta
para que sea const correcta, para salvar a sí mismo ya otros la molestia de tarde modificándolo.
Tenga en cuenta que esto también puede aplicarse a la corrección volatile si es necesario, con
las mismas reglas que para la corrección const , pero esto se usa con mucha menos frecuencia.
Refrences:
ISO_CPP
Tutorial de C ++
Examples
Los basicos
const corrección const es la práctica de diseñar código de modo que solo el código que necesita
modificar una instancia pueda modificar una instancia (es decir, tenga acceso de escritura) y, a la
inversa, que cualquier código que no necesite modificar una instancia no pueda hacerlo entonces
(es decir, solo tiene acceso de lectura). Esto evita que la instancia se modifique
involuntariamente, lo que hace que el código sea menos propenso a errores, y documenta si el
código está destinado a cambiar el estado de la instancia o no. También permite que las
instancias se traten como const siempre que no necesiten ser modificadas, o definidas como const
https://riptutorial.com/es/home 188
si no es necesario cambiarlas después de la inicialización, sin perder ninguna funcionalidad.
Esto se hace dando a los miembros las funciones const CV-qualifiers , y haciendo const
parámetros de puntero / referencia, excepto en el caso de que necesiten acceso de escritura.
class ConstCorrectClass {
int x;
public:
int getX() const { return x; } // Function is const: Doesn't modify instance.
void setX(int i) { x = i; } // Not const: Modifies instance.
};
// ...
Debido a la naturaleza de la corrección de const, esto comienza con las funciones de los
miembros de la clase y se abre camino hacia afuera; Si intenta llamar a una función miembro no
const desde una instancia const o desde una instancia no const tratada como const , el compilador
le dará un error sobre la pérdida de calificadores cv.
En una clase const , todas las funciones miembro que no cambian el estado lógico tienen this cv
calificado como const , lo que indica que no modifican el objeto (aparte de mutable campos mutable
, que se pueden modificar libremente incluso en casos const ); si una función calificada para cv
const devuelve una referencia, esa referencia también debe ser una const . Esto permite que se
los llame en instancias constantes y no calificadas para CV, ya que una const T* es capaz de
vincularse a una T* o una const T* . Esto, a su vez, permite que las funciones declaren sus
parámetros pasados por referencia como const cuando no necesitan ser modificados, sin perder
ninguna funcionalidad.
Además, en una clase correcta const , todos los parámetros de función pasados por referencia
serán const correctos, como se describe en Const Correct Function Parameters , de modo que solo
pueden modificarse cuando la función necesita modificarlos explícitamente.
https://riptutorial.com/es/home 189
Primero, echemos un vistazo a this calificadores cv:
class ConstIncorrect {
Field fld;
public:
ConstIncorrect(Field& f); // Modifies.
class ConstCorrectCVQ {
Field fld;
public:
ConstCorrectCVQ(Field& f); // Modifies.
const Field& getField() const; // Doesn't modify. Exposes member as const reference,
// preventing indirect modification.
void setField(Field& f); // Modifies.
ConstCorrectCVQ::ConstCorrectCVQ(Field& f) : fld(f) {}
Field& ConstCorrectCVQ::getField() const { return fld; }
void ConstCorrectCVQ::setField(Field& f) { fld = f; }
void ConstCorrectCVQ::doSomething(int i) {
fld.insert_value(i);
}
void ConstCorrectCVQ::doNothing() const {}
https://riptutorial.com/es/home 190
}
Entonces podemos combinar esto con Const Correct Function Parameters , causando la clase sea
plenamente const -correct.
class ConstCorrect {
Field fld;
public:
ConstCorrect(const Field& f); // Modifies instance. Doesn't modify parameter.
const Field& getField() const; // Doesn't modify. Exposes member as const reference,
// preventing indirect modification.
void setField(const Field& f); // Modifies instance. Doesn't modify parameter.
Esto también se puede combinar con la sobrecarga basada en const , en el caso de que
queramos un comportamiento si la instancia es const , y un comportamiento diferente si no lo es;
un uso común para esto es constainers que proporcionan accesores que solo permiten
modificaciones si el contenedor en sí no es const .
class ConstCorrectContainer {
int arr[5];
public:
// Subscript operator provides read access if instance is const, or read/write access
// otherwise.
int& operator[](size_t index) { return arr[index]; }
const int& operator[](size_t index) const { return arr[index]; }
// ...
};
En una función de corrección const , todos los parámetros pasados por referencia se marcan
como const menos que la función los modifique directa o indirectamente, lo que evita que el
programador cambie inadvertidamente algo que no pretendían cambiar. Esto permite que la
función de tomar tanto const y los casos no-CV-cualificado, ya su vez, hace que la instancia está
https://riptutorial.com/es/home 191
this a ser de tipo const T* cuando un miembro de la función se llama, donde T es el tipo de clase.
struct Example {
void func() { std::cout << 3 << std::endl; }
void func() const { std::cout << 5 << std::endl; }
};
int main() {
Example a, b;
const_incorrect_function(a, &b);
const_correct_function(a, &b);
}
// Output:
3
3
5
5
Si bien los efectos de esto son menos evidentes que los de const diseño de la clase correcta (en
ese const funciones -correct y const clases -incorrect causará errores de compilación, mientras
que const clases -correct y const funciones -incorrect compilará correctamente), const correcta las
funciones detectarán una gran cantidad de errores que las funciones const incorrectas dejarán
pasar, como la que se muestra a continuación. [Nota, sin embargo, que un const función -incorrect
causará errores de compilación si se aprueba una const ejemplo, cuando se espera que un no
const uno.]
if (vals.size() && (vals_ind != -1) && (vals_ind < vals.size()) && !(h.needs_recalc())) {
return vals[h.get_cache_index(v_ind)];
}
T temp = v[v_ind];
temp -= h.poll_device();
temp *= h.obtain_random();
temp += h.do_tedious_calculation(temp, v[h.get_last_handled_index()]);
https://riptutorial.com/es/home 192
// We're feeling tired all of a sudden, and this happens.
if (vals_ind != -1) {
vals[vals_ind] = temp;
} else {
v.push_back(temp); // Oops. Should've been accessing vals.
vals_ind = vals.size() - 1;
h.register_index(v_ind, vals_ind);
}
return vals[vals_ind];
}
return vals[vals_ind];
}
Una de las cosas más útiles acerca de la corrección const es que sirve como una forma de
documentar el código, proporcionando ciertas garantías al programador y otros usuarios. Estas
garantías son impuestas por el compilador debido a la const , con una falta de const a su vez
indica que el código no las proporciona.
Esto se puede usar para hacer suposiciones sobre el estado del objeto después de que se llame
a cualquier función miembro dada, incluso sin ver la definición de esa función:
https://riptutorial.com/es/home 193
// ConstMemberFunctions.h
class ConstMemberFunctions {
int val;
mutable int cache;
mutable bool state_changed;
public:
// Constructor clearly changes logical state. No assumptions necessary.
ConstMemberFunctions(int v = 0);
// We can assume this function doesn't change logical state, and doesn't call
// set_val(). It may or may not call squared_calc() or bad_func().
int calc() const;
// We can assume this function doesn't change logical state, and doesn't call
// set_val(). It may or may not call calc() or bad_func().
int squared_calc() const;
// We can assume this function doesn't change logical state, and doesn't call
// set_val(). It may or may not call calc() or squared_calc().
void bad_func() const;
// We can assume this function changes logical state, and may or may not call
// calc(), squared_calc(), or bad_func().
void set_val(int v);
};
Debido a las reglas const , estos supuestos serán de hecho aplicados por el compilador.
// ConstMemberFunctions.cpp
ConstMemberFunctions::ConstMemberFunctions(int v /* = 0*/)
: cache(0), val(v), state_changed(true) {}
return cache;
}
https://riptutorial.com/es/home 194
state_changed = true;
}
}
Esto se puede usar para hacer suposiciones sobre el estado de los parámetros después de pasar
a cualquier función dada, incluso sin ver la definición de esa función.
// function_parameter.h
// We can assume that c isn't modified (and c.set_val() isn't called), and isn't passed
// to non_qualified_function_parameter(). If passed to one_const_one_not(), it is the first
// parameter.
void const_function_parameter(const ConstMemberFunctions& c);
// We can assume that c is modified and/or c.set_val() is called, and may or may not be passed
// to any of these functions. If passed to one_const_one_not, it may be either parameter.
void non_qualified_function_parameter(ConstMemberFunctions& c);
// We can assume that c isn't modified (and c.set_val() isn't called), and isn't passed
// to non_qualified_function_parameter(). If passed to one_const_one_not(), it is the first
// parameter.
void bad_parameter(const ConstMemberFunctions& c);
Debido a las reglas const , estos supuestos serán de hecho aplicados por el compilador.
// function_parameter.cpp
https://riptutorial.com/es/home 195
<< std::endl;
}
const_function_parameter(l);
const_function_parameter(r);
}
Si bien es posible eludir la corrección const y, por extensión, romper estas garantías, el
programador debe hacer esto intencionalmente (al igual que romper la encapsulación con
Machiavelli , arriba), y es probable que cause un comportamiento indefinido.
// ...
https://riptutorial.com/es/home 196
Sin embargo, debido a que esto requiera el programador de decir muy específicamente al
compilador que tienen la intención de ignorar const Ness, y ser consistentes entre los
compiladores, generalmente es seguro asumir que const código correcto se abstendrá de hacerlo
a menos que se especifique lo contrario.
https://riptutorial.com/es/home 197
Capítulo 26: constexpr
Introducción
constexpres una palabra clave que se puede usar para marcar el valor de una variable como una
expresión constante, una función como potencialmente utilizable en expresiones constantes, o
(desde C ++ 17) una declaración if que tiene solo una de sus ramas seleccionadas para compilar.
Observaciones
La palabra clave constexpr se agregó en C ++ 11, pero desde hace algunos años desde que se
publicó el estándar C ++ 11, no todos los compiladores principales lo admitieron. En el momento
en que se publicó el estándar C ++ 11. En el momento de la publicación de C ++ 14, todos los
compiladores principales admiten constexpr .
Examples
variables constexpr
Una variable declarada constexpr es implícitamente const y su valor puede usarse como una
expresión constante.
C ++ 11
int main()
{
constexpr int N = 10 + 2;
cout << N;
}
#define N 10 + 2
int main()
{
cout << N;
https://riptutorial.com/es/home 198
}
Producirá:
cout << 10 + 2;
que obviamente se convertirá en cout << 10 + 2; . Sin embargo, el compilador tendría que hacer
más trabajo. Además, crea un problema si no se utiliza correctamente.
cout << N * 2;
formas:
cout << 10 + 2 * 2; // 14
Una variable const es una variable que necesita memoria para su almacenamiento. Un constexpr
no lo hace. Un constexpr produce una constante de tiempo de compilación, que no se puede
cambiar. Puedes argumentar que la const puede también ser cambiada. Pero considere:
int main()
{
const int size1 = 10;
const int size2 = abs(10);
int arr_one[size1];
int arr_two[size2];
}
Con la mayoría de los compiladores, la segunda instrucción fallará (puede funcionar con GCC,
por ejemplo). El tamaño de cualquier matriz, como usted sabe, tiene que ser una expresión
constante (es decir, resultados en valor de tiempo de compilación). A la segunda variable size2 se
le asigna algún valor que se decide en tiempo de ejecución (aunque sabe que es 10 , para el
compilador no es tiempo de compilación).
Esto significa que una const puede o no ser una verdadera constante de compilación. No puede
garantizar ni hacer cumplir que un valor const particular es absolutamente tiempo de compilación.
Puedes usar #define pero tiene sus propios escollos.
C ++ 11
int main()
{
https://riptutorial.com/es/home 199
constexpr int size = 10;
int arr[size];
}
Una expresión constexpr debe evaluar un valor en tiempo de compilación. Por lo tanto, no puede
utilizar:
C ++ 11
C ++ 11
C ++ 11
funciones constexpr
Una función que se declara constexpr está implícitamente en línea y las llamadas a dicha función
potencialmente producen expresiones constantes. Por ejemplo, la siguiente función, si se llama
con argumentos de expresión constante, también produce una expresión constante:
C ++ 11
Por lo tanto, el resultado de la llamada a la función se puede usar como una matriz enlazada o un
argumento de plantilla, o para inicializar una variable constexpr :
C ++ 11
int main()
{
constexpr int S = Sum(10,20);
int Array[S];
https://riptutorial.com/es/home 200
int Array2[Sum(20,30)]; // 50 array size, compile time
}
Lo interesante de constexpr funciones constexpr es que también puede usarlo como funciones
comunes:
C ++ 11
int a = 20;
auto sum = Sum(a, abs(-20));
Sumno será una función constexpr ahora, se compilará como una función ordinaria, tomando
argumentos variables (no constantes) y devolviendo un valor no constante. No necesitas escribir
dos funciones.
También significa que si intenta asignar dicha llamada a una variable no constante, no se
compilará:
C ++ 11
int a = 20;
constexpr auto sum = Sum(a, abs(-20));
La razón es simple: a constexpr solo se le debe asignar una constante de tiempo de compilación.
Sin embargo, la llamada a la función anterior hace que Sum no sea constexpr (el valor R es no
const, pero el valor L se declara a sí mismo como constexpr ).
La función constexpr también debe devolver una constante de tiempo de compilación. Lo siguiente
no se compilará:
C ++ 11
Porque a1 es una variable que no es constexpr, y prohíbe que la función sea una verdadera
función constexpr . constexpr y asignarle a testamento tampoco funcionará, ya que aún no se
conoce el valor de a (parámetro entrante):
C ++ 11
https://riptutorial.com/es/home 201
{
constexpr int a1 = a; // ERROR
..
C ++ 11
Dado que abs(a) no es una expresión constante (incluso abs(10) no funcionará, ya que abs no
devuelve constexpr int !
C ++ 11
Creamos nuestra propia función Abs , que es un constexpr , y el cuerpo de Abs tampoco rompe
ninguna regla. Además, en el sitio de llamada (dentro de Sum ), la expresión se evalúa como
constexpr . Por lo tanto, la llamada a Sum(-10, 20) será una expresión constante en tiempo de
compilación que resultará en 30 .
Estática si declaración
C ++ 17
La if constexpr se puede usar para compilar condicionalmente el código. La condición debe ser
una expresión constante. La rama no seleccionada se descarta. No se crea una instancia de una
declaración descartada dentro de una plantilla. Por ejemplo:
Además, no es necesario definir las variables y funciones que solo se usan dentro de las
https://riptutorial.com/es/home 202
sentencias descartadas, y las sentencias de return descartadas no se utilizan para la deducción
del tipo de devolución de la función.
if constexpr(false) {
foobar; // error; foobar has not been declared
std::vector<int> v("hello, world"); // error; no matching constructor
}
https://riptutorial.com/es/home 203
Capítulo 27: Construir sistemas
Introducción
C ++, al igual que C, tiene un historial largo y variado con respecto a los flujos de trabajo de
compilación y los procesos de construcción. Hoy en día, C ++ tiene varios sistemas de
compilación populares que se utilizan para compilar programas, a veces para múltiples
plataformas dentro de un sistema de compilación. Aquí, algunos sistemas de construcción serán
revisados y analizados.
Observaciones
Actualmente, no existe un sistema de compilación universal o dominante para C ++ que sea
popular y multiplataforma. Sin embargo, existen varios sistemas de compilación principales que
se adjuntan a las plataformas / proyectos principales, el más notable es GNU Make con el sistema
operativo GNU / Linux y NMAKE con el sistema de proyectos Visual C ++ / Visual Studio.
Examples
Generando entorno de construcción con CMake
CMake genera entornos de compilación para casi cualquier compilador o IDE a partir de una sola
definición de proyecto. Los siguientes ejemplos demostrarán cómo agregar un archivo CMake al
código C ++ multiplataforma "Hello World" .
cmake_minimum_required(VERSION 2.4)
project(HelloWorld)
add_executable(HelloWorld main.cpp)
Este archivo le dice a CMake el nombre del proyecto, qué versión de archivo debe esperar e
instrucciones para generar un ejecutable llamado "HelloWorld" que requiere main.cpp .
https://riptutorial.com/es/home 204
comandos:
> cmake .
CMake también puede abstraer los comandos básicos del shell de la plataforma del ejemplo
anterior:
CMake incluye generadores para una serie de herramientas de construcción e IDE comunes.
Para generar makefiles para nmake Visual Studio :
Introducción
GNU Make (styled make ) es un programa dedicado a la automatización de ejecutar comandos de
shell. GNU Make es un programa específico que pertenece a la familia Make. Sigue siendo
popular entre los sistemas operativos similares a Unix y POSIX, incluidos los derivados del kernel
de Linux, Mac OS X y BSD.
GNU Make es especialmente notable por estar vinculado al Proyecto GNU, que está vinculado al
popular sistema operativo GNU / Linux. GNU Make también tiene versiones compatibles que se
ejecutan en varias versiones de Windows y Mac OS X. También es una versión muy estable con
un significado histórico que sigue siendo popular. Es por estas razones que GNU Make se enseña
a menudo junto con C y C ++.
https://riptutorial.com/es/home 205
Reglas básicas
Para compilar con make, cree un Makefile en el directorio de su proyecto. Su Makefile podría ser
tan simple como:
Makefile
# Then, we say that we want to compile with g++'s recommended warnings and some extra ones.
CXXFLAGS=-Wall -Wextra -pedantic
SRCS=main.cpp
# When you call `make` at the command line, this "target" is called.
# The $(EXE) at the right says that the `all` target depends on the `$(EXE)` target.
# $(EXE) expands to be the content of the EXE variable
# Note: Because this is the first target, it becomes the default target if `make` is called
without target
all: $(EXE)
NOTA: asegúrese de que las sangrías estén con una pestaña, no con cuatro
espacios. De lo contrario, obtendrá un error de Makefile:10: *** missing separator.
Stop.
$ cd ~/Path/to/project
$ make
$ ls
app main.cpp Makefile
$ ./app
Hello World!
https://riptutorial.com/es/home 206
$ make clean
$ ls
main.cpp Makefile
Construcciones incrementales
Cuando empiezas a tener más archivos, make se vuelve más útil. ¿Qué pasa si editas a.cpp pero
no b.cpp ? Recompilar b.cpp tomaría más tiempo.
.
+-- src
| +-- a.cpp
| +-- a.hpp
| +-- b.cpp
| +-- b.hpp
+-- Makefile
Makefile
CXX=g++
CXXFLAGS=-Wall -Wextra -pedantic
EXE=app
SRCS_GLOB=src/*.cpp
SRCS=$(wildcard $(SRCS_GLOB))
OBJS=$(SRCS:.cpp=.o)
all: $(EXE)
$(EXE): $(OBJS)
@$(CXX) -o $@ $(OBJS)
depend: .depend
.depend: $(SRCS)
@-rm -f ./.depend
@$(CXX) $(CXXFLAGS) -MM $^>>./.depend
clean:
-rm -f $(EXE)
-rm $(OBJS)
-rm *~
-rm .depend
include .depend
De nuevo mira las pestañas. Este nuevo Makefile garantiza que solo recompile archivos
modificados, minimizando el tiempo de compilación.
https://riptutorial.com/es/home 207
Documentación
Para obtener más información sobre make, consulte la documentación oficial de Free Software
Foundation , la documentación de stackoverflow y la elaborada respuesta de dmckee sobre
stackoverflow .
Puede crear el código C ++ multiplataforma "Hello World" , utilizando Scons , una herramienta de
construcción de software en lenguaje Python .
Primero, cree un archivo llamado SConstruct (tenga en cuenta que SCons buscará un archivo con
este nombre exacto de forma predeterminada). Por ahora, el archivo debe estar en un directorio a
lo largo de su hello.cpp . Escribe en el nuevo archivo la línea.
Program('hello.cpp')
$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o hello.o -c hello.cpp
g++ -o hello hello.o
scons: done building targets.
Las clases Environment y Glob le ayudarán a configurar aún más qué construir. Por ejemplo, el
archivo SConstruct
env=Environment(CPPPATH='/usr/include/boost/',
CPPDEFINES=[],
LIBS=[],
SCONS_CXX_STANDARD="c++11"
)
env.Program('hello', Glob('src/*.cpp'))
construye el ejecutable hello , usando todos los archivos cpp en src . Su CPPPATH es
/usr/include/boost y especifica el estándar C ++ 11.
Ninja
Introducción
https://riptutorial.com/es/home 208
El sistema de construcción Ninja se describe en el sitio web del proyecto como "un sistema de
construcción pequeña con un enfoque en la velocidad". Ninja está diseñado para generar sus
archivos mediante generadores de archivos de sistema de compilación, y adopta un enfoque de
bajo nivel para construir sistemas, en contraste con los administradores de sistemas de
compilación de nivel superior como CMake o Meson.
Ninja está escrito principalmente en C ++ y Python, y fue creado como una alternativa al sistema
de construcción SCons para el proyecto Chromium.
Introducción
NMAKE es una utilidad de línea de comandos desarrollada por Microsoft para ser utilizada
principalmente junto con Microsoft Visual Studio y / o las herramientas de línea de comandos de
Visual C ++.
Autotools (GNU)
Introducción
Los Autotools son un grupo de programas que crean un sistema de compilación GNU para un
paquete de software dado. Es un conjunto de herramientas que trabajan en conjunto para
producir varios recursos de compilación, como un Makefile (para usar con GNU Make). Por lo
tanto, Autotools puede considerarse un generador de sistema de construcción de facto.
• Autoconf
• Automake (no debe confundirse con make )
En general, Autotools está destinado a generar el script compatible con Unix y Makefile para
permitir que el siguiente comando genere (e instale) la mayoría de los paquetes (en el caso
simple):
Como tal, Autotools también tiene una relación con ciertos administradores de paquetes,
especialmente aquellos que están conectados a sistemas operativos que cumplen con los
Estándares POSIX.
https://riptutorial.com/es/home 209
Lea Construir sistemas en línea: https://riptutorial.com/es/cplusplus/topic/8200/construir-sistemas
https://riptutorial.com/es/home 210
Capítulo 28: Contenedores C ++
Introducción
Los contenedores C ++ almacenan una colección de elementos. Los contenedores incluyen
vectores, listas, mapas, etc. Usando plantillas, los contenedores C ++ contienen colecciones de
primitivas (por ejemplo, ints) o clases personalizadas (por ejemplo, MyClass).
Examples
Diagrama de flujo de contenedores C ++
Elegir qué contenedor de C ++ usar puede ser complicado, así que aquí hay un diagrama de flujo
simple para ayudarlo a decidir qué contenedor es el adecuado para el trabajo.
https://riptutorial.com/es/home 211
https://riptutorial.com/es/home 212
. Este pequeño gráfico en el diagrama de flujo es de Megan Hopkins
https://riptutorial.com/es/home 213
Capítulo 29: Control de flujo
Observaciones
Consulte el tema de los bucles para los diferentes tipos de bucles.
Examples
caso
Introduce una etiqueta de caso de una declaración de cambio. El operando debe ser una
expresión constante y coincidir con la condición de cambio en el tipo. Cuando se ejecuta la
instrucción de cambio, saltará a la etiqueta del caso con el operando igual a la condición, si la
hay.
char c = getchar();
bool confirmed;
switch (c) {
case 'y':
confirmed = true;
break;
case 'n':
confirmed = false;
break;
default:
std::cout << "invalid response!\n";
abort();
}
cambiar
El switch palabra clave va seguido de una condición entre paréntesis y un bloque, que puede
contener etiquetas de case y una etiqueta default opcional. Cuando se ejecuta la instrucción de
cambio, el control se transferirá a una etiqueta de case con un valor que coincida con el de la
condición, si corresponde, o a la etiqueta default , si corresponde.
La condición debe ser una expresión o una declaración, que tenga un tipo de entero o
enumeración, o un tipo de clase con una función de conversión a tipo de entero o enumeración.
char c = getchar();
bool confirmed;
switch (c) {
case 'y':
confirmed = true;
https://riptutorial.com/es/home 214
break;
case 'n':
confirmed = false;
break;
default:
std::cout << "invalid response!\n";
abort();
}
captura
try {
std::vector<int> v(N);
// do something
} catch (const std::bad_alloc&) {
std::cout << "failed to allocate memory for vector!" << std::endl;
} catch (const std::runtime_error& e) {
std::cout << "runtime error: " << e.what() << std::endl;
} catch (...) {
std::cout << "unexpected exception!" << std::endl;
throw;
}
defecto
char c = getchar();
bool confirmed;
switch (c) {
case 'y':
confirmed = true;
break;
case 'n':
confirmed = false;
break;
default:
std::cout << "invalid response!\n";
abort();
}
C ++ 11
https://riptutorial.com/es/home 215
destructor, un operador de asignación de copia o un operador de asignación de movimiento para
tener su comportamiento predeterminado.
class Base {
// ...
// we want to be able to delete derived classes through Base*,
// but have the usual behaviour for Base's destructor.
virtual ~Base() = default;
};
Si
Introduce una sentencia if. La palabra clave if debe ir seguida de una condición entre paréntesis,
que puede ser una expresión o una declaración. Si la condición es verdadera, se ejecutará la
subestación posterior a la condición.
int x;
std::cout << "Please enter a positive number." << std::endl;
std::cin >> x;
if (x <= 0) {
std::cout << "You didn't enter a positive number!" << std::endl;
abort();
}
más
int x;
std::cin >> x;
if (x%2 == 0) {
std::cout << "The number is even\n";
} else {
std::cout << "The number is odd\n";
}
ir
Salta a una declaración etiquetada, que debe estar ubicada en la función actual.
https://riptutorial.com/es/home 216
release_widget(widget);
return result;
}
regreso
int f() {
return 42;
}
int x = f(); // x is 42
int g() {
return 3.14;
}
int y = g(); // y is 3
Si return no tiene un operando, la función debe tener un tipo de retorno void . Como caso
especial, una función void -returning también puede devolver una expresión si la expresión tiene
el tipo void .
void f(int x) {
if (x < 0) return;
std::cout << sqrt(x);
}
int g() { return 42; }
void h() {
return f(); // calls f, then returns
return g(); // ill-formed
}
Cuando se devuelve main , std::exit se llama implícitamente con el valor de retorno, y el valor se
devuelve al entorno de ejecución. (Sin embargo, regresar de main destruye las variables locales
automáticas, mientras que llamar std::exit directamente no lo hace).
lanzar
1. Cuando se produce el throw en una expresión con un operando, su efecto es lanzar una
excepción , que es una copia del operando.
https://riptutorial.com/es/home 217
throw std::invalid_argument("count cannot be negative!");
}
while (count--) { putchar('*'); }
}
2. Cuando se produce el throw en una expresión sin un operando, su efecto es volver a emitir
la excepción actual . Si no hay una excepción actual, se llama a std::terminate .
try {
// something risky
} catch (const std::bad_alloc&) {
std::cerr << "out of memory" << std::endl;
} catch (...) {
std::cerr << "unexpected exception" << std::endl;
// hope the caller knows how to handle this exception
throw;
}
Tenga en cuenta que los dos primeros usos de throw enumerados anteriormente constituyen
expresiones en lugar de declaraciones. (El tipo de una expresión de lanzamiento es void ). Esto
hace posible anidarlos dentro de expresiones, de esta manera:
tratar
A la palabra clave try le sigue un bloque, o una lista de inicialización de constructor y luego un
bloque (ver aquí ). Al bloque try le sigue uno o más bloques catch . Si una excepción se propaga
fuera del bloque try, cada uno de los bloques catch correspondientes después del bloque try tiene
la oportunidad de manejar la excepción, si los tipos coinciden.
https://riptutorial.com/es/home 218
}
si y si no
se usaba para verificar si la expresión dada devuelve verdadero o falso y actúa como tal:
if (condition) statement
la condición puede ser cualquier expresión válida de C ++ que devuelva algo que se verifique con
la verdad / falsedad, por ejemplo:
if (true) { /* code here */ } // evaluate that true is true and execute the code in the
brackets
if (false) { /* code here */ } // always skip the code since false is always false
La condición puede ser cualquier cosa, una función, una variable o una comparación, por
ejemplo.
if(istrue()) { } // evaluate the function, if it returns true, the if will execute the code
if(isTrue(var)) { } //evalute the return of the function after passing the argument var
if(a == b) { } // this will evaluate the return of the experssion (a==b) which will be true if
equal and false if unequal
if(a) { } //if a is a boolean type, it will evaluate for its value, if it's an integer, any
non zero value will be true,
if (a && b) { } // will be true only if both a and b are true (binary operators are outside
the scope here
if (a || b ) { } //true if a or b is true
if (a== "test") {
//will execute if a is a string "test"
} else {
// only if the first failed, will execute
}
if (a=='a') {
// if a is a char valued 'a'
} else if (a=='b') {
// if a is a char valued 'b'
https://riptutorial.com/es/home 219
} else if (a=='c') {
// if a is a char valued 'c'
} else {
//if a is none of the above
}
sin embargo, debe tenerse en cuenta que debe usar ' switch ' en su lugar si su código verifica el
valor de la misma variable
La instrucción de descanso:
Usando break podemos dejar un bucle incluso si la condición para su final no se cumple. Puede
usarse para terminar un bucle infinito, o para forzarlo a que termine antes de su final natural.
La sintaxis es
break;
Ejemplo : a menudo utilizamos break en los casos de switch , es decir, una vez que se cumple un
caso, el bloque de código de esa condición se ejecuta.
switch(conditon){
case 1: block1;
case 2: block2;
case 3: block3;
default: blockdefault;
}
switch(condition){
case 1: block1;
break;
case 2: block2;
break;
case 3: block3;
break;
default: blockdefault;
break;
}
por lo que solo se procesa un bloque y el control sale del bucle de conmutación.
break también se puede usar en otros bucles condicionales y no condicionales como if , while ,
for etc;
ejemplo:
https://riptutorial.com/es/home 220
if(condition1){
....
if(condition2){
.......
break;
}
...
}
La instrucción continua:
La instrucción de continuación hace que el programa omita el resto del bucle en la iteración actual
como si se hubiera llegado al final del bloque de instrucciones, lo que provocó que saltara a la
siguiente iteración.
La sintaxis es
continue;
for(int i=0;i<10;i++){
if(i%2==0)
continue;
cout<<"\n @"<<i;
}
@1
@3
@5
@7
@9
i este código siempre que se cumpla la condición i%2==0 continue se procesa, esto hace que el
compilador omita todo el código restante (imprimiendo @ ei) y se ejecute la declaración de
incremento / decremento del bucle.
La instrucción goto:
Permite realizar un salto absoluto a otro punto del programa. Debe usar esta función con cuidado,
https://riptutorial.com/es/home 221
ya que su ejecución ignora cualquier tipo de limitación de anidamiento. El punto de destino se
identifica mediante una etiqueta, que luego se utiliza como un argumento para la instrucción goto.
Una etiqueta está hecha de un identificador válido seguido de dos puntos (:)
La sintaxis es
goto label;
..
.
label: statement;
Ejemplo:
int num = 1;
STEP:
do{
if( num%2==0 )
{
num = num + 1;
goto STEP;
}
salida:
https://riptutorial.com/es/home 222
value of num : 1
value of num : 3
value of num : 5
value of num : 7
value of num : 9
siempre que se cumpla la condición num%2==0 goto envía el control de ejecución al comienzo del
bucle do-while while.
La función de salida:
exites una función definida en cstdlib . El propósito de exit es terminar el programa en ejecución
con un código de salida específico. Su prototipo es:
https://riptutorial.com/es/home 223
Capítulo 30: Conversiones de tipo explícito
Introducción
Una expresión puede ser convertido o fundido para escribir explícitamente T usando
dynamic_cast<T> , static_cast<T> , reinterpret_cast<T> , o const_cast<T> , dependiendo de qué tipo
de molde que se pretende.
Sintaxis
• especificador de tipo simple ( )
• especificador de tipo simple ( expresión-lista )
• especificador de tipo simple braced-init-list
• typename-specifier ( )
• typename-specifier ( expresión-lista )
• nombre de archivo-especificador braced- init-list
• dynamic_cast < type-id > ( expresión )
• static_cast < type-id > ( expresión )
• reinterpret_cast < type-id > ( expresión )
• const_cast < type-id > ( expresión )
• ( type-id ) expresión-cast
Observaciones
Las seis notaciones emitidas tienen una cosa en común:
• Las conversiones de "tipo punning" , que se pueden usar para acceder a la memoria de un
tipo como si fuera de un tipo diferente.
• Conversiones entre tipos enteros y tipos de punteros , en cualquier dirección.
https://riptutorial.com/es/home 224
La palabra clave static_cast puede realizar una variedad de diferentes conversiones:
• Cualquier conversión que se pueda realizar mediante una inicialización directa, incluidas las
conversiones implícitas y las conversiones que llaman a un constructor explícito o una
función de conversión. Vea aquí y aquí para más detalles.
• Entre los tipos aritméticos y de enumeración, y entre diferentes tipos de enumeración. Ver
las conversiones de enumeración
• Del puntero al miembro de la clase derivada, al puntero del miembro de la clase base. Los
tipos apuntados deben coincidir. Ver conversión derivada a base para punteros a miembros
• void* a T* .
C ++ 11
Examples
Base a conversión derivada
Del mismo modo, una referencia a la clase base se puede convertir en una referencia a la clase
derivada usando static_cast .
https://riptutorial.com/es/home 225
Derived& r2 = r1; // error; cast required
Derived& r3 = static_cast<Derived&>(r1); // OK; r3 now refers to Derived object
Si el tipo de fuente es polimórfico, dynamic_cast se puede usar para realizar una conversión de
base a derivada. Realiza una verificación en tiempo de ejecución y la falla es recuperable en lugar
de producir un comportamiento indefinido. En el caso del puntero, se devuelve un puntero nulo en
caso de error. En el caso de referencia, se produce una excepción en caso de error de tipo
std::bad_cast (o una clase derivada de std::bad_cast ).
Arrojando constness
void bad_strlen(char*);
const char* s = "hello, world!";
bad_strlen(s); // compile error
bad_strlen(const_cast<char*>(s)); // OK, but it's better to make bad_strlen accept const char*
const_castpuede utilizar const_cast al tipo de referencia para convertir un lvalue cualificado por
const en un valor no calificado por const.
const_cast es peligroso porque hace imposible que el sistema de tipo C ++ impida que intente
modificar un objeto const. Si lo hace, se traduce en un comportamiento indefinido.
int x = 42;
char* p = static_cast<char*>(&x); // error: static_cast cannot perform this conversion
char* p = reinterpret_cast<char*>(&x); // OK
*p = 'z'; // maybe this modifies x (see below)
C ++ 11
https://riptutorial.com/es/home 226
El resultado de reinterpret_cast representa la misma dirección que el operando, siempre que la
dirección esté alineada adecuadamente para el tipo de destino. De lo contrario, el resultado no se
especifica.
int x = 42;
char& r = reinterpret_cast<char&>(x);
const void* px = &x;
const void* pr = &r;
assert(px == pr); // should never fire
C ++ 11
int x = 123;
unsigned int& r1 = reinterpret_cast<unsigned int&>(x);
int& r2 = reinterpret_cast<int&>(r1);
r2 = 456; // sets x to 456
reinterpret_cast también se puede utilizar para convertir de un tipo de puntero a datos a otro, o
un tipo de función de puntero a miembro a otro.
Un puntero de objeto (incluido void* ) o un puntero de función se puede convertir a un tipo entero
usando reinterpret_cast . Esto solo se compilará si el tipo de destino es lo suficientemente largo.
El resultado está definido por la implementación y generalmente proporciona la dirección
numérica del byte en memoria a la que apuntan los punteros.
Por lo general, long o unsigned long es lo suficientemente largo para contener cualquier valor de
puntero, pero esto no está garantizado por la norma.
C ++ 11
Si los tipos std::intptr_t y std::uintptr_t existen, se garantiza que son lo suficientemente largos
para contener un void* (y, por lo tanto, cualquier puntero al tipo de objeto). Sin embargo, no se
garantiza que sean lo suficientemente largos para contener un puntero de función.
De manera similar, reinterpret_cast se puede usar para convertir un tipo entero en un tipo de
puntero. Nuevamente, el resultado está definido por la implementación, pero se garantiza que un
https://riptutorial.com/es/home 227
valor de puntero no se modificará en un viaje de ida y vuelta a través de un tipo entero. El
estándar no garantiza que el valor cero se convierta en un puntero nulo.
Una conversión que implique llamar a un constructor explícito o una función de conversión no se
puede hacer de manera implícita. Podemos solicitar que la conversión se realice explícitamente
usando static_cast . El significado es el mismo que el de una inicialización directa, excepto que el
resultado es temporal.
class C {
std::unique_ptr<int> p;
public:
explicit C(int* p) : p(p) {}
};
void f(C c);
void g(int* p) {
f(p); // error: C::C(int*) is explicit
f(static_cast<C>(p)); // ok
f(C(p)); // equivalent to previous line
C c(p); f(c); // error: C is not copyable
}
Conversión implícita
Sin la conversión explícita de tipos, se pasaría un objeto double a los puntos suspensivos y
se produciría un comportamiento indefinido.
https://riptutorial.com/es/home 228
struct Base { /* ... */ };
struct Derived : Base {
Derived& operator=(const Derived& other) {
static_cast<Base&>(*this) = other;
// alternative:
// Base& this_base_ref = *this; this_base_ref = other;
}
};
static_cast puede convertir de un tipo de punto flotante o entero a un tipo de enumeración (ya
sea con o sin ámbito), y viceversa. También puede convertir entre tipos de enumeración.
C ++ 11
Ejemplo:
Ejemplo:
enum Scale {
SINGLE = 1,
https://riptutorial.com/es/home 229
DOUBLE = 2,
QUAD = 4
};
Scale s1 = 1; // error
Scale s2 = static_cast<Scale>(2); // s2 is DOUBLE
Scale s3 = static_cast<Scale>(3); // s3 has value 3, and is not equal to any enumerator
Scale s9 = static_cast<Scale>(9); // unspecified value in C++14; UB in C++17
C ++ 11
enum Direction {
UP = 0,
LEFT = 1,
DOWN = 2,
RIGHT = 3,
};
Direction d = static_cast<Direction>(3.14); // d is RIGHT
struct A {};
struct B { int x; };
struct C : A, B { int y; double z; };
int B::*p1 = &B::x;
int C::*p2 = p1; // ok; implicit conversion
int B::*p3 = p2; // error
int B::*p4 = static_cast<int B::*>(p2); // ok; p4 is equal to p1
int A::*p5 = static_cast<int A::*>(p2); // undefined; p2 points to x, which is a member
// of the unrelated class B
double C::*p6 = &C::z;
double A::*p7 = static_cast<double A::*>(p6); // ok, even though A doesn't contain z
int A::*p8 = static_cast<int A::*>(p6); // error: types don't match
nulo * a T *
https://riptutorial.com/es/home 230
realmente apunta a un objeto T , el resultado apunta a ese objeto. De lo contrario, el resultado no
se especifica.
C ++ 11
Incluso si el operando no apunta a un objeto T , siempre que el operando apunte a un byte cuya
dirección esté correctamente alineada para el tipo T , el resultado de la conversión apunta al
mismo byte.
Casting estilo c
Cuando se utiliza este lanzamiento, usa uno de los siguientes lanzamientos de c ++ (en orden):
• const_cast<NewType>(variable)
• static_cast<NewType>(variable)
• const_cast<NewType>(static_cast<const NewType>(variable))
• reinterpret_cast<const NewType>(variable)
• const_cast<NewType>(reinterpret_cast<const NewType>(variable))
La conversión funcional es muy similar, aunque como pocas restricciones como resultado de su
sintaxis: NewType(expression) . Como resultado, solo se pueden convertir los tipos sin espacios.
Es mejor usar el nuevo c ++ cast, porque es más legible y se puede detectar fácilmente en
cualquier lugar dentro de un código fuente de C ++ y los errores se detectarán en tiempo de
compilación, en lugar de en tiempo de ejecución.
Como este lanzamiento puede dar lugar a reinterpret_cast no deseado, a menudo se considera
peligroso.
https://riptutorial.com/es/home 231
Capítulo 31: Copia elision
Examples
Propósito de la copia elision
Hay lugares en el estándar donde un objeto se copia o mueve para inicializar un objeto. Elección
de copia (a veces llamada optimización de valor de retorno) es una optimización por la cual, bajo
ciertas circunstancias específicas, se permite que un compilador evite la copia o el movimiento
aunque la norma diga que debe suceder.
std::string get_string()
{
return std::string("I am a string.");
}
De acuerdo con la estricta redacción de la norma, esta función inicializará una std::string
temporal, luego la copiará / moverá al objeto de valor de retorno y luego destruirá la temporal. El
estándar es muy claro que así se interpreta el código.
Copy elision es una regla que permite a un compilador de C ++ ignorar la creación del temporal y
su posterior copia / destrucción. Es decir, el compilador puede tomar la expresión de inicialización
para el temporal e inicializar directamente el valor de retorno de la función. Esto obviamente
guarda el rendimiento.
1. El tipo debe tener el constructor de copiar / mover que se habría llamado. Incluso si el
compilador evita la copia / movimiento, el tipo aún debe poder copiarse / moverse.
C ++ 11
struct my_type
{
my_type() = default;
my_type(const my_type &) {std::cout <<"Copying\n";}
my_type(my_type &&) {std::cout <<"Moving\n";}
};
my_type func()
{
return my_type();
}
https://riptutorial.com/es/home 232
¿Qué hará la func llamada? Bueno, nunca se imprimirá "Copiando", ya que el temporal es un
rvalue y my_type es un tipo movible. Entonces, ¿se imprimirá "Moving"?
Sin la regla de elision de copia, esto se requeriría para imprimir siempre "Mover". Pero como la
regla de elision de copia existe, el constructor de movimientos puede o no ser llamado; es
dependiente de la implementación
Debido a que elision es una optimización, su compilador puede no ser compatible con elision en
todos los casos. Y sin importar si el compilador elude un caso particular o no, el tipo todavía debe
soportar la operación que se está elidiando. Por lo tanto, si se elimina una construcción de copia,
el tipo aún debe tener un constructor de copia, aunque no se llamará.
C ++ 17
Normalmente, elision es una optimización. Si bien prácticamente todos los compiladores son
compatibles con la copia de la elision en los casos más simples, tenerla aún representa una carga
particular para los usuarios. A saber, el tipo de quién se está eliminando la copia / movimiento
debe seguir teniendo la operación de copia / movimiento que fue eliminada.
Por ejemplo:
std::mutex a_mutex;
std::lock_guard<std::mutex> get_lock()
{
return std::lock_guard<std::mutex>(a_mutex);
}
Esto puede ser útil en los casos en que a_mutex es un mutex que es privado en algún sistema,
pero un usuario externo puede querer tener un bloqueo de ámbito en él.
Esto tampoco es legal, porque std::lock_guard no se puede copiar o mover. A pesar de que
virtualmente cada compilador de C ++ ocultará la copia / movimiento, el estándar aún requiere
que el tipo tenga esa operación disponible.
Hasta C ++ 17.
Bajo la redacción anterior a C ++ 17, ese código dice crear un temporal y luego usar el temporal
para copiar / mover al valor de retorno, pero la copia temporal puede eliminarse. Bajo la redacción
de C ++ 17, eso no crea un temporal en absoluto.
En C ++ 17, cualquier expresión prvalue , cuando se usa para inicializar un objeto del mismo tipo
que la expresión, no genera un temporal. La expresión inicializa directamente ese objeto. Si
https://riptutorial.com/es/home 233
devuelve un prvalue del mismo tipo que el valor de retorno, entonces el tipo no necesita tener un
constructor de copiar / mover. Y por lo tanto, bajo las reglas de C ++ 17, el código anterior puede
funcionar.
La redacción de C ++ 17 funciona en los casos en que el tipo del prvalue coincide con el tipo que
se está inicializando. Por lo tanto, dado get_lock arriba, esto tampoco requerirá una copia /
movimiento:
Dado que el resultado de get_lock es una expresión prvalue que se utiliza para inicializar un
objeto del mismo tipo, no se realizará ninguna copia o movimiento. Esa expresión nunca crea un
temporal; se utiliza para inicializar directamente the_lock . No hay elision porque no hay copia /
movimiento para ser elide elide.
El término "elision de copia garantizada" es, por lo tanto, un nombre poco apropiado, pero ese es
el nombre de la función tal como se propone para la estandarización de C ++ 17 . No garantiza en
absoluto la elisión; elimina la copia / movimiento por completo, redefiniendo C ++ para que nunca
haya una copia / movimiento que deba ser borrado.
Esta característica solo funciona en casos que involucran una expresión prvalue. Como tal, esto
usa las reglas habituales de elision:
std::mutex a_mutex;
std::lock_guard<std::mutex> get_lock()
{
std::lock_guard<std::mutex> my_lock(a_mutex);
//Do stuff
return my_lock;
}
Si bien este es un caso válido para la elección de copia, las reglas de C ++ 17 no eliminan la
copia / movimiento en este caso. Como tal, el tipo aún debe tener un constructor de copiar /
mover para usar para inicializar el valor de retorno. Y como lock_guard no lo hace, esto sigue
siendo un error de compilación. Se permite que las implementaciones se nieguen a rechazar
copias al pasar o devolver un objeto de tipo de copia trivial. Esto es para permitir el
desplazamiento de tales objetos en registros, que algunas ABI podrían imponer en sus
convenciones de llamada.
struct trivially_copyable {
int a;
};
Si devuelve una expresión prvalue de una función, y la expresión prvalue tiene el mismo tipo que
https://riptutorial.com/es/home 234
el tipo de retorno de la función, entonces se puede eliminar la copia del prvalue temporal:
std::string func()
{
return std::string("foo");
}
Parámetro elision
Cuando pasa un argumento a una función, y el argumento es una expresión prvalue del tipo de
parámetro de la función, y este tipo no es una referencia, entonces la construcción del prvalue se
puede eliminar.
func(std::string("foo"));
Esto dice que para crear una string temporal, luego muévala en el parámetro de función str .
Copy elision permite que esta expresión cree directamente el objeto en str , en lugar de usar un
movimiento + temporal.
Esta es una optimización útil para los casos en que un constructor se declara explicit . Por
ejemplo, podríamos haber escrito lo anterior como func("foo") , pero solo porque la string tiene
un constructor implícito que convierte de const char* a una string . Si ese constructor fuera
explicit , nos veríamos obligados a usar un temporal para llamar al constructor explicit . Copiar
elision nos evita tener que hacer una copia / movimiento innecesario.
Si devuelve una expresión lvalue desde una función, y este valor lvalue:
• representa una variable automática local para esa función, que se destruirá después de la
return
• La variable automática no es un parámetro de función.
• y el tipo de la variable es el mismo tipo que el tipo de retorno de la función
Si todos estos son el caso, entonces la copia / movimiento desde el lvalue se puede eliminar:
std::string func()
{
std::string str("foo");
//Do stuff
return str;
}
Los casos más complejos son elegibles para la elección, pero cuanto más complejo sea el caso,
menos probable será que el compilador realmente lo evite:
https://riptutorial.com/es/home 235
std::string func()
{
std::string ret("foo");
if(some_condition)
{
return "bar";
}
return ret;
}
El compilador todavía podría elidir a ret , pero las posibilidades de que lo hagan disminuyen.
Como se señaló anteriormente, elision no está permitido para los parámetros de valor.
Si utiliza una expresión prvalue para copiar, inicialice una variable, y esa variable tiene el mismo
tipo que la expresión prvalue, entonces la copia puede ser eliminada.
std::string func()
{
return std::string("foo");
}
https://appwritefunc.yet-another-testing-