0% encontró este documento útil (0 votos)
1K vistas918 páginas

Cplusplus Es PDF

Cargado por

Kevin
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
1K vistas918 páginas

Cplusplus Es PDF

Cargado por

Kevin
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

C++

#c++
Tabla de contenido
Acerca de 1

Capítulo 1: Empezando con C ++ 2

Observaciones 2

Versiones 2

Examples 2

Hola Mundo 2

Análisis 2

Comentarios 4

Comentarios de una sola línea 4

C-Style / Block Comentarios 4

Importancia de los comentarios 5

Marcadores de comentario utilizados para deshabilitar el código 6

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

Llamadas de Funciones Especiales - Operadores 9

Visibilidad de prototipos y declaraciones de funciones. 10

El proceso de compilación estándar de C ++. 11

Preprocesador 12

Capítulo 2: Administracion de recursos 14

Introducción 14

Examples 14

Adquisición de recursos es la inicialización 14

Mutexes y seguridad de rosca 15

Capítulo 3: Alcances 17

Examples 17
Alcance de bloque simple 17

Variables globales 17

Capítulo 4: Algoritmos de la biblioteca estándar 19

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

Usando std :: nth_element para encontrar la mediana (u otros cuantiles) 29

Capítulo 5: Alineación 30

Introducción 30

Observaciones 30

Examples 30

Consultar la alineación de un tipo. 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

Leyendo un archivo ASCII en un std :: string 38

Leyendo un archivo en un contenedor 39

Leyendo un `struct` desde un archivo de texto formateado. 40

Copiando un archivo 41
¿Revisar el final del archivo dentro de una condición de bucle, mala práctica? 42

Escribir archivos con configuraciones locales no estándar 42

Capítulo 7: Archivos de encabezado 45

Observaciones 45

Examples 45

Ejemplo básico 45

Archivos fuente 45

El proceso de compilación 47

Plantillas en archivos de encabezado 47

Capítulo 8: Aritmética de punto flotante 49

Examples 49

Los números de punto flotante son raros 49

Capítulo 9: Arrays 51

Introducción 51

Examples 51

Tamaño de matriz: tipo seguro en tiempo de compilación. 51

Matriz en bruto de tamaño dinámico 52

Expandiendo la matriz de tamaño dinámico usando std :: vector. 53

Una matriz de matriz sin formato de tamaño fijo (es decir, una matriz sin formato 2D). 54

Una matriz de tamaño dinámico utilizando std :: vector para almacenamiento. 55

Inicialización de matriz 57

Capítulo 10: Atributos 59

Sintaxis 59

Examples 59

[[sin retorno]] 59

[[caer a través]] 60

[[obsoleto]] y [[obsoleto ("motivo")]] 61

[[nodiscard]] 62

[[maybe_unused]] 62

Capítulo 11: auto 64

Observaciones 64

Examples 64
Muestra auto básica 64

Plantillas de auto y expresión 65

auto, const, y referencias 65

Tipo de retorno final 66

Lambda genérica (C ++ 14) 66

objetos de auto y proxy 67

Capítulo 12: Bucles 68

Introducción 68

Sintaxis 68

Observaciones 68

Examples 68

Basado en rango para 68

En bucle 71

Mientras bucle 73

Declaración de variables en condiciones. 74

Bucle Do-while 75

Declaraciones de control de bucle: romper y continuar 76

Rango-para sobre un sub-rango 77

Capítulo 13: Búsqueda de nombre dependiente del argumento 79

Examples 79

Que funciones se encuentran 79

Capítulo 14: C ++ Streams 81

Observaciones 81

Examples 81

Corrientes de cuerda 81

Leyendo un archivo hasta el final. 82

Leyendo un archivo de texto línea por línea 82

Líneas sin caracteres de espacios en blanco. 82

Líneas con caracteres de espacio en blanco. 82

Leyendo un archivo en un búfer a la vez 83

Copiando arroyos 83

Arrays 84
Imprimiendo colecciones con iostream 84

Impresión básica 84

Tipo implícito de reparto 84

Generación y transformación. 85

Arrays 85

Análisis de archivos 86

Análisis de archivos en contenedores STL 86

Análisis de tablas de texto heterogéneas 86

Transformación 87

Capítulo 15: Campos de bits 88

Introducción 88

Observaciones 88

Examples 89

Declaración y uso 89

Capítulo 16: Categorías de valor 91

Examples 91

Significados de la categoría de valor 91

prvalue 91

xvalor 92

valor 92

glvalue 93

valor 93

Capítulo 17: Clases / Estructuras 95

Sintaxis 95

Observaciones 95

Examples 95

Conceptos básicos de clase 95

Especificadores de acceso 96

Herencia 97

Herencia virtual 99

Herencia múltiple 101


Acceso a los miembros de la clase 102

Fondo 103

Herencia privada: restringiendo la interfaz de clase base 103

Clases finales y estructuras. 104

Amistad 105

Clases / Estructuras Anidadas 106

Tipos de miembros y alias 111

Miembros de la clase estatica 114

Funciones miembro no estáticas 119

Estructura / clase sin nombre 121

Capítulo 18: Clasificación 123

Observaciones 123

Examples 123

Clasificación de contenedores de secuencia con orden específico 123

Clasificación de contenedores de secuencia por sobrecargado menos operador 123

Clasificación de contenedores de secuencia utilizando la función de comparación 124

Ordenando los contenedores de secuencias usando expresiones lambda (C ++ 11) 125

Clasificación y secuenciación de contenedores. 126

clasificación con std :: map (ascendente y descendente) 127

Clasificación de matrices incorporadas 129

Capítulo 19: Comparaciones lado a lado de ejemplos clásicos de C ++ resueltos a través de 130

Examples 130

Buceando a través de un contenedor 130

Capítulo 20: Compilando y construyendo 132

Introducción 132

Observaciones 132

Examples 132

Compilando con GCC 132

Vinculación con bibliotecas: 134

Compilando con Visual C ++ (Línea de Comando) 134

Compilación con Visual Studio (interfaz gráfica) - Hello World 138

Compilando con Clang 145


Compiladores en linea 146

El proceso de compilación de C ++. 147

Compilando con Code :: Blocks (interfaz gráfica) 149

Capítulo 21: Comportamiento definido por la implementación 155

Examples 155

Char puede estar sin firmar o firmado 155

Tamaño de los tipos integrales. 155

Tamaño de char 155

Tamaño de los tipos enteros con signo y sin signo 155

Tamaño de char16_t y char32_t 157

Tamaño de bool 157

Tamaño de wchar_t 158

Modelos de datos 158

Número de bits en un byte 159

Valor numérico de un puntero 159

Rangos de tipos numéricos 160

Representación del valor de los tipos de punto flotante 161

Desbordamiento al convertir de entero a entero con signo 162

Tipo subyacente (y, por tanto, tamaño) de una enumeración 162

Capítulo 22: Comportamiento indefinido 163

Introducción 163

Observaciones 163

Examples 164

Leer o escribir a través de un puntero nulo. 164

No hay declaración de retorno para una función con un tipo de retorno no nulo 164

Modificar un literal de cadena 165

Accediendo a un índice fuera de límites 165

División entera por cero 166

Desbordamiento de enteros firmado 166

Usando una variable local sin inicializar 167

Múltiples definiciones no idénticas (la regla de una definición) 168


Emparejamiento incorrecto de la asignación de memoria y desasignación 169

Accediendo a un objeto como el tipo equivocado 169

Desbordamiento de punto flotante 170

Llamando (Puro) a los Miembros Virtuales del Constructor o Destructor 170

Eliminar un objeto derivado a través de un puntero a una clase base que no tiene un destru 171

Accediendo a una referencia colgante 171

Extendiendo el espacio de nombres `std` o` posix` 172

Desbordamiento durante la conversión hacia o desde el tipo de punto flotante 173

Conversión estática de base a derivada no válida 173

Función de llamada a través del tipo de puntero de función no coincidente 173

Modificar un objeto const 173

Acceso a miembro inexistente a través de puntero a miembro 174

Conversión derivada a base no válida para punteros a miembros 175

Aritmética de puntero no válido 175

Desplazando por un número de posiciones no válido 176

Volviendo de una función [[noreturn]] 176

Destruyendo un objeto que ya ha sido destruido. 176

Recursión de plantilla infinita 177

Capítulo 23: Comportamiento no especificado 178

Observaciones 178

Examples 178

Orden de inicialización de globales a través de TU 178

Valor de una enumeración fuera de rango 179

Reparto estático a partir de un valor falso * 179

Resultado de algunas conversiones reinterpret_cast 180

Resultado de algunas comparaciones de punteros 180

Espacio ocupado por una referencia. 181

Orden de evaluacion de argumentos de funcion. 181

Estado movido de la mayoría de las clases de biblioteca estándar 183

Capítulo 24: Concurrencia con OpenMP 184

Introducción 184

Observaciones 184

Examples 184
OpenMP: Secciones paralelas 184

OpenMP: Secciones paralelas 185

OpenMP: Parallel For Loop 186

OpenMP: Recopilación paralela / Reducción 186

Capítulo 25: Const Correccion 188

Sintaxis 188

Observaciones 188

Examples 188

Los basicos 188

Diseño correcto de la clase de Const 189

Constar los parámetros de función correcta 191

Constancia de la corrección como documentación 193

Funciones de miembros calificados para CV const : 193

Parámetros de la función const : 195

Capítulo 26: constexpr 198

Introducción 198

Observaciones 198

Examples 198

variables constexpr 198

funciones constexpr 200

Estática si declaración 202

Capítulo 27: Construir sistemas 204

Introducción 204

Observaciones 204

Examples 204

Generando entorno de construcción con CMake 204

Compilando con GNU make 205

Introducción 205

Reglas básicas 205

Construcciones incrementales 207

Documentación 207
Construyendo con scons 208

Ninja 208

Introducción 208

NMAKE (Utilidad de mantenimiento de programas de Microsoft) 209

Introducción 209

Autotools (GNU) 209

Introducción 209

Capítulo 28: Contenedores C ++ 211

Introducción 211

Examples 211

Diagrama de flujo de contenedores C ++ 211

Capítulo 29: Control de flujo 214

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

Estructuras condicionales: if, if..else 219

Saltar declaraciones: romper, continuar, goto, salir. 220

Capítulo 30: Conversiones de tipo explícito 224

Introducción 224

Sintaxis 224

Observaciones 224

Examples 225

Base a conversión derivada 225


Arrojando constness 226

Tipo de conversión de punning 226

Conversión entre puntero y entero 227

Conversión por constructor explícito o función de conversión explícita 228

Conversión implícita 228

Enum las conversiones 229

Derivado a conversión base para punteros a miembros 230

nulo * a T * 230

Casting estilo c 231

Capítulo 31: Copia elision 232

Examples 232

Propósito de la copia elision 232

Copia garantizada elision 233

Valor de retorno elision 234

Parámetro elision 235

Valor de retorno con nombre elision 235

Copia inicializacion elision 236

Capítulo 32: Copiando vs Asignación 237

Sintaxis 237

Parámetros 237

Observaciones 237

Examples 237

Operador de Asignación 238

Copia Constructor 238

Copiar constructor vs Asignación de constructor 239

Capítulo 33: decltype 241

Introducción 241

Examples 241

Ejemplo básico 241

Otro ejemplo 241

Capítulo 34: deducción de tipo 243

Observaciones 243
Examples 243

Deducción de parámetros de plantilla para constructores. 243

Tipo de plantilla Deducción 243

Deducción de tipo automático 244

Capítulo 35: Devolviendo varios valores de una función 247

Introducción 247

Examples 247

Uso de parámetros de salida 247

Usando std :: tuple 248

Usando std :: array 249

Usando std :: pair 249

Usando struct 250

Encuadernaciones Estructuradas 251

Usando un consumidor de objetos de función 252

Usando std :: vector 253

Usando el iterador de salida 254

Capítulo 36: Diseño de tipos de objetos 255

Observaciones 255

Examples 255

Tipos de clase 255

Tipos aritméticos 258

Tipos de caracteres estrechos 258

Tipos enteros 258

Tipos de punto flotante 258

Arrays 259

Capítulo 37: Ejemplos de servidor cliente 260

Examples 260

Hola servidor TCP 260

Hola cliente TCP 263

Capítulo 38: El estándar ISO C ++ 265

Introducción 265

Observaciones 265
Examples 266

Borradores de trabajo actuales 266

C ++ 11 266

Extensiones de lenguaje 266

Características generales 266

Las clases 267

Otros tipos 267

Plantillas 267

Concurrencia 267

Características de varios idiomas 267

Extensiones de biblioteca 268

General 268

Contenedores y algoritmos 268

Concurrencia 268

C ++ 14 269

Extensiones de lenguaje 269

Extensiones de biblioteca 269

En desuso / Eliminado 269

C ++ 17 269

Extensiones de lenguaje 269

Extensiones de biblioteca 270

C ++ 03 270

Extensiones de lenguaje 270

C ++ 98 270

Extensiones de lenguaje (con respecto a C89 / C90) 270

Extensiones de biblioteca 271

C ++ 20 271

Extensiones de lenguaje 271

Extensiones de biblioteca 271

Capítulo 39: El puntero este 272


Observaciones 272

Examples 272

este puntero 272

Uso de este puntero para acceder a datos de miembros 274

Uso de este puntero para diferenciar entre datos de miembros y parámetros 275

este puntero CV-calificadores 276

este puntero ref-calificadores 279

Capítulo 40: Enhebrado 281

Sintaxis 281

Parámetros 281

Observaciones 281

Examples 281

Operaciones de hilo 281

Pasando una referencia a un hilo 282

Creando un std :: thread 282

Operaciones en el hilo actual 284

Usando std :: async en lugar de std :: thread 286

Asincrónicamente llamando a una función 286

Errores comunes 286

Asegurando un hilo siempre está unido 286

Reasignando objetos de hilo 287

Sincronizacion basica 288

Uso de variables de condición 288

Crear un grupo de subprocesos simple 290

Almacenamiento de hilo local 292

Capítulo 41: Entrada / salida básica en c ++ 294

Observaciones 294

Examples 294

entrada de usuario y salida estándar 294

Capítulo 42: Enumeración 296

Examples 296

Declaración de enumeración básica 296


Enumeración en declaraciones de cambio 297

Iteración sobre una enumeración 297

Enumerados con alcance 298

Enumerar la declaración hacia adelante en C ++ 11 299

Capítulo 43: Errores comunes de compilación / enlazador (GCC) 301

Examples 301

error: '***' no fue declarado en este alcance 301

Variables 301

Funciones 301

referencia indefinida a `*** ' 302

error fatal: ***: No existe tal archivo o directorio 303

Capítulo 44: Escriba palabras clave 304

Examples 304

clase 304

estructura 305

enumerar 305

Unión 307

Capítulo 45: Espacios de nombres 308

Introducción 308

Sintaxis 308

Observaciones 308

Examples 309

¿Qué son los espacios de nombres? 309

Haciendo espacios de nombres 310

Extendiendo espacios de nombres 311

Usando directiva 311

Búsqueda dependiente del argumento 312

¿Cuándo no se produce ADL? 313

Espacio de nombres en línea 313

Sin nombre / espacios de nombres anónimos 315

Espacios de nombres anidados compactos 316

Aliasing un espacio de nombres largo 316


Alcance de la Declaración de Alias 317

Alias del espacio de nombres 317

Capítulo 46: Especificaciones de vinculación 319

Introducción 319

Sintaxis 319

Observaciones 319

Examples 319

Controlador de señal para sistema operativo similar a Unix 319

Hacer un encabezado de biblioteca C compatible con C ++ 319

Capítulo 47: Especificadores de clase de almacenamiento 321

Introducción 321

Observaciones 321

Examples 321

mudable 321

registro 322

estático 322

auto 323

externo 324

Capítulo 48: Estructuras de datos en C ++ 326

Examples 326

Implementación de listas enlazadas en C ++ 326

Capítulo 49: Estructuras de sincronización de hilos. 329

Introducción 329

Examples 329

std :: shared_lock 329

std :: call_once, std :: once_flag 329

Bloqueo de objetos para un acceso eficiente. 330

std :: condition_variable_any, std :: cv_status 331

Capítulo 50: Excepciones 332

Examples 332

Atrapando excepciones 332

Excepción de recirculación (propagación) 333


Función Try Blocks En constructor 334

Función Try Block para la función regular 334

Función Try Blocks En Destructor 335

Mejores prácticas: tirar por valor, atrapar por referencia constante 335

La excepción jerarquizada 336

std :: uncaught_exceptions 338

Excepción personalizada 340

Capítulo 51: Expresiones Fold 343

Observaciones 343

Examples 343

Pliegues Unarios 343

Pliegues binarios 344

Doblando sobre una coma 344

Capítulo 52: Expresiones regulares 346

Introducción 346

Sintaxis 346

Parámetros 346

Examples 347

Ejemplos básicos de regex_match y regex_search 347

Ejemplo de regex_replace 347

regex_token_iterator Ejemplo 348

Ejemplo de regex_iterator 348

Dividiendo una cuerda 349

Cuantificadores 349

Anclas 351

Capítulo 53: Fecha y hora usando encabezamiento 352

Examples 352

Tiempo de medición utilizando 352

Encuentra el número de días entre dos fechas 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

Capítulo 55: Función de sobrecarga de plantillas 356

Observaciones 356

Examples 356

¿Qué es una sobrecarga de plantilla de función válida? 356

Capítulo 56: Funciones de miembro de clase constante 358

Observaciones 358

Examples 358

función miembro constante 358

Capítulo 57: Funciones de miembro virtual 360

Sintaxis 360

Observaciones 360

Examples 360

Usando override con virtual en C ++ 11 y versiones posteriores 360

Funciones de miembro virtual vs no virtual 361

Funciones virtuales finales 362

Comportamiento de funciones virtuales en constructores y destructores. 363

Funciones virtuales puras 364

Capítulo 58: Funciones en linea 367

Introducción 367

Sintaxis 367

Observaciones 367

Inline como directiva de vinculación 367

Preguntas frecuentes 367

Ver también 368

Examples 368

Declaración de función en línea no miembro 368

Definición de función en línea no miembro 368

Funciones en línea miembro 368

¿Qué es la función en línea? 369

Capítulo 59: Funciones especiales para miembros 370


Examples 370

Destructores virtuales y protegidos. 370

Movimiento implícito y copia 371

Copiar e intercambiar 371

Constructor predeterminado 373

Incinerador de basuras 375

Capítulo 60: Funciones miembro no estáticas 378

Sintaxis 378

Observaciones 378

Examples 378

Funciones miembro no estáticas 378

Encapsulacion 379

Nombre ocultar e importar 380

Funciones de miembro virtual 382

Const Correccion 384

Capítulo 61: Futuros y Promesas 387

Introducción 387

Examples 387

std :: futuro y std :: promesa 387

Ejemplo de asíncrono diferido 387

std :: packaged_task y std :: futuro 388

std :: future_error y std :: future_errc 388

std :: futuro y std :: async 389

Clases de operaciones asincrónicas 391

Capítulo 62: Generación de números aleatorios 392

Observaciones 392

Examples 392

Generador de valor aleatorio verdadero 392

Generando un número pseudoaleatorio 393

Uso del generador para múltiples distribuciones. 393

Capítulo 63: Gestión de la memoria 395

Sintaxis 395
Observaciones 395

Examples 395

Apilar 395

Almacenamiento gratuito (Heap, asignación dinámica ...) 396

Colocación nueva 397

Capítulo 64: Herramientas y Técnicas de Depuración y Prevención de Depuración de C ++ 400

Introducción 400

Observaciones 400

Examples 400

Mi programa de C ++ termina con segfault - valgrind 400

Análisis de Segfault con GDB 402

Código limpio 403

El uso de funciones separadas para acciones separadas. 404

Usando formateo / construcciones consistentes 405

Señala la atención a las partes importantes de tu código. 405

Conclusión 405

Análisis estático 405

Advertencias del compilador 406

Herramientas externas 406

Otras herramientas 407

Conclusión 407

Apilamiento seguro (corrupciones de la pila) 407

¿Qué partes de la pila se mueven? 407

¿Para qué se usa realmente? 407

¿Cómo habilitarlo? 408

Conclusión 408

Capítulo 65: Idioma Pimpl 409

Observaciones 409

Examples 409

Lenguaje básico de Pimpl 409

Capítulo 66: Implementación de patrones de diseño en C ++ 411


Introducción 411

Observaciones 411

Examples 411

Patrón observador 411

Patrón de adaptador 414

Patrón de fábrica 416

Patrón de constructor con API fluida 417

Pasar el constructor alrededor 419

Variante de diseño: objeto mutable 420

Capítulo 67: Incompatibilidades C 421

Introducción 421

Examples 421

Palabras clave reservadas 421

Punteros débilmente escritos 421

goto o cambiar 421

Capítulo 68: Inferencia de tipos 422

Introducción 422

Observaciones 422

Examples 422

Tipo de datos: Auto 422

Lambda auto 422

Bucles y auto 423

Capítulo 69: Internacionalización en C ++ 424

Observaciones 424

Examples 424

Entendiendo las características de la cadena C ++ 424

Capítulo 70: Iteración 426

Examples 426

descanso 426

continuar 426

hacer 426

para 426
mientras 427

rango basado en bucle 427

Capítulo 71: Iteradores 428

Examples 428

Iteradores C (Punteros) 428

Rompiendolo 428

Visión general 429

Los iteradores son posiciones 429

De los iteradores a los valores 429

Iteradores inválidos 431

Navegando con iteradores 431

Conceptos de iterador 432

Rasgos del iterador 432

Iteradores inversos 433

Iterador de vectores 434

Iterador de mapas 434

Iteradores de corriente 435

Escribe tu propio iterador respaldado por generador 435

Capítulo 72: La Regla De Tres, Cinco Y Cero 437

Examples 437

Regla de cinco 437

Regla de cero 438

Regla de tres 439

Protección de autoasignación 441

Capítulo 73: Lambdas 443

Sintaxis 443

Parámetros 443

Observaciones 444

Examples 444

¿Qué es una expresión lambda? 444

Especificando el tipo de retorno 447


Captura por valor 448

Captura generalizada 449

Captura por referencia 450

Captura por defecto 451

Lambdas genericas 451

Conversión a puntero de función. 453

Clase lambdas y captura de esta. 453

Portar funciones lambda a C ++ 03 usando functores 455

Lambdas recursivas 456

Usa std::function 456

Utilizando dos punteros inteligentes: 457

Usa un combinador en Y 457

Usando lambdas para desempaquetar paquetes de parámetros en línea 458

Capítulo 74: Literales 461

Introducción 461

Examples 461

cierto 461

falso 461

nullptr 461

esta 462

Literal entero 462

Capítulo 75: Literales definidos por el usuario 465

Examples 465

Literales definidos por el usuario con valores dobles largos. 465

Literales estándar definidos por el usuario para la duración 465

Literales estándar definidos por el usuario para cuerdas. 466

Literales estándar definidos por el usuario para complejos. 466

Literales auto-hechos definidos por el usuario para binarios 467

Capítulo 76: Manipulación de bits 469

Observaciones 469

Examples 469

Poniendo un poco 469


Manipulación de bits estilo C 469

Usando std :: bitset 469

Despejando un poco 469

Manipulación de bits estilo C 469

Usando std :: bitset 470

Toggling un poco 470

Manipulación de bits estilo C 470

Usando std :: bitset 470

Revisando un poco 470

Manipulación de bits estilo C 470

Usando std :: bitset 471

Cambiando el nth bit a x 471

Manipulación de bits estilo C 471

Usando std :: bitset 471

Establecer todos los bits 471

Manipulación de bits estilo C 471

Usando std :: bitset 471

Eliminar el bit de ajuste más a la derecha 471

Manipulación de bits estilo C 471

Set de bits de conteo 472

Compruebe si un entero es una potencia de 2 473

Aplicación de manipulación de bits: letra pequeña a mayúscula 473

Capítulo 77: Manipuladores de corriente 475

Introducción 475

Observaciones 475

Examples 476

Manipuladores de corriente 477

Manipuladores de flujo de salida 483

Manipuladores de flujo de entrada 484

Capítulo 78: Más comportamientos indefinidos en C ++ 486


Introducción 486

Examples 486

Refiriéndose a los miembros no estáticos en las listas de inicializadores 486

Capítulo 79: Mejoramiento 487

Introducción 487

Examples 487

Expansión en línea / en línea 487

Optimización de la base vacía 487

Capítulo 80: Metaprogramacion 489

Introducción 489

Observaciones 489

Examples 489

Cálculo de factoriales 489

Iterando sobre un paquete de parámetros 492

Iterando con std :: integer_sequence 493

Despacho de etiquetas 494

Detectar si la expresión es válida 495

Cálculo de la potencia con C ++ 11 (y superior) 496

Distinción manual de los tipos cuando se da cualquier tipo T 497

Si-entonces-de lo contrario 498

Generic Min / Max con cuenta de argumento variable 498

Capítulo 81: Metaprogramacion aritmica 500

Introducción 500

Examples 500

Cálculo de la potencia en O (log n) 500

Capítulo 82: Modelo de memoria C ++ 11 502

Observaciones 502

Operaciones atómicas 502

Consistencia secuencial 503

Pedidos relajados 503

Liberar-Adquirir pedidos 503

Orden de liberación de consumo 504


Vallas 504

Examples 504

Necesidad de modelo de memoria 504

Ejemplo de valla 506

Capítulo 83: Mover la semantica 508

Examples 508

Mover la semantica 508

Mover constructor 508

Mover la tarea 510

Usando std :: move para reducir la complejidad de O (n²) a O (n) 511

Uso de semántica de movimiento en contenedores 514

Reutilizar un objeto movido 515

Capítulo 84: Mutex recursivo 516

Examples 516

std :: recursive_mutex 516

Capítulo 85: Mutexes 517

Observaciones 517

Es mejor usar std :: shared_mutex que std :: shared_timed_mutex . 517

El siguiente código es la implementación de MSVC14.1 de std :: shared_mutex. 517

El siguiente código es la implementación de MSVC14.1 de std :: shared_timed_mutex. 519

std :: shared_mutex procesó lectura / escritura más de 2 veces más que std :: shared_timed 522

Examples 525

std :: unique_lock, std :: shared_lock, std :: lock_guard 525

Estrategias para clases de bloqueo: std :: try_to_lock, std :: adopt_lock, std :: defer_lo 526

std :: mutex 527

std :: scoped_lock (C ++ 17) 528

Tipos mutex 528

std :: bloqueo 528

Capítulo 86: Objetos callables 529

Introducción 529

Observaciones 529

Examples 529
Punteros a funciones 529

Clases con operador () (Functors) 530

Capítulo 87: Operadores de Bits 531

Observaciones 531

Examples 531

& - a nivel de bit y 531

| - en modo bit o 532

^ - XOR bitwise (OR exclusivo) 532

~ - bitwise NOT (complemento único) 534

<< - desplazamiento a la izquierda 535

>> - cambio a la derecha 536

Capítulo 88: Optimización en C ++ 537

Examples 537

Optimización de clase base vacía 537

Introducción al rendimiento 537

Optimizando ejecutando menos código. 538

Eliminando código inútil 538

Haciendo código solo una vez 538

Evitar la reasignación inútil y copiar / mover 539

Usando contenedores eficientes 540

Optimización de objetos pequeños 540

Ejemplo 540

¿Cuándo usar? 542

Capítulo 89: Palabra clave amigo 543

Introducción 543

Examples 543

Función de amigo 543

Método de amigo 544

Clase de amigo 544

Capítulo 90: palabra clave const 546

Sintaxis 546

Observaciones 546
Examples 546

Variables locales const 546

Punteros const 547

Funciones de miembro const 547

Evitar la duplicación de código en los métodos const y non-const getter. 547

Capítulo 91: palabra clave mutable 550

Examples 550

modificador de miembro de clase no estático 550

lambdas mutables 550

Capítulo 92: Palabras clave 552

Introducción 552

Sintaxis 552

Observaciones 552

Examples 554

asm 554

explícito 555

noexcept 555

escribe un nombre 557

tamaño de 557

Diferentes palabras clave 558

Capítulo 93: Palabras clave de la declaración variable 563

Examples 563

const 563

decltype 563

firmado 564

no firmado 564

volátil 565

Capítulo 94: Palabras clave de tipo básico 566

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

Capítulo 95: Paquetes de parametros 570

Examples 570

Una plantilla con un paquete de parámetros. 570

Expansión de un paquete de parámetros. 570

Capítulo 96: Patrón de diseño Singleton 571

Observaciones 571

Examples 571

Inicialización perezosa 571

Subclases 572

Hilo seguro Singeton 573

Desinticialización estática segura de singleton. 574

Capítulo 97: Patrón de Plantilla Curiosamente Recurrente (CRTP) 575

Introducción 575

Examples 575

El patrón de plantilla curiosamente recurrente (CRTP) 575

CRTP para evitar la duplicación de código 577

Capítulo 98: Perfilado 579

Examples 579

Perfilando con gcc y gprof 579

Generando diagramas de callgraph con gperf2dot 580

Perfilando el uso de la CPU con gcc y Google Perf Tools 581

Capítulo 99: Plantillas 584

Introducción 584

Sintaxis 584
Observaciones 584

Examples 586

Plantillas de funciones 586

Reenvío de argumentos 587

Plantilla de clase básica 588

Especialización en plantillas 589

Especialización en plantillas parciales. 589

Valor predeterminado del parámetro de la plantilla 591

Plantilla alias 592

Plantilla plantilla parámetros 592

Declaración de argumentos de plantilla no tipo con auto 593

Borrador personalizado vacío para unique_ptr 593

Parámetro de plantilla sin tipo 594

Estructuras de datos de plantillas variables 595

Instanciación explícita 598

Capítulo 100: Plantillas de expresiones 600

Examples 600

Plantillas de expresiones básicas en expresiones algebraicas de elementos 600

Archivo vec.hh: wrapper para std :: vector, utilizado para mostrar el registro cuando se l 602

Archivo expr.hh: implementación de plantillas de expresión para operaciones de elementos ( 603

Archivo main.cc: test src file 607

Un ejemplo básico que ilustra plantillas de expresiones. 609

Capítulo 101: Polimorfismo 614

Examples 614

Definir clases polimórficas. 614

Descenso seguro 615

Polimorfismo y Destructores 617

Capítulo 102: precedencia del operador 618

Observaciones 618

Examples 618

Operadores aritméticos 619

Operadores lógicos AND y OR 619


Lógica && y || operadores: cortocircuito 619

Operadores Unarios 620

Capítulo 103: Preprocesador 622

Introducción 622

Observaciones 622

Examples 622

Incluir guardias 622

Lógica condicional y manejo multiplataforma. 623

Macros 625

Mensajes de error del preprocesador 629

Macros predefinidas 629

Macros x 631

#pragma una vez 633

Operadores de preprocesador 633

Capítulo 104: Pruebas unitarias en C ++ 635

Introducción 635

Examples 635

Prueba de google 635

Ejemplo mínimo 635

Captura 635

Capítulo 105: Punteros 637

Introducción 637

Sintaxis 637

Observaciones 637

Examples 637

Fundamentos de puntero 637

Creando una variable de puntero 637

Tomando la dirección de otra variable 638

Accediendo al contenido de un puntero. 639

Desreferenciación de punteros inválidos 639

Operaciones de puntero 640


Aritmética de puntero 641

Incremento / Decremento 641

Suma resta 641

Diferencia de puntero 641

Capítulo 106: Punteros a los miembros 643

Sintaxis 643

Examples 643

Punteros a funciones miembro estáticas 643

Punteros a funciones miembro 644

Punteros a variables miembro 644

Punteros a variables miembro estáticas 645

Capítulo 107: Punteros inteligentes 647

Sintaxis 647

Observaciones 647

Examples 647

Compartir propiedad (std :: shared_ptr) 647

Compartir con propiedad temporal (std :: weak_ptr) 650

Propiedad única (std :: unique_ptr) 651

Uso de eliminaciones personalizadas para crear una envoltura para una interfaz C 654

Propiedad única sin semántica de movimiento (auto_ptr) 655

Consiguiendo un share_ptr refiriéndose a esto 657

Casting std :: shared_ptr pointers 658

Escribiendo un puntero inteligente: value_ptr 658

Capítulo 108: RAII: la adquisición de recursos es la inicialización 661

Observaciones 661

Examples 661

Cierre 661

Finalmente / ScopeExit 662

ScopeSuccess (c ++ 17) 663

ScopeFail (c ++ 17) 664

Capítulo 109: Recursion en C ++ 667

Examples 667
Uso de la recursión de la cola y la recursión del estilo de Fibonnaci para resolver la sec 667

Recursion con memoizacion. 667

Capítulo 110: Reenvío perfecto 669

Observaciones 669

Examples 669

Funciones de fábrica 669

Capítulo 111: Referencias 671

Examples 671

Definiendo una referencia 671

Las referencias de C ++ son alias de variables existentes 671

Capítulo 112: Regla de una definición (ODR) 673

Examples 673

Función multiplicada definida 673

Funciones en linea 673

Violación ODR a través de la resolución de sobrecarga 675

Capítulo 113: Resolución de sobrecarga 676

Observaciones 676

Examples 676

Coincidencia exacta 676

Categorización de argumento a costo de parámetro 677

Búsqueda de nombres y verificación de acceso 678

Sobrecarga en la referencia de reenvío 678

Pasos de resolución de sobrecarga 679

Promociones y conversiones aritméticas. 681

Sobrecarga dentro de una jerarquía de clases 682

Sobrecarga en constness y volatilidad. 683

Capítulo 114: RTTI: Información de tipo de tiempo de ejecución 685

Examples 685

Nombre de un tipo 685

dynamic_cast 685

La palabra clave typeid 685

Cuándo usar el que está en c ++ 686


Capítulo 115: Semáforo 687

Introducción 687

Examples 687

Semáforo C ++ 11 687

Clase de semáforo en acción. 687

Capítulo 116: Separadores de dígitos 689

Examples 689

Separador de dígitos 689

Capítulo 117: SFINAE (el fallo de sustitución no es un error) 690

Examples 690

enable_if 690

Cuando usarlo 690

void_t 692

arrastrando decltype en plantillas de funciones 693

Que es el SFINAE 694

enable_if_all / enable_if_any 695

is_detected 697

Resolución de sobrecarga con un gran número de opciones. 698

Capítulo 118: Sobrecarga de funciones 700

Introducción 700

Observaciones 700

Examples 700

¿Qué es la sobrecarga de funciones? 700

Tipo de retorno en la sobrecarga de funciones 702

Función de miembro cv-qualifier Sobrecarga 702

Capítulo 119: Sobrecarga del operador 705

Introducción 705

Observaciones 705

Examples 705

Operadores aritméticos 705

Operadores unarios 707


Operadores de comparación 708

Operadores de conversión 709

Operador de subíndice de matriz 710

Operador de llamada de función 711

Operador de asignación 712

Operador NO bit a bit 712

Operadores de cambio de bit para E / S 713

Números complejos revisados 714

Operadores nombrados 718

Capítulo 120: static_assert 721

Sintaxis 721

Parámetros 721

Observaciones 721

Examples 721

static_assert 721

Capítulo 121: std :: array 723

Parámetros 723

Observaciones 723

Examples 723

Inicializando un std :: array 723

Acceso a elementos 724

Comprobando el tamaño de la matriz 726

Iterando a través de la matriz 727

Cambiando todos los elementos de la matriz a la vez 727

Capítulo 122: std :: atómica 728

Examples 728

tipos atómicos 728

Capítulo 123: std :: cualquiera 731

Observaciones 731

Examples 731

Uso básico 731

Capítulo 124: std :: forward_list 732


Introducción 732

Observaciones 732

Examples 732

Ejemplo 732

Métodos 733

Capítulo 125: std :: function: Para envolver cualquier elemento que sea llamable 735

Examples 735

Uso simple 735

std :: función utilizada con std :: bind 735

std :: función con lambda y std :: bind 736

`function` sobrecarga 737

Enlace std :: función a diferentes tipos de llamada 738

Almacenando argumentos de funciones en std :: tuple 740

Capítulo 126: std :: integer_sequence 742

Introducción 742

Examples 742

Gire un std :: tuple en parámetros de función 742

Crear un paquete de parámetros que consiste en enteros. 743

Convertir una secuencia de índices en copias de un elemento. 743

Capítulo 127: std :: iomanip 745

Examples 745

std :: setw 745

std :: setprecision 745

std :: setfill 746

std :: setiosflags 746

Capítulo 128: std :: map 749

Observaciones 749

Examples 749

Elementos de acceso 749

Inicializando un std :: map o std :: multimap 750

Borrando elementos 751

Insertando elementos 752


Iterando sobre std :: map o std :: multimap 754

Buscando en std :: map o en std :: multimap 754

Comprobando el número de elementos 755

Tipos de mapas 755

Mapa regular 755

Multi-Mapa 756

Hash-Map (Mapa desordenado) 756

Creando std :: map con tipos definidos por el usuario como clave 756

Ordenamiento estricto y débil 757

Capítulo 129: std :: opcional 758

Examples 758

Introducción 758

Otros enfoques opcionales 758

Opcional vs puntero 758

Opcional vs Sentinel 758

Opcional vs std::pair<bool, T> 758

Usando opcionales para representar la ausencia de un valor. 758

Usando opcionales para representar el fallo de una función. 759

opcional como valor de retorno 760

valor_o 761

Capítulo 130: std :: par 762

Examples 762

Creando un par y accediendo a los elementos. 762

Comparar operadores 762

Capítulo 131: std :: set y std :: multiset 764

Introducción 764

Observaciones 764

Examples 764

Insertando valores en un conjunto 764

Insertar valores en un multiset 765

Cambiar el tipo predeterminado de un conjunto 766

Orden predeterminado 767


Orden personalizado 767

Tipo lambda 768

Otras opciones de clasificación 768

Buscando valores en set y multiset 768

Eliminar valores de un conjunto 769

Capítulo 132: std :: string 771

Introducción 771

Sintaxis 771

Observaciones 772

Examples 772

Terrible 772

Reemplazo de cuerdas 773

Reemplazar por posición 773

Reemplazar las ocurrencias de una cadena con otra cadena 773

Concatenación 774

Accediendo a un personaje 775

operador [] (n) 775

en (n) 775

frente() 775

atrás() 776

Tokenizar 776

Conversión a (const) char * 777

Encontrar caracteres en una cadena 778

Recorte de caracteres al inicio / final 778

Comparacion lexicografica 780

Conversión a std :: wstring 781

Usando la clase std :: string_view 782

Recorriendo cada personaje 783

Conversión a enteros / tipos de punto flotante 783

Convertir entre codificaciones de caracteres. 784

Comprobando si una cadena es un prefijo de otra 785


Convertir a std :: string 786

Capítulo 133: std :: variante 788

Observaciones 788

Examples 788

Basic std :: uso variante 788

Crear punteros pseudo-método 789

Construyendo un `std :: variant` 790

Capítulo 134: std :: vector 791

Introducción 791

Observaciones 791

Examples 791

Inicializando un std :: vector 791

Insertando Elementos 792

Iterando Sobre std :: vector 794

Iterando en la dirección hacia adelante 794

Iterando en la dirección inversa 794

Hacer cumplir elementos const 795

Una nota sobre la eficiencia 796

Elementos de acceso 796

Acceso basado en índices: 796

Iteradores 799

Usando std :: vector como una matriz C 800

Iterador / Invalidación de puntero 800

Borrando elementos 801

Eliminando el último elemento: 801

Eliminando todos los elementos: 801

Eliminando elemento por índice: 802

Borrar todos los elementos en un rango: 802

Eliminando elementos por valor: 802

Eliminando elementos por condición: 802


Eliminar elementos por lambda, sin crear una función de predicado adicional 802

Borrar elementos por condición de un bucle: 803

Eliminar elementos por condición de un bucle inverso: 803

Encontrando un Elemento en std :: vector 804

Convertir una matriz a std :: vector 805

vector : La excepción a tantas, tantas reglas 806

Tamaño y capacidad del vector 807

Vectores de concatenacion 809

Reduciendo la capacidad de un vector 810

Uso de un vector ordenado para la búsqueda rápida de elementos 810

Funciones que devuelven grandes vectores 812

Encuentre el elemento máximo y mínimo y el índice respectivo en un vector 813

Matrices usando vectores 814

Capítulo 135: Técnicas de refactorización 816

Introducción 816

Examples 816

Recorrer la refactorización 816

Ir a la limpieza 818

Capítulo 136: Tipo de borrado 820

Introducción 820

Examples 820

Mecanismo basico 820

Borrado a un tipo regular con vtable manual 821

Una función `std :: function` solo para movimiento 824

Borrado hasta un búfer contiguo de T 826

Borrado de tipos Borrado de tipos con std :: any 828

Capítulo 137: Tipo de Devolución Covarianza 834

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

Capítulo 138: Tipo de rasgos 838

Observaciones 838

Examples 838

Rasgos de tipo estándar 838

Constantes 838

Funciones 838

Los tipos 839

Escribe relaciones con std :: is_same 839

Rasgos fundamentales de tipo 840

Tipo de propiedades 841

Capítulo 139: Tipo de retorno final 843

Sintaxis 843

Observaciones 843

Examples 843

Evite calificar un nombre de tipo anidado 843

Expresiones lambda 843

Capítulo 140: Tipos atómicos 845

Sintaxis 845

Observaciones 845

Examples 845

Acceso multihilo 845

Capítulo 141: Tipos sin nombre 847

Examples 847

Clases sin nombre 847

Miembros anónimos 847

Como un alias de tipo 848

Anonima union 848

Capítulo 142: Typedef y alias de tipo 849

Introducción 849

Sintaxis 849
Examples 849

Sintaxis básica de typedef 849

Usos más complejos de typedef. 849

Declarando múltiples tipos con typedef 850

Declaración de alias con "utilizando" 850

Capítulo 143: Uniones 852

Observaciones 852

Examples 852

Características básicas de la unión 852

Uso tipico 852

Comportamiento indefinido 853

Capítulo 144: Usando std :: unordered_map 854

Introducción 854

Observaciones 854

Examples 854

Declaración y uso 854

Algunas funciones básicas 854

Capítulo 145: Utilizando declaración 856

Introducción 856

Sintaxis 856

Observaciones 856

Examples 856

Importando nombres individualmente desde un espacio de nombres 856

Volver a declarar miembros de una clase base para evitar ocultar el nombre 856

Heredando constructores 857

Capítulo 146: Valor y semántica de referencia 858

Examples 858

Copia profunda y soporte de movimiento. 858

Definiciones 860

Capítulo 147: Variables en linea 862

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.

El proceso para compilar un programa C ++ difiere inherentemente entre compiladores y sistemas


operativos. El tema Compilación y creación contiene los detalles sobre cómo compilar el código C
++ en diferentes plataformas para una variedad de compiladores.

Versiones

Versión Estándar Fecha de lanzamiento

C ++ 98 ISO / IEC 14882: 1998 1998-09-01

C ++ 03 ISO / IEC 14882: 2003 2003-10-16

C ++ 11 ISO / IEC 14882: 2011 2011-09-01

C ++ 14 ISO / IEC 14882: 2014 2014-12-15

C ++ 17 TBD 2017-01-01

C ++ 20 TBD 2020-01-01

Examples
Hola Mundo

Este programa imprime Hello World! al flujo de salida estándar:

#include <iostream>

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

Véalo en vivo en Coliru .

https://riptutorial.com/es/home 2
Análisis
Examinemos cada parte de este código en detalle:

• es una directiva de preprocesador que incluye el contenido del archivo


#include <iostream>
de cabecera estándar de C ++ iostream .

iostream es un archivo de encabezado de biblioteca estándar que contiene definiciones


de los flujos de entrada y salida estándar. Estas definiciones se incluyen en el std nombres
std , que se explica a continuación.

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:

○ stdes un espacio de nombres , y :: es el operador de resolución de alcance que


permite buscar objetos por nombre dentro de un espacio de nombres.

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 .

○ std::cout es el objeto de flujo de salida estándar , definido en iostream , y se imprime


en la salida estándar ( stdout ).

○ << 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.

○ "Hello World!" es una cadena de caracteres literal , o un "texto literal". El operador


de inserción de flujo para los literales de cadena de caracteres se define en el archivo
iostream .

○ std::endl es un objeto especial de manipulador de flujo de E / S , también definido


en el archivo iostream . Insertar un manipulador en un flujo cambia el estado del flujo.

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).

Un método alternativo que evita la descarga es:

std::cout << "Hello World!\n";

donde \n es la secuencia de escape de caracteres para el carácter de nueva línea.

○ El punto y coma ( ; ) notifica al compilador que una declaración ha finalizado. Todas


las declaraciones de C ++ y las definiciones de clase requieren un punto y coma
finalizado.

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.

Hay dos tipos de comentarios en C ++:

Comentarios de una sola línea


La secuencia de doble barra diagonal hacia adelante // marcará todo el texto hasta que aparezca
una nueva línea como comentario:

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;
}

En cualquier comentario de bloque, puedes escribir lo que quieras. Cuando el compilador


encuentra el símbolo */ , termina el comentario de bloque:

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:

void SomeFunction(/* argument 1 */ int a, /* argument 2 */ int b);

Importancia de los comentarios


Al igual que con todos los lenguajes de programación, los comentarios proporcionan varios
beneficios:

• Documentación explícita de código para facilitar la lectura / mantenimiento


• Explicación del propósito y funcionalidad del código.
• Detalles sobre la historia o razonamiento detrás del código.
• Colocación de derechos de autor / licencias, notas de proyectos, agradecimientos
especiales, créditos de contribuyentes, etc. directamente en el código fuente.

https://riptutorial.com/es/home 5
Sin embargo, los comentarios también tienen sus desventajas:

• Deben mantenerse para reflejar cualquier cambio en el código.


• Los comentarios excesivos tienden a hacer que el código sea menos legible

La necesidad de comentarios se puede reducir escribiendo un código claro y autodocumentado.


Un ejemplo simple es el uso de nombres explicativos para variables, funciones y tipos. Factorizar
tareas relacionadas lógicamente en funciones discretas va de la mano con esto.

Marcadores de comentario utilizados para


deshabilitar el código
Durante el desarrollo, los comentarios también se pueden usar para deshabilitar rápidamente
partes del código sin borrarlo. A menudo, esto es útil para propósitos de prueba o depuración,
pero no es un buen estilo para nada que no sean ediciones temporales. Esto a menudo se
conoce como "comentar".

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.

• Nota: las funciones de C ++ son esencialmente procedimientos y no siguen la definición


exacta o las reglas de las funciones matemáticas.

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)

En el ejemplo anterior, la función int add2(int i) declara lo siguiente al compilador:

• El tipo de retorno es int .


• El nombre de la función es add2 .
• El número de argumentos a la función es 1:
○ El primer argumento es del tipo int .
○ El primer argumento se mencionará en el contenido de la función con el nombre i .

El nombre del argumento es opcional; La declaración para la función también podría ser la
siguiente:

int add2(int); // Omitting the function arguments' name is also permitted.

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>

int add2(int i); // Declaration of add2

// Note: add2 is still missing a DEFINITION.


// Even though it doesn't appear directly in code,

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;
}

Aquí, add2(2) es la sintaxis de una llamada de función.

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.

Un ejemplo de una definición de función para add2 podría ser:

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."

int j = i + 2; // Definition of a variable j as the value of i+2.


return j; // Returning or, in essence, substitution of j for a function call to
// add2.
}

Sobrecarga de funciones
Puedes crear múltiples funciones con el mismo nombre pero diferentes parámetros.

int add2(int i) // Code contained in this definition will be evaluated


{ // when add2() is called with one parameter.
int j = i + 2;
return j;
}

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.

int multiply(int a, int b = 7); // b has default value of 7.


int multiply(int a, int b)
{
return a * b; // If multiply() is called with one parameter, the
} // value will be multiplied by the default, 7.

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:

int multiply(int a = 10, int b = 20); // This is legal


int multiply(int a = 10, int b); // This is illegal since int a is in the former

Llamadas de Funciones Especiales -


Operadores
Existen llamadas a funciones especiales en C ++ que tienen una sintaxis diferente a
name_of_function(value1, value2, value3) . El ejemplo más común es el de los operadores.

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

es equivalente a la siguiente llamada de función:

operator+(3, 3)

Todos los nombres de funciones del operador comienzan con el operator .

Mientras que en el predecesor inmediato de C ++, C, a los nombres de funciones de operador no


se les pueden asignar diferentes significados al proporcionar definiciones adicionales con
diferentes tipos de firmas, en C ++, esto es válido. "Ocultar" las definiciones de funciones
adicionales bajo un nombre de función único se conoce como sobrecarga de operadores en C
++, y es una convención relativamente común, pero no universal, en C ++.

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
}

void foo(int x) // this later definition is not known in main


{
}

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:

void foo(int x) {} //Declare the foo function and body first

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:

void foo(int); // Prototype declaration of foo, seen by main


// Must specify return type, name, and argument list types
int main()
{
foo(2); // OK: foo is known, called even though its body is not yet defined
}

void foo(int x) //Must match the prototype


{
// Define body of foo here
}

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

y luego proporcionar la definición completa en otro lugar:

// foo.cpp --> foo.o

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 :

// main.cpp --> main.o


#include "foo.h" // foo's prototype declaration is "hidden" in here
int main() { foo(2); } // foo is valid to call because its prototype declaration was
beforehand.
// the prototype and body definitions of foo are linked through the object files

Se produce un error de "símbolo externo no resuelto" cuando existen la función prototipo y la


llamada , pero el cuerpo de la función no está definido. Estos pueden ser más difíciles de resolver
ya que el compilador no informará el error hasta la etapa final de vinculación, y no sabe a qué
línea saltar en el código para mostrar el error.

El proceso de compilación estándar de C ++.

El código del programa ejecutable de C ++ generalmente es producido por un compilador.

Un compilador es un programa que traduce código de un lenguaje de programación a otra forma


que es (más) directamente ejecutable para una computadora. Usar un compilador para traducir
código se llama compilación.

C ++ hereda la forma de su proceso de compilación de su lenguaje "principal", C. A continuación


se muestra una lista que muestra los cuatro pasos principales de la compilación en C ++:

1. El preprocesador de C ++ copia el contenido de cualquier archivo de encabezado incluido


en el archivo de código fuente, genera un código de macro y reemplaza las constantes
simbólicas definidas usando #define con sus valores.
2. El archivo de código fuente expandido producido por el preprocesador C ++ se compila en
lenguaje ensamblador apropiado para la plataforma.
3. El código del ensamblador generado por el compilador se ensambla en el código de objeto
apropiado para la plataforma.
4. El archivo de código de objeto generado por el ensamblador está vinculado junto con los
archivos de código de objeto para cualquier función de biblioteca utilizada para producir un
archivo ejecutable.

• 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

El preprocesador es una parte importante del compilador.

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

La primera directiva de preprocesador que encontrará es probablemente la

#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.

La siguiente directiva importante es probablemente la

#define something something_else

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.

Lea Empezando con C ++ en línea: https://riptutorial.com/es/cplusplus/topic/206/empezando-con-


c-plusplus

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

La adquisición de recursos es la inicialización (RAII) es un lenguaje común en la gestión de


recursos. En el caso de la memoria dinámica, utiliza punteros inteligentes para llevar a cabo la
gestión de recursos. Cuando se usa RAII, un recurso adquirido se otorga de inmediato a un
puntero inteligente o administrador de recursos equivalente. Solo se puede acceder al recurso a
través de este administrador, por lo que el administrador puede realizar un seguimiento de varias
operaciones. Por ejemplo, std::auto_ptr libera automáticamente su recurso correspondiente
cuando queda fuera del alcance o se elimina de otro modo.

#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

El principal problema de std::auto_ptr es que no se puede copiar sin transferir la propiedad:

#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
}

Debido a estas extrañas semánticas de copia, std::auto_ptr no se puede usar en contenedores,

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

Mutexes y seguridad de rosca

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

Tiempo Paso 1 Lee 1 de la variable

Tiempo Paso 2 Lee 1 de la variable

Tiempo Paso 3 Agrega 1 más 1 para obtener 2

Tiempo Paso 4 Agrega 1 más 1 para obtener 2

Tiempo paso 5 Almacenar 2 en variable

Tiempo Paso 6 Almacenar 2 en variable

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.

Un mutex (acrónimo de mut UAL ex conclusión) es un objeto de la gestión de recursos diseñado


para resolver este tipo de problema. Cuando un hilo quiere acceder a un recurso, "adquiere" la

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

std::mutex es la implementación de C ++ 11 de un mutex.

#include <thread>
#include <mutex>
#include <iostream>
using namespace std;

void add_1(int& i, const mutex& m) { // function to be run in thread


m.lock();
i += 1;
m.unlock();
}

int main() {
int var = 1;
mutex m;

cout << var << endl; // prints 1

thread t1(add_1, var, m); // create thread with arguments


thread t2(add_1, var, m); // create another thread
t1.join(); t2.join(); // wait for both threads to finish

cout << var << endl; // prints 3


}

Lea Administracion de recursos en línea:


https://riptutorial.com/es/cplusplus/topic/8336/administracion-de-recursos

https://riptutorial.com/es/home 16
Capítulo 3: Alcances
Examples
Alcance de bloque simple

El alcance de una variable en un bloque { ... } , comienza después de la declaración y termina


al final del bloque. Si hay un bloque anidado, el bloque interno puede ocultar el alcance de una
variable que se declara en el bloque externo.

{
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;

std::cout << x; // <- Output is 200


}

std::cout << x; // <- Output is 100


}

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__

extern int circle_radius; // Promise to the compiler that circle_radius


// will be defined somewhere

#endif

https://riptutorial.com/es/home 17
// File foo1.cpp:

#include "my_globals.h"

int circle_radius = 123; // Defining the extern variable

// File main.cpp:

#include "my_globals.h"
#include <iostream>

int main()
{
std::cout << "The radius is: " << circle_radius << "\n";'
return 0;
}

Salida:

The radius is: 123

Lea Alcances en línea: https://riptutorial.com/es/cplusplus/topic/3453/alcances

https://riptutorial.com/es/home 18
Capítulo 4: Algoritmos de la biblioteca
estándar
Examples
std :: for_each

template<class InputIterator, class Function>


Function for_each(InputIterator first, InputIterator last, Function f);

Efectos:

Aplica f al resultado de la desreferenciación de todos los iteradores en el rango [first, last)


partir del first y continúa hasta el last - 1 .

Parámetros:

first, last - el rango para aplicar f a.

f - objeto llamable que se aplica al resultado de la anulación de la referencia de cada iterador en


el rango [first, last) .

Valor de retorno:

f (hasta C ++ 11) y std::move(f) (desde C ++ 11).

Complejidad:

Se aplica f exactamente el last - first vez.

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

template< class Iterator >


bool next_permutation( Iterator first, Iterator last );
template< class Iterator, class Compare >
bool next_permutation( Iterator first, Iterator last, Compare cmpFun );

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 :

std::vector< int > v { 1, 2, 3 };


do
{
for( int i = 0; i < v.size(); i += 1 )
{
std::cout << v[i];
}
std::cout << std::endl;
}while( std::next_permutation( v.begin(), v.end() ) );

Imprima todos los casos de permutación de 1,2,3 en orden lexicográficamente creciente.


salida:

123
132
213
231
312
321

std :: acumular

Definido en el encabezado <numeric>

template<class InputIterator, class T>


T accumulate(InputIterator first, InputIterator last, T init); // (1)

template<class InputIterator, class T, class BinaryOperation>


T accumulate(InputIterator first, InputIterator last, T init, BinaryOperation f); // (2)

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:

first, last - el rango para aplicar f a.


init - valor inicial del acumulador.
f - función de plegado binario.

Valor de retorno:

Valor acumulado de f aplicaciones.

Complejidad:

O (n × k) , donde n es la distancia de la first a la last , O (k) es la complejidad de la función f .

Ejemplo:

Ejemplo de suma simple:

std::vector<int> v { 2, 3, 4 };
auto sum = std::accumulate(v.begin(), v.end(), 1);
std::cout << sum << std::endl;

Salida:

10

Convertir dígitos a número:

c ++ 11

class Converter {
public:
int operator()(int a, int d) const { return a * 10 + d; }
};

y después

const int ds[3] = {1, 2, 3};


int n = std::accumulate(ds, ds + 3, 0, Converter());
std::cout << n << std::endl;

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

template <class InputIterator, class T>


InputIterator find (InputIterator first, InputIterator last, const T& val);

Efectos

Encuentra la primera aparición de val dentro del rango [primero, último)

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>

using namespace std;

int main(int argc, const char * argv[]) {

//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

//a value not in the vector


itr_50 = find(intVec.begin(), intVec.end(), 50); //does not occur

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

first occurence of: 9


only occurence of: 43
element after first 9: 10
last element: 48

std :: cuenta

template <class InputIterator, class T>


typename iterator_traits<InputIterator>::difference_type
count (InputIterator first, InputIterator last, const T& val);

Efectos

Cuenta el número de elementos que son iguales a val.

Parámetros

first => iterador que apunta al comienzo del rango


last => iterador que apunta al final del rango
val => La ocurrencia de este valor en el rango será contada

Regreso

El número de elementos en el rango que son iguales (==) a val.

Ejemplo

#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

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};

//count occurences of 9, 55, and 101


size_t count_9 = count(intVec.begin(), intVec.end(), 9); //occurs twice
size_t count_55 = count(intVec.begin(), intVec.end(), 55); //occurs once
size_t count_101 = count(intVec.begin(), intVec.end(), 101); //occurs once

//print result
cout << "There are " << count_9 << " 9s"<< endl;
cout << "There is " << count_55 << " 55"<< endl;
cout << "There is " << count_101 << " 101"<< ends;

//find the first element == 4 in the vector


vector<int>::iterator itr_4 = find(intVec.begin(), intVec.end(), 4);

//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

template <class InputIterator, class UnaryPredicate>


typename iterator_traits<InputIterator>::difference_type
count_if (InputIterator first, InputIterator last, UnaryPredicate red);

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>

using namespace std;

/*
Define a few functions to use as predicates
*/

//return true if number is odd


bool isOdd(int i){
return i%2 == 1;
}

//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;
}
};

int main(int argc, const char * argv[]) {

//create a vector
vector<int> myvec = {1,5,8,0,7,6,4,5,2,1,5,0,6,9,7};

//using a lambda function to count even numbers


size_t evenCount = count_if(myvec.begin(), myvec.end(), [](int i){return i % 2 == 0;}); //
>= C++11

//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);

//using a functor to count numbers greater than 5


size_t greaterCount = count_if(myvec.begin(), myvec.end(), Greater(5));

cout << "vector size: " << myvec.size() << endl;


cout << "even numbers: " << evenCount << " found" << endl;
cout << "odd numbers: " << oddCount << " found" << endl;
cout << "numbers > 5: " << greaterCount << " found"<< endl;

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

template <class InputIterator, class UnaryPredicate>


InputIterator find_if (InputIterator first, InputIterator last, UnaryPredicate pred);

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>

using namespace std;

/*
define some functions to use as predicates
*/

//Returns true if x is multiple of 10


bool multOf10(int x) {
return x % 10 == 0;
}

//returns true if item greater than passed in parameter


class Greater {
int _than;

public:
Greater(int th):_than(th){

}
bool operator()(int data) const
{
return data > _than;
}
};

int main()
{

vector<int> myvec {2, 5, 6, 10, 56, 7, 48, 89, 850, 7, 456};

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 a function pointer


vector<int>::iterator pow10 = find_if(myvec.begin(), myvec.end(), multOf10);

//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()

//check if pointer points to myvec.end()


if(nf != myvec.end()) {
cout << "nf points to: " << *nf << endl;
}
else {
cout << "item not found" << endl;
}

cout << "First item > 10: " << *gt10 << endl;
cout << "First Item n * 10: " << *pow10 << endl;
cout << "First Item > 5: " << *gt5 << endl;

return 0;
}

Salida

item not found


First item > 10: 56
First Item n * 10: 10
First Item > 5: 6

std :: min_element

template <class ForwardIterator>


ForwardIterator min_element (ForwardIterator first, ForwardIterator last);

template <class ForwardIterator, class Compare>


ForwardIterator min_element (ForwardIterator first, ForwardIterator last,Compare comp);

Efectos

Encuentra el elemento mínimo en un rango.

Parámetros

first - iterador que apunta al comienzo del rango


last : iterador que apunta al final del rango comp : un puntero de función u objeto de función que

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

Iterador al elemento mínimo en el rango.

Complejidad

Lineal en uno menos que el número de elementos comparados.

Ejemplo

#include <iostream>
#include <algorithm>
#include <vector>
#include <utility> //to use make_pair

using namespace std;

//function compare two pairs


bool pairLessThanFunction(const pair<string, int> &p1, const pair<string, int> &p2)
{
return p1.second < p2.second;
}

int main(int argc, const char * argv[]) {

vector<int> intVec {30,200,167,56,75,94,10,73,52,6,39,43};

vector<pair<string, int>> pairVector = {make_pair("y", 25), make_pair("b", 2),


make_pair("z", 26), make_pair("e", 5) };

// default using < operator


auto minInt = min_element(intVec.begin(), intVec.end());

//Using pairLessThanFunction
auto minPairFunction = min_element(pairVector.begin(), pairVector.end(),
pairLessThanFunction);

//print minimum of intVector


cout << "min int from default: " << *minInt << endl;

//print minimum of pairVector


cout << "min pair from PairLessThanFunction: " << (*minPairFunction).second << endl;

return 0;
}

Salida

min int from default: 6


min pair from PairLessThanFunction: 2

https://riptutorial.com/es/home 28
Usando std :: nth_element para encontrar la mediana (u otros cuantiles)

El algoritmo std::nth_element toma tres iteradores: un iterador al principio, a la posición n y al final.


Una vez que la función devuelve, el n-ésimo elemento (por orden) será el n-ésimo elemento más
pequeño. (La función tiene sobrecargas más elaboradas, p. Ej., Algunos tienen funciones de
comparación; consulte el enlace anterior para ver todas las variaciones).

Nota Esta función es muy eficiente, tiene una complejidad lineal.

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> v{5, 1, 2, 3, 4};

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);

// This makes the 2nd position hold the median.


std::nth_element(b, med, e);

// The median is now at v[2].

Para encontrar el p th cuantil , cambiaríamos algunas de las líneas de arriba:

const std::size_t pos = p * std::distance(b, e);

std::advance(nth, pos);

y buscar el cuantil en pos .

Lea Algoritmos de la biblioteca estándar en línea:


https://riptutorial.com/es/cplusplus/topic/3177/algoritmos-de-la-biblioteca-estandar

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:

• , donde x es una expresión constante, le da a la entidad la alineación x , si es


alignas(x)
compatible.
• alignas(T) , donde T es un tipo, le da a la entidad una alineación igual al requisito de
alineación de T , es decir, alignof(T) , si es compatible.

Si se aplican múltiples especificadores alignas a la misma entidad, se aplica el más estricto.

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(int) unsigned char buf[sizeof(int)];


new (buf) int(42);

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(1) int i; //Il-formed, unless `int` on this platform is aligned to 1 byte.


alignas(char) int j; //Il-formed, unless `int` has the same or smaller alignment than `char`.

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.

Lea Alineación en línea: https://riptutorial.com/es/cplusplus/topic/9249/alineacion

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:

std::istream para leer texto.

std::ostream para escribir texto.

std::streambuf para leer o escribir personajes.

La entrada formateada utiliza el operator>> .

La salida formateada utiliza el operator<< .

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.

Más sobre streams: <iostream> Library

Examples
Abriendo un archivo

La apertura de un archivo se realiza de la misma manera para las 3 secuencias de archivos (


ifstream , ofstream y fstream ).

Puedes abrir el archivo directamente en el constructor:

std::ifstream ifs("foo.txt"); // ifstream: Opens file "foo.txt" for reading only.

std::ofstream ofs("foo.txt"); // ofstream: Opens file "foo.txt" for writing only.

std::fstream iofs("foo.txt"); // fstream: Opens file "foo.txt" for reading and writing.

Alternativamente, puede usar la función de miembro open() stream open() :

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:

// Try to read the file 'foo.txt'.


std::ifstream ifs("fooo.txt"); // Note the typo; the file can't be opened.

// Check if the file has been opened successfully.


if (!ifs.is_open()) {
// The file hasn't been opened; take appropriate actions here.
throw CustomException(ifs, "File could not be opened");
}

Cuando la ruta del archivo contenga barras diagonales inversas (por ejemplo, en el sistema
Windows), debe eliminarlas correctamente:

// Open the file 'c:\folder\foo.txt' on Windows.


std::ifstream ifs("c:\\folder\\foo.txt"); // using escaped backslashes

C ++ 11

o usar el literal en bruto:

// Open the file 'c:\folder\foo.txt' on Windows.


std::ifstream ifs(R"(c:\folder\foo.txt)"); // using raw literal

o use barras diagonales en su lugar:

// Open the file 'c:\folder\foo.txt' on Windows.


std::ifstream ifs("c:/folder/foo.txt");

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 :

// Open the file 'пример\foo.txt' on Windows.


std::ifstream ifs(LR"(пример\foo.txt)"); // using wide characters with raw literal

Leyendo de un archivo

Hay varias formas de leer datos 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:

John Doe 25 4 6 1987


Jane Doe 15 5 24 1976

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;

// Sets position to the end of the file.


is.seekg(0, std::ios::end);

// Reserves memory for the file.


whole_file.reserve(is.tellg());

https://riptutorial.com/es/home 34
// Sets position to the start of the file.
is.seekg(0, std::ios::beg);

// Sets contents of 'whole_file' to all characters in the file.


whole_file.assign(std::istreambuf_iterator<char>(is),
std::istreambuf_iterator<char>());

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");

// The function getline returns false if there are no more lines.


for (std::string str; std::getline(is, str);) {
// Process the line that has been read.
}

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];

// Read 4 characters from the file.


is.read(str, 4);

Después de ejecutar un comando de lectura, siempre debe verificar si el indicador de estado de


error failbit se ha establecido, ya que indica si la operación falló o no. Esto se puede hacer
llamando a la función miembro de la secuencia de archivos fail() :

is.read(str, 4); // This operation might fail for any reason.

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";

// Writes 3 characters from data -> "Foo".


os.write(data, 3);
}

Después de escribir en un flujo, siempre debe verificar si se ha establecido el indicador de estado


de error badbit , ya que indica si la operación falló o no. Esto se puede hacer llamando a la
función miembro del flujo de salida del archivo bad() :

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.

(Todos los modos se pueden encontrar en el std::ios nombres std::ios ).

Se puede proporcionar un modo de apertura como segundo parámetro para el constructor de un


flujo de archivos o para su función miembro open() :

std::ofstream os("foo.txt", std::ios::out | std::ios::trunc);

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.

Si no especifica un modo de apertura, se utilizan los siguientes modos predeterminados:

• ifstream - in
• ofstream - out
• fstream - in y out

Los modos de apertura de archivos que puede especificar por diseño son:

Modo Sentido por Descripción

app adjuntar Salida Anexa datos al final del archivo.

binary binario De entrada y salida La entrada y salida se realiza en binario.

in entrada Entrada Abre el archivo para su lectura.

out salida Salida Abre el archivo para escribir.

https://riptutorial.com/es/home 36
Modo Sentido por Descripción

trunc truncar De entrada y salida Elimina el contenido del archivo al abrirlo.

ate al final Entrada Va al final del archivo al abrir.

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.

Por ejemplo, en Windows, la secuencia de final de línea es CRLF ( "\r\n" ).


Escribe: "\n" => "\r\n"
Lee: "\r\n" => "\n"

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 ( {} ):

std::string const prepared_data = prepare_data();


{
// Open a file for writing.
std::ofstream output("foo.txt");

// 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:

// Open the file "foo.txt" for the first time.


std::ofstream output("foo.txt");

// Get some data to write from somewhere.


std::string const prepared_data = prepare_data();

// Write data to the file "foo.txt".


output << prepared_data;

// Close the file "foo.txt".


output.close();

// 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;

// Close the file "foo.txt" once again.


output.close();

Flushing un arroyo

Las secuencias de archivos se almacenan en búfer de forma predeterminada, al igual que


muchos otros tipos de secuencias. Esto significa que las escrituras en la secuencia pueden no
causar que el archivo subyacente cambie inmediatamente. A fin de forzar que todas las escrituras
en búfer se realicen inmediatamente, puede vaciar la secuencia. Puede hacerlo directamente
invocando el método flush() o mediante el manipulador de flujo std::flush :

std::ofstream os("foo.txt");
os << "Hello World!" << std::flush;

char data[3] = "Foo";


os.write(data, 3);
os.flush();

Hay un manipulador de flujo std::endl que combina la escritura de una nueva línea con la
descarga de flujo:

// Both following lines do the same thing


os << "Hello World!\n" << std::flush;
os << "Hello world!" << std::endl;

El almacenamiento en búfer puede mejorar el rendimiento de la escritura en una secuencia. Por lo


tanto, las aplicaciones que escriben mucho deben evitar el lavado innecesario. Por el contrario, si
la E / S se realiza con poca frecuencia, las aplicaciones deben considerar vaciar con frecuencia
para evitar que los datos se atasquen en el objeto de flujo.

Leyendo un archivo ASCII en un std :: string

std::ifstream f("file.txt");

if (f)
{
std::stringstream buffer;
buffer << f.rdbuf();
f.close();

// The content of "file.txt" is available in the string `buffer.str()`


}

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<< .

Otra posibilidad (popularizada en Effective STL por Scott Meyers ) es:

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 .

Por último, si bien no menos importante:

std::ifstream f("file.txt");

if (f)
{
f.seekg(0, std::ios::end);
const auto size = f.tellg();

std::string str(size, ' ');


f.seekg(0);
f.read(&str[0], size);
f.close();

// Operations on `str`...
}

que es probablemente la opción más rápida (entre las tres propuestas).

Leyendo un archivo en un contenedor

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:

// Unfortunately there is no built in type that reads line using >>


// So here we build a simple helper class to do it. That will convert
// back to a string when used in string context.
struct Line
{
// Store data here
std::string data;
// Convert object to string
operator std::string const&() const {return data;}
// Read a line from a stream.
friend std::istream& operator>>(std::istream& stream, Line& line)
{
return std::getline(stream, line.data);
}
};

std::ifstream file("file3.txt");

// Read the lines of a file into a container.


std::vector<std::string> v(std::istream_iterator<Line>{file},
std::istream_iterator<Line>{});

Leyendo un `struct` desde un archivo de texto formateado.

C ++ 11

struct info_type
{
std::string name;
int age;
float height;

// we define an overload of operator>> as a friend function which


// gives in privileged access to private data members
friend std::istream& operator>>(std::istream& is, info_type& info)
{
// skip whitespace
is >> std::ws;
std::getline(is, info.name);
is >> info.age;
is >> info.height;
return is;
}

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);
}

for(auto const& info: v)


{
std::cout << " name: " << info.name << '\n';
std::cout << " age: " << info.age << " years" << '\n';
std::cout << "height: " << info.height << "lbs" << '\n';
std::cout << '\n';
}
}

file4.txt

Wogger Wabbit
2
6.2
Bilbo Baggins
111
81.3
Mary Poppins
29
154.8

Salida:

name: Wogger Wabbit


age: 2 years
height: 6.2lbs

name: Bilbo Baggins


age: 111 years
height: 81.3lbs

name: Mary Poppins


age: 29 years
height: 154.8lbs

Copiando un archivo

std::ifstream src("source_filename", std::ios::binary);


std::ofstream dst("dest_filename", std::ios::binary);
dst << src.rdbuf();

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");

La biblioteca del sistema de archivos se desarrolló originalmente como boost.filesystem y


finalmente se fusionó con ISO C ++ a partir de C ++ 17.

¿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;

// What if *only* now the eof / fail bit is set?

/* Use `buffer` */
}

Podrías escribir correctamente:

while (!f.eof())
{
f >> buffer >> std::ws;

if (f.fail())
break;

/* Use `buffer` */
}

pero

while (f >> buffer)


{
/* Use `buffer` */
}

Es más sencillo y menos propenso a errores.

Otras referencias:

• std::ws : descarta los espacios en blanco iniciales de un flujo de entrada


• std::basic_ios::fail : devuelve true si se ha producido un error en la secuencia asociada

Escribir archivos con configuraciones locales no estándar

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:

• Siempre debe aplicar un local a una secuencia antes de abrir el archivo.


• Una vez que se haya imbuido el flujo, no debe cambiar la configuración regional.

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;

// Write a floating-point value using the user's preferred locale.


std::ofstream ofs1;
ofs1.imbue(std::locale(""));
ofs1.open("file1.txt");
ofs1 << 78123.456 << std::endl;

// Use a specific locale (names are system-dependent)


std::ofstream ofs2;
ofs2.imbue(std::locale("en_US.UTF-8"));
ofs2.open("file2.txt");
ofs2 << 78123.456 << std::endl;

// Switch to the classic "C" locale


std::ofstream ofs3;
ofs3.imbue(std::locale::classic());
ofs3.open("file3.txt");
ofs3 << 78123.456 << 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

(note la coma decimal en la primera línea).

Lea Archivo I / O en línea: https://riptutorial.com/es/cplusplus/topic/496/archivo-i---o

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:

Un archivo de encabezado es un archivo que contiene declaraciones C y definiciones


de macro (consulte Macros) que se compartirán entre varios archivos de origen.
Solicita el uso de un archivo de encabezado en su programa incluyéndolo, con la
directiva de preprocesamiento de C '#include'.

Los archivos de encabezado tienen dos propósitos.

• 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.

Sin embargo, para el preprocesador de C en sí, un archivo de encabezado no es diferente de un


archivo de origen.

El esquema de organización del archivo de encabezado / fuente es simplemente una convención


estándar y fuertemente establecida por varios proyectos de software con el fin de proporcionar
separación entre la interfaz y la implementación.

Aunque el propio estándar de C ++ no lo impone formalmente, seguir la convención del archivo


fuente / encabezado es altamente recomendable y, en la práctica, ya es casi ubicuo.

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

/* Note how this header contains only a declaration of a function.


* Header functions usually do not define implementations for declarations
* unless code must be further processed at compile time, as in templates.
*/

/* 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

// global_value and my_function() will be


// recognized as the same constructs if this header is included by different files.
const int global_value = 42;
int my_function();

#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

#include <iostream> // A C++ Standard Library header.


#include "my_function.h" // A personal header

int main(int argc, char** argv) {


std::cout << my_function() << std::endl;
return 0;

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.

Suponiendo que el archivo de encabezado y el archivo de código fuente ya están en el mismo


directorio, un programador ejecutaría los siguientes comandos:

g++ -c my_function.cpp # Compiles the source file my_function.cpp


# --> object file my_function.o

g++ main.cpp my_function.o # Links the object file containing the


# implementation of int my_function()
# to the compiled, object version of main.cpp
# and then produces the final executable a.out

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

g++ main.o my_function.o

Plantillas en archivos de encabezado

Las plantillas requieren la generación de código en tiempo de compilación: una función de


plantilla, por ejemplo, se convertirá efectivamente en múltiples funciones distintas una vez que
una función de plantilla esté parametrizada por el uso en el código fuente.

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.

Por lo tanto, el código de plantilla, si se coloca en encabezados, también debe contener su


definición. Un ejemplo de esto es a continuación:

// templated_function.h

template <typename T>


T* null_T_pointer() {
T* type_point = NULL; // or, alternatively, nullptr instead of NULL
// for C++11 or later
return type_point;
}

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.

Dos cosas suceden que hacen esto falso:

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 .

El ejemplo clásico es el siguiente cálculo:

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.

//64-bit floats have 53 digits of precision, including the whole-number-part.


double a = 0011111110111001100110011001100110011001100110011001100110011010; //imperfect

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!

Lea Aritmética de punto flotante en línea: https://riptutorial.com/es/cplusplus/topic/5115/aritmetica-


de-punto-flotante

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.

#include <stddef.h> // size_t, ptrdiff_t

//----------------------------------- Machinery:

using Size = ptrdiff_t;

template< class Item, size_t n >


constexpr auto n_items( Item (&)[n] ) noexcept
-> Size
{ return n; }

//----------------------------------- 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";
}

El lenguaje C para tamaño de matriz, sizeof(a)/sizeof(a[0]) , aceptará un puntero como


argumento y, por lo general, dará un resultado incorrecto.

Para C ++ 11

usando C ++ 11 puedes hacer:

std::extent<decltype(MyArray)>::value;

https://riptutorial.com/es/home 51
Ejemplo:

char MyArray[] = { 'X','o','c','e' };


const auto n = std::extent<decltype(MyArray)>::value;
std::cout << n << "\n"; // Prints 4

Hasta C ++ 17 (a partir de este escrito) C ++ no tenía un lenguaje central incorporado ni una


utilidad de biblioteca estándar para obtener el tamaño de una matriz, pero esto puede
implementarse pasando la matriz por referencia a una plantilla de función, como mostrado
anteriormente. Punto fino pero importante: el parámetro de tamaño de la plantilla es un size_t ,
algo inconsistente con el tipo de resultado de la función Size con signo, para acomodar el
compilador g ++ que a veces insiste en size_t para la coincidencia de la plantilla.

Con C ++ 17 y versiones posteriores, se puede usar std::size , que está especializado para
arreglos.

Matriz en bruto de tamaño dinámico

// Example of raw dynamic size array. It's generally better to use std::vector.
#include <algorithm> // std::sort
#include <iostream>
using namespace std;

auto int_from( istream& in ) -> int { int x; in >> x; return x; }

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.

for( int i = 1; i <= n; ++i )


{
cout << "The #" << i << " number, please: ";
a[i-1] = int_from( cin );
}

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[] ,

int* a = new int[n]; // ← Allocation of array of n items.

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 .

Expandiendo la matriz de tamaño dinámico usando std :: vector.

// Example of std::vector as an expanding dynamic size array.


#include <algorithm> // std::sort
#include <iostream>
#include <vector> // std::vector
using namespace std;

int int_from( std::istream& in ) { int x = 0; in >> x; return x; }

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.
}

sort( a.begin(), a.end() );


int const n = a.size();
for( int i = 0; i < n; ++i ) { cout << a[i] << ' '; }
cout << '\n';
}

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.

La complejidad de la secuencia de n operaciones push_back , incluida la copia o el movimiento


involucrado en las expansiones vectoriales, se amortiza O ( n ). "Amortizado": en promedio.

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).

// A fixed size raw array matrix (that is, a 2D raw array).


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

auto main() -> int


{
int const n_rows = 3;
int const n_cols = 7;
int const m[n_rows][n_cols] = // A raw array matrix.
{
{ 1, 2, 3, 4, 5, 6, 7 },
{ 8, 9, 10, 11, 12, 13, 14 },
{ 15, 16, 17, 18, 19, 20, 21 }
};

for( int y = 0; y < n_rows; ++y )


{
for( int x = 0; x < n_cols; ++x )
{
cout << setw( 4 ) << m[y][x]; // Note: do NOT use 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

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.

Como C ++ no proporciona soporte integrado para matrices de tamaño dinámico, aparte de la


asignación dinámica, una matriz de tamaño dinámico a menudo se implementa como una clase.
Luego, la notación de indexación de matriz sin procesar m[y][x] tiene algún costo, ya sea al
exponer la implementación (de modo que, por ejemplo, una vista de una matriz transpuesta se
vuelve prácticamente imposible) o al agregar un poco de sobrecarga y pequeños inconvenientes
cuando se hace al regresar un objeto proxy del operator[] . Y así, la notación de indexación para
tal abstracción puede y será generalmente diferente, tanto en el aspecto como en el orden de los
índices, por ejemplo, m(x,y) o m.at(x,y) o m.item(x,y) .

Una matriz de tamaño dinámico utilizando std :: vector para almacenamiento.

Desafortunadamente, a partir de C ++ 14 no hay una clase de matriz de tamaño dinámico en la


biblioteca estándar de C ++. Clases de matriz que apoyan el tamaño dinámico son sin embargo
disponibles a partir de una serie de bibliotecas 3 ª parte, incluyendo la biblioteca Boost Matrix (una
sub-biblioteca dentro de la biblioteca Boost).

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:

// A dynamic size matrix using std::vector for storage.

//--------------------------------------------- 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;

template< class Item >


class Matrix
{
private:

https://riptutorial.com/es/home 55
vector<Item> items_;
Size n_cols_;

auto index_for( Size const x, Size const y ) const


-> Size
{ return y*n_cols_ + x; }

public:
auto n_rows() const -> Size { return items_.size()/n_cols_; }
auto n_cols() const -> Size { return n_cols_; }

auto item( Size const x, Size const y )


-> Item&
{ return items_[index_for(x, y)]; }

auto item( Size const x, Size const y ) const


-> Item const&
{ return items_[index_for(x, y)]; }

Matrix(): n_cols_( 0 ) {}

Matrix( Size const n_cols, Size const n_rows )


: items_( n_cols*n_rows )
, n_cols_( n_cols )
{}

Matrix( initializer_list< initializer_list<Item> > const& values )


: items_()
, n_cols_( values.size() == 0? 0 : values.begin()->size() )
{
for( auto const& row : values )
{
assert( Size( row.size() ) == n_cols_ );
items_.insert( items_.end(), row.begin(), row.end() );
}
}
};
} // namespace my

//--------------------------------------------- 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];

Una matriz se puede declarar e inicializar al mismo tiempo así

int arrayOfInts[5] = {10, 20, 30, 40, 50};

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:

int arrayOfInts[] = {10, 20, 30, 40, 50};

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

double arrayOfDoubles[5] = {1.14159, 2.14159, 3.14159, 4.14159, 5.14159};

string arrayOfStrings[5] = { "C++", "is", "super", "duper", "great!"};

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

Lea Arrays en línea: https://riptutorial.com/es/cplusplus/topic/3017/arrays

https://riptutorial.com/es/home 58
Capítulo 10: Atributos
Sintaxis
• [[detalles]]: atributo simple sin argumentos

• [[detalles (argumentos)]]: Atributo con argumentos

• __attribute (detalles): No estándar GCC / Clang / IBM específico

• __declspec (detalles): no estándar MSVC específico

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]] :

[[noreturn]] void ownAssertFailureHandler(std::string message) {


std::cerr << message << std::endl;
if (THROW_EXCEPTION_ON_ASSERT)
throw AssertException(std::move(message));
std::terminate();
}

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:

std::vector<int> createSequence(int end) {


if (end > 0) {
std::vector<int> sequence;
sequence.reserve(end+1);
for (int i = 0; i <= end; ++i)
sequence.push_back(i);
return sequence;
}

https://riptutorial.com/es/home 59
ownAssertFailureHandler("Negative number passed to createSequence()"s);
// return std::vector<int>{}; //< Not needed because of [[noreturn]]
}

Es un comportamiento indefinido si la función realmente regresará, por lo que no se permite lo


siguiente:

[[noreturn]] void assertPositive(int number) {


if (number >= 0)
return;
else
ownAssertFailureHandler("Positive number expected"s); //< [[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; }
}

double fortyTwo = fortyTwoDivideBy<InconsistencyChangeToOne>(0);


double unreachable = fortyTwoDivideBy<InconsistencyThrower>(0);

Las siguientes funciones de biblioteca estándar tienen este atributo:

• 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.

A partir de C ++ 17, se introdujo un atributo estándar para indicar que la advertencia no es


necesaria cuando el código debe cumplirse. Los compiladores pueden dar advertencias de forma
segura cuando un caso finaliza sin break o [[fallthrough]] y tiene al menos una declaración.

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]] .

[[obsoleto]] y [[obsoleto ("motivo")]]

C ++ 14

C ++ 14 introdujo una forma estándar de desaprobar funciones a través de atributos.


[[deprecated]] se puede usar para indicar que una función está en desuso.
[[deprecated("reason")]] permite agregar una razón específica que puede ser mostrada por el
compilador.

void function(std::unique_ptr<A> &&a);

// 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);

// No message, will result in generic warning if called.


[[deprecated]]
void function(A *a);

Este atributo puede ser aplicado a:

• la declaracion de una clase


• un nombre de typedef
• una variable
• un miembro de datos no estáticos
• Una función
• una enumeración
• una plantilla de especialización

(ref. c ++ 14 borrador estándar : 7.6.5 Atributo obsoleto)

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:

• Definición de una función


• Un tipo

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);

void f(int &i) {


assert(i == 0); // Just to make comments clear!
++i; // i == 1
auto exit1 = onExit([&i]{ --i; }); // Reduce by 1 on exiting f()
++i; // i == 2
onExit([&i]{ --i; }); // BUG: Reducing by 1 directly
// Compiler warning expected
std::cout << i << std::end; // Expected: 2, Real: 1
}

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.

[[maybe_unused]] auto mapInsertResult = configuration.emplace("LicenseInfo",


stringifiedLicenseInfo);
assert(mapInsertResult.second); // We only get called during startup, so we can't be in the
map

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);
}

std::string createConfigFilePath(const std::string &relativePath) {


#if OS == "WINDOWS"
return createWindowsConfigFilePath(relativePath);
#elif OS == "LINUX"
return createLinuxConfigFilePath(relativePath);
#else
#error "OS is not yet supported"
#endif
}

Consulte la propuesta para obtener ejemplos más detallados sobre cómo se puede usar
[[maybe_unused]] .

Lea Atributos en línea: https://riptutorial.com/es/cplusplus/topic/5251/atributos

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
}

Ese viejo significado ahora se ha eliminado.

Examples
Muestra auto básica

La palabra clave auto proporciona la deducción automática del tipo de una variable.

Es especialmente conveniente cuando se trata de nombres tipográficos largos:

std::map< std::string, std::shared_ptr< Widget > > table;


// C++98
std::map< std::string, std::shared_ptr< Widget > >::iterator i = table.find( "42" );
// C++11/14/17
auto j = table.find( "42" );

con rango basado en bucles :

vector<int> v = {0, 1, 2, 3, 4, 5};


for(auto n: v)
std::cout << n << ' ';

con lambdas :

auto f = [](){ std::cout << "lambda\n"; };


f();

Para evitar la repetición del tipo:

auto w = std::make_shared< Widget >();

Para evitar copias sorprendentes e innecesarias:

https://riptutorial.com/es/home 64
auto myMap = std::map<int,float>();
myMap.emplace(1,3.14);

std::pair<int,float> const& firstPair2 = *myMap.begin(); // copy!


auto const& firstPair = *myMap.begin(); // no copy!

El motivo de la copia es que el tipo devuelto es en realidad std::pair<const int,float> !

Plantillas de auto y expresión

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.

auto, const, y referencias

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 .

std::vector<std::string> strings = { "stuff", "things", "misc" };


for(auto s : strings) {
std::cout << s << std::endl;
}

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;
}

En el cuerpo de este bucle, las modificaciones a s afectarán directamente el elemento de las


strings que hace referencia.

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:

for(const auto& s : strings) {


std::cout << s << std::endl;
}

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.

Tipo de retorno final

auto se utiliza en la sintaxis para el tipo de retorno final:

auto main() -> int {}

que es equivalente a

int main() {}

Mayormente útil combinado con decltype para usar parámetros en lugar de std::declval<T> :

template <typename T1, typename T2>


auto Add(const T1& lhs, const T2& rhs) -> decltype(lhs + rhs) { return lhs + rhs; }

Lambda genérica (C ++ 14)

C ++ 14

C ++ 14 permite usar auto en argumento lambda

auto print = [](const auto& arg) { std::cout << arg << std::endl; };

print(42);
print("hello world");

Esa lambda es mayormente equivalente a

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");

objetos de auto y proxy

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.

Como ejemplo, cuando se utilizan objetos proxy en el código:

std::vector<bool> flags{true, true, false};


auto flag = flags[0];
flags.push_back(true);

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.

Cuando flags.push_back(true) modifica el contenedor, esta pseudo-referencia podría terminar


colgando, refiriéndose a un elemento que ya no existe.

También hace posible la siguiente situación:

void foo(bool b);

std::vector<bool> getFlags();

auto flag = getFlags()[5];


foo(flag);

El vector se descarta inmediatamente, por lo que la flag es una pseudo-referencia a un elemento


que ha sido descartado. La llamada a foo invoca un comportamiento indefinido.

En casos como este, puede declarar una variable con auto e inicializarla mediante la conversión al
tipo que desea deducir:

auto flag = static_cast<bool>(getFlags()[5]);

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.

Lea auto en línea: https://riptutorial.com/es/cplusplus/topic/2421/auto

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:

vector<float> v = {0.4f, 12.5f, 16.234f};

for(auto val: v)
{
std::cout << val << " ";
}

std::cout << std::endl;

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
}
}

Este cambio se introdujo para el soporte planificado de Ranges TS en C ++ 20.

En este caso, nuestro bucle es equivalente a:

{
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).

Si desea referenciarlo, puede hacerlo:

vector<float> v = {0.4f, 12.5f, 16.234f};

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 :

const vector<float> v = {0.4f, 12.5f, 16.234f};

for(const float &val: v)


{
std::cout << val << " ";
}

Se utilizarían referencias de reenvío cuando el iterador de secuencia devuelva un objeto proxy y


usted necesite operar en ese objeto de forma no const . Nota: lo más probable es que confunda a
los lectores de su código.

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:

float arr[] = {0.4f, 12.5f, 16.234f};

for(auto val: arr)


{
std::cout << val << " ";
}

Tenga en cuenta que la asignación de una matriz dinámica no cuenta:

float *arr = new float[3]{0.4f, 12.5f, 16.234f};

for(auto val: arr) //Compile error.


{
std::cout << val << " ";
}

• 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];

// pointers are iterators


const float* begin() const {return &arr[0];}
const float* end() const {return &arr[3];}
float* begin() {return &arr[0];}
float* end() {return &arr[3];}

https://riptutorial.com/es/home 70
};

int main()
{
Rng rng = {{0.4f, 12.5f, 16.234f}};

for(auto val: rng)


{
std::cout << val << " ";
}
}

• 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];};

// pointers are iterators


const float* begin(const Rng &rng) {return &rng.arr[0];}
const float* end(const Rng &rng) {return &rng.arr[3];}
float* begin(Rng &rng) {return &rng.arr[0];}
float* end(Rng &rng) {return &rng.arr[3];}
}

int main()
{
Mine::Rng rng = {{0.4f, 12.5f, 16.234f}};

for(auto val: rng)


{
std::cout << val << " ";
}
}

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 .

Un bucle for se define de la siguiente manera:

for (/*initialization statement*/; /*condition*/; /*iteration execution*/)


{
// body of the loop
}

Explicación de las declaraciones del marcador de posición:

• 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 .

El equivalente aproximado de un for de bucle, reescrito como un while bucle es:

/*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:

for(int i = 0; i < 10; i++) {


std::cout << i << std::endl;
}

Un bucle válido también es:

for(int a = 0, b = 10, c = 20; (a+b+c < 100); c--, b++, a+=c) {


std::cout << a << " " << b << " " << c << std::endl;
}

Un ejemplo de ocultar variables declaradas antes de un bucle es:

int i = 99; //i = 99


for(int i = 0; i < 10; i++) { //we declare a new variable i
//some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 99

Pero si desea utilizar la variable ya declarada y no ocultarla, omita la parte de declaración:

int i = 99; //i = 99


for(i = 0; i < 10; i++) { //we are using already declared variable i
//some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 10

Notas:

• Las instrucciones de inicialización e incremento pueden realizar operaciones no


relacionadas con la declaración de condición, o nada en absoluto, si así lo desea. Pero por

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 .

Ejemplo de un bucle que cuenta de 0 a 10:

for (int counter = 0; counter <= 10; ++counter)


{
std::cout << counter << '\n';
}
// counter is not accessible here (had value 11 at the end)

Explicación de los fragmentos de código:

• 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.

Al dejar todas las declaraciones en blanco, puede crear un bucle infinito:

// infinite loop
for (;;)
std::cout << "Never ending!\n";

El while de bucle equivalente de la anterior es:

// 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:

std::vector<std::string> names = {"Albert Einstein", "Stephen Hawking", "Michael Ellis"};


for(std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
std::cout << *it << std::endl;
}

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 (int i = 0; i < 10)


//... The rest is the same

Para crear un bucle infinito, se puede usar la siguiente construcción:

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.

Declaración de variables en condiciones.

En la condición de la for y while bucles, también se permitió declarar un objeto. Se considerará


que este objeto está en el alcance hasta el final del bucle, y persistirá en cada iteración del bucle:

for (int i = 0; i < 5; ++i) {


do_something(i);
}
// i is no longer in scope.

for (auto& a : some_container) {


a.do_something();
}
// a is no longer in scope.

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

Nota: No olvide el punto y coma al final de while(condition); , que se necesita en la construcción


do-while .

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):

#define BAD_MACRO(x) f1(x); f2(x); f3(x);

// Only the call to f1 is protected by the condition here


if (cond) BAD_MACRO(var);

#define GOOD_MACRO(x) do { f1(x); f2(x); f3(x); } while(0)

// All calls are protected here


if (cond) GOOD_MACRO(var);

Declaraciones de control de bucle: romper y continuar

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.

La break comunicado termina un bucle sin ninguna consideración adicional.

for (int i = 0; i < 10; i++)


{
if (i == 4)
break; // this will immediately exit our loop
std::cout << i << '\n';
}

El código de arriba se imprimirá:

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).

for (int i = 0; i < 6; i++)


{
if (i % 2 == 0) // evaluates to true if i is even
continue; // this will immediately go back to the start of the loop
/* the next line will only be reached if the above "continue" statement

https://riptutorial.com/es/home 76
does not execute */
std::cout << i << " is an odd number\n";
}

El código de arriba se imprimirá:

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:

for (int i = 0; i < 4; i++)


{
std::cout << i << '\n';
}

El segundo ejemplo con continue podría reescribirse como:

for (int i = 0; i < 6; i++)


{
if (i % 2 != 0) {
std::cout << i << " is an odd number\n";
}
}

Rango-para sobre un sub-rango

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.

template<class Iterator, class Sentinel=Iterator>


struct range_t {
Iterator b;
Sentinel e;
Iterator begin() const { return b; }
Sentinel end() const { return e; }
bool empty() const { return begin()==end(); }
range_t without_front( std::size_t count=1 ) const {
if (std::is_same< std::random_access_iterator_tag, typename
std::iterator_traits<Iterator>::iterator_category >{} ) {
count = (std::min)(std::size_t(std::distance(b,e)), count);
}
return {std::next(b, count), e};
}
range_t without_back( std::size_t count=1 ) const {
if (std::is_same< std::random_access_iterator_tag, typename
std::iterator_traits<Iterator>::iterator_category >{} ) {
count = (std::min)(std::size_t(std::distance(b,e)), count);
}
return {b, std::prev(e, count)};

https://riptutorial.com/es/home 77
}
};

template<class Iterator, class Sentinel>


range_t<Iterator, Sentinel> range( Iterator b, Sentinal e ) {
return {b,e};
}
template<class Iterable>
auto range( Iterable& r ) {
using std::begin; using std::end;
return range(begin(r),end(r));
}

template<class C>
auto except_first( C& c ) {
auto r = range(c);
if (r.empty()) return r;
return r.without_front();
}

Ahora podemos hacer:

std::vector<int> v = {1,2,3,4};

for (auto i : except_first(v))


std::cout << i << '\n';

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 .

Lea Bucles en línea: https://riptutorial.com/es/cplusplus/topic/589/bucles

https://riptutorial.com/es/home 78
Capítulo 13: Búsqueda de nombre
dependiente del argumento
Examples
Que funciones se encuentran

Las funciones se encuentran al recopilar primero un conjunto de "clases asociadas" y "espacios


de nombres asociados" que incluyen uno o más de los siguientes, según el tipo de argumento T
Primero, mostremos las reglas para los nombres de especialización de clases, enumeración y de
plantilla de clase.

• Si T es una clase anidada, enumeración de miembros, entonces la clase circundante.


• Si T es una enumeración (que también puede ser un miembro de la clase!), El espacio de
nombres más interna de la misma.
• Si T es una clase (que también se pueden anidar!), Todas sus clases base y la propia clase.
El espacio de nombres más interno de todas las clases asociadas.
• Si T es un ClassTemplate<TemplateArguments> (¡esto también es una clase!), Las clases y los
espacios de nombres asociados con los argumentos de tipo de plantilla, el espacio de
nombres de cualquier argumento de plantilla de plantilla y la clase circundante de cualquier
argumento de plantilla de plantilla, si un argumento de plantilla es una plantilla de miembro.

Ahora hay algunas reglas para los tipos incorporados también

• Si T es un puntero a U o matriz de U , las clases y los espacios de nombres asociados con U


Ejemplo: void (*fptr)(A); f(fptr); , incluye los espacios de nombres y las clases asociadas
con void(A) (ver la siguiente regla).
• Si T es un tipo de función, las clases y los espacios de nombres asociados con los tipos de
parámetros y de retorno. Ejemplo: void(A) incluiría los espacios de nombres y las clases
asociadas con A
• Si T es un puntero a miembro, las clases y los espacios de nombres asociados con el tipo de
miembro (pueden aplicarse tanto a puntero a funciones de miembro como puntero a
miembro de datos). Ejemplo: BA::*p; void (A::*pf)(B); f(p); f(pf); incluye los espacios de
nombres y las clases asociadas con A , B , void(B) (que aplica la viñeta anterior para los tipos
de función).

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;

struct X { struct Y { }; friend void f(Y) { } };


void f(X p) { }
void f(std::shared_ptr<X> p) { }
}

// example calls
f(A::X());
f(A::X::Y());
f(std::make_shared<A::X>());

g(A::Z()); // invalid: "using namespace I;" is ignored!

Lea Búsqueda de nombre dependiente del argumento en línea:


https://riptutorial.com/es/cplusplus/topic/5163/busqueda-de-nombre-dependiente-del-argumento

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.

Considere el siguiente código corto:

#include <sstream>
#include <string>

using namespace std;

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:

ss << "the answer to everything is " << 42;

Después de eso, sin embargo, el flujo resultante se puede obtener de esta manera:

const string result = ss.str();

(el result la cadena será igual a "the answer to everything is 42" ).

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.
};

ostream &operator<<(ostream &os, const foo &f);

Para obtener la representación de cadena de un objeto foo ,

foo f;

podríamos usar

ostringstream ss;
ss << f;
const string result = ss.str();

Entonces el result contiene la representación de cadena del objeto foo .

Leyendo un archivo hasta el final.

Leyendo un archivo de texto línea por línea


Una forma adecuada de leer un archivo de texto línea por línea hasta el final generalmente no
está clara en la documentación de ifstream . Consideremos algunos errores comunes cometidos
por los programadores principiantes de C ++ y una forma adecuada de leer el archivo.

Líneas sin caracteres de espacios en blanco.


En aras de la simplicidad, supongamos que cada línea del archivo no contiene símbolos de
espacios en blanco.

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;
}

Líneas con caracteres de espacio en blanco.

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.

Leyendo un archivo en un búfer a la vez


Finalmente, leamos el archivo desde el principio hasta el final sin detenerse en ningún carácter,
incluidos los espacios en blanco y las nuevas líneas. Si sabemos que el tamaño exacto del
archivo o el límite superior de la longitud es aceptable, podemos cambiar el tamaño de la cadena
y luego leer:

s.resize(100);
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
s.begin());

De lo contrario, necesitamos insertar cada carácter al final de la cadena, por lo que


std::back_inserter es lo que necesitamos:

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:

std::ifstream ifs("1.txt", std::ios::binary);

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.

Imprimiendo colecciones con iostream

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 !

Tipo implícito de reparto


std::ostream_iterator permite emitir implícitamente el tipo de contenido del contenedor. Por

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:

std::cout << std::setprecision(3);


std::fixed(std::cout);

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, " ! "));

por lo que el código de arriba rinde

1.000 ! 2.000 ! 3.000 ! 4.000 !

A pesar de std::vector hold int s.

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:

std::boolalpha(std::cout); // print booleans alphabetically


std::transform(v.begin(), v.end(), std::ostream_iterator<bool>(std::cout, " "),
[](int val) {
return (val % 2) == 0;
});

o imprimir el elemento cuadrado:

std::transform(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "),


[](int val) {
return val * val;
});

Impresión de N números al azar delimitados por espacios:

const int N = 10;


std::generate_n(std::ostream_iterator<int>(std::cout, " "), N, std::rand);

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:

int v[] = {1,2,3,4,8,16};


std::transform(v, std::end(v), std::ostream_iterator<int>(std::cout, " "),
[](int val) {
return val * val;
});

Análisis de archivos

Análisis de archivos en contenedores STL


s son muy útiles para leer secuencias de números u otros datos analizables en
istream_iterator
contenedores STL sin bucles explícitos en el código.

Usando tamaño de contenedor explícito:

std::vector<int> v(100);
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
v.begin());

o con la inserción de iterador:

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.

Análisis de tablas de texto heterogéneas


Como istream::operator>> lee el texto hasta que aparezca un símbolo de espacio en blanco, se
puede usar en la condición while para analizar tablas de datos complejas. Por ejemplo, si
tenemos un archivo con dos números reales seguidos por una cadena (sin espacios) en cada
línea:

1.12 3.14 foo


2.1 2.2 barr

se puede analizar de esta manera:

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;
});

Lea C ++ Streams en línea: https://riptutorial.com/es/cplusplus/topic/7660/c-plusplus-streams

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;

En algún código posterior:

my_var.y = 5;

Si sizeof (unsigned) == 4 , entonces x se almacena al inicio de la estructura, y se almacena y 4


bytes. El código de ensamblaje generado puede parecerse a:

loada register1,#myvar ; get the address of the structure


storei register1[4],#0x05 ; put the value '5' at offset 4, e.g., set 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;

std::cout << "Year:" << d.Year << std::endl <<


"Month:" << d.Month << std::endl <<
"Day:" << d.Day << std::endl;

Lea Campos de bits en línea: https://riptutorial.com/es/cplusplus/topic/2710/campos-de-bits

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.

La otra propiedad es si es legal moverse implícitamente del valor de la expresión. O más


específicamente, si la expresión, cuando se usa como un parámetro de función, se unirá a los
tipos de parámetros de valor r o no.

C ++ define 3 categorías de valores que representan la combinación útil de estas propiedades:


lvalue (expresiones con identidad pero que no se pueden mover desde), xvalue (expresiones con
identidad que se pueden mover desde), y prvalue (expresiones sin identidad que se pueden
mover desde). C ++ no tiene expresiones que no tengan identidad y no puedan moverse.

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.

Este gráfico sirve como ilustración:

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:

• Expresiones que representan objetos temporales, como std::string("123") .


• Una expresión de llamada de función que no devuelve una referencia.
• Un literal ( excepto un literal de cadena - esos son valores de l), como tiene 1 , true , 0.5f , o
'a'
• Una expresión lambda

La dirección incorporada del operador ( & ) no se puede aplicar a estas expresiones.

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;

4; // prvalue: does not have an identity


x; // lvalue
x.n; // lvalue
std::move(x); // xvalue
std::forward<X&>(x); // lvalue
X{4}; // prvalue: does not have an identity
X{4}.n; // xvalue: does have an identity and denotes resources
// that can be reused

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

X* foo_ptr(); // foo_ptr() is a prvalue


X& foo_ref(); // foo_ref() is an lvalue

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).

Si una expresión tiene un nombre , es un glvalue:

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

foo(); // has no name, so is a prvalue, not a glvalue


X{}; // temporary has no name, so is a prvalue, not a glvalue
X{}.n; // HAS a name, so is a glvalue. can be moved from, so it's an xvalue

valor

Una expresión de rvalor es cualquier expresión a la que se puede mover implícitamente,


independientemente de si tiene identidad.

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:

std::string str("init"); //1


std::string test1(str); //2
std::string test2(std::move(str)); //3

str = std::string("new value"); //4


std::string &&str_ref = std::move(str); //5
std::string test3(str_ref); //6

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 .

Para moverlo, tendríamos que emplear std::move nuevamente.

Lea Categorías de valor en línea: https://riptutorial.com/es/cplusplus/topic/763/categorias-de-valor

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.

Una clase es una colección de miembros de la clase , que puede ser:

• variables miembro (también llamadas "campos"),


• funciones miembro (también llamadas "métodos"),
• tipos de miembros o typedefs (por ejemplo, "clases anidadas"),
• Plantillas de miembros (de cualquier tipo: plantilla de variable, función, clase o alias)

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 ).

Por ejemplo, los siguientes fragmentos de código son idénticos:

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;

Se accede a los miembros de una clase usando la sintaxis de puntos.

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:

Palabra clave Descripción

public Todos tienen acceso

protected Sólo la clase en sí, las clases derivadas y los amigos tienen acceso.

private Sólo la clase en sí y los amigos tienen acceso.

Cuando el tipo se define con la palabra clave de class , el especificador de acceso


predeterminado es private , pero si el tipo se define con la palabra clave struct , el especificador
de acceso predeterminado es public :

struct MyStruct { int x; };


class MyClass { int x; };

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: */

int x() const noexcept { return m_x; }


void setX(int const x) noexcept { m_x = x; }

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

Las clases / estructuras pueden tener relaciones de 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;
};

//Make B inherit publicly (default) from A


struct B : A

https://riptutorial.com/es/home 97
{
};

Hay 3 formas de herencia para una clase / estructura:

• public
• private
• protected

Tenga en cuenta que la herencia predeterminada es la misma que la visibilidad predeterminada


de los miembros: public si usa la palabra clave struct , y private para la palabra clave class .

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 :

struct B : public A // or just `struct B : A`


{
void foo()
{
p1 = 0; //well formed, p1 is public 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; //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

Al usar la herencia, puede especificar la palabra clave 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`
}

Si anulamos el comentario /*A(88)*/ no obtendremos ningún error ya que C ahora está


inicializando su base virtual indirecta 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 ).

Es útil para resolver el problema del diamante .

A A A
/ \ | |
B C B C
\ / \ /
D D
virtual inheritance normal inheritance

By C heredan de A , y D hereda de B y C , por lo que hay 2 instancias de A en D ! Esto se traduce


en ambigüedad cuando se accede al miembro de A a D , ya que el compilador no tiene forma de
saber de qué clase desea acceder a ese miembro (¿el que B hereda o el que hereda C ?) .

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() {}
};

struct B : public /*virtual*/ A {};


struct C : public /*virtual*/ A {};

struct D : public B, public C


{
void bar()
{
foo(); //Error, which foo? B::foo() or C::foo()? - Ambiguous
}
};

https://riptutorial.com/es/home 100
Eliminar los comentarios resuelve la ambigüedad.

Herencia múltiple

Aparte de la herencia única:

class A {};
class B : public A {};

También puede tener herencia múltiple:

class A {};
class B {};
class C : public A, public B {};

C ahora tendrá herencia de A y B al mismo tiempo.

Nota: esto puede generar ambigüedad si se usan los mismos nombres en varias class o
struct heredadas. ¡Ten cuidado!

La ambigüedad en la herencia múltiple

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 }
};

class derived : public base1, public base2


{

};

int main()
{
derived obj;

// Error because compiler can't figure out which function to call


//either function( ) of base1 or base2 .
obj.function( )

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.
}

Acceso a los miembros de la clase

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();

Cuando se accede a miembros de la clase estática, se utiliza el operador :: , pero en el nombre


de la clase en lugar de una instancia de la misma. Alternativamente, se puede acceder al

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.

Herencia privada: restringiendo la interfaz de clase base

La herencia privada es útil cuando se requiere restringir la interfaz pública de la clase:

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;
};

Entonces para ambos casos es posible:

B b;
A& a = static_cast<A&>(b); // OK for public inheritance
a.move(); // OK

Clases finales y estructuras.

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:

// Compilation error: cannot derive from final class:


class B : public A {
};

La clase final puede aparecer en cualquier lugar de la jerarquía de clases:

class A {
};

// OK.
class B final : public A {
};

// Compilation error: cannot derive from final class B.


class C : public B {
};

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);
};

void printWeight(Animal animal)


{
std::cout << animal.weight << "\n";
}

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;
}
}

std::ostream& operator<<(std::ostream& os, Animal animal)


{
os << "Animal height: " << animal.height << "\n";
return os;
}

https://riptutorial.com/es/home 105
int main() {
Animal animal = {10, 5};
printWeight(animal);

AnimalPrinter aPrinter;
aPrinter.print(animal);

std::cout << animal;


}

10
10, 5
Animal height: 5

Clases / Estructuras Anidadas

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;

class Inner1 {};

Inner1 in1;
Inner2* in2p;

public:
Outer();
~Outer();
};

class Outer::Inner2 {};

Outer::Outer() : in1(Inner1()), in2p(new Inner2) {}


Outer::~Outer() {
if (in2p) { delete in2p; }
}

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;
};

void barge_in(Outer& out, Outer::Inner& in) {


int i = in.i; // Good.
int o = out.o; // Error: int Outer::o is private.
}

void barge_out(Outer& out, Outer::Inner& in) {


int i = in.i; // Error: int Outer::Inner::i is private.

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(); }
};

// ...

Outer::Inner oi; // Error: Outer::Inner is private.

auto oi = Outer::make_Inner(); // Good.


oi.func(); // Good.
Outer::make_Inner().func(); // Good.

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;
};

typedef Outer::Inner ImOut; // Good.


typedef Outer::Inner_ ImBad; // Error.

// ...

Outer::Inner oi; // Good.


Outer::Inner_ oi; // Error.
ImOut oi; // Good.

Al igual que con otras clases, las clases anidadas pueden derivar o derivarse de otras clases.

struct Base {};

struct Outer {
struct Inner : Base {};
};

struct Derived : Outer::Inner {};

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;

virtual ~BaseOuter() = default;

virtual Inner& getInner() { return b_in; }


};

void BaseOuter::BaseInner_::do_something_else() {}

// ---

class DerivedOuter : public BaseOuter {


// Note the use of the qualified typedef; BaseOuter::BaseInner_ is private.
struct DerivedInner_ : BaseOuter::Inner {
void do_something() override {}
void do_something_else() override;
} d_in;

public:
typedef DerivedInner_ Inner;

BaseOuter::Inner& getInner() override { return d_in; }


};

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;
};

IHaveTypedefs::MyTypedef i; // x_i is an int.


IHaveTypedefs::MyTypedefHelper hi; // x_hi is a Helper<int>.

typedef IHaveTypedefs::MyTypedef TypedefBeFree;


TypedefBeFree ii; // ii is an int.

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
};

TypedefAccessLevels::PrvInt prv_i; // Error: TypedefAccessLevels::PrvInt is private.


TypedefAccessLevels::ProInt pro_i; // Error: TypedefAccessLevels::ProInt is protected.
TypedefAccessLevels::PubInt pub_i; // Good.

class Derived : public TypedefAccessLevels {


PrvInt prv_i; // Error: TypedefAccessLevels::PrvInt is private.
ProInt pro_i; // Good.
PubInt pub_i; // Good.
};

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;

MyHelper get_helper() const { return MyHelper(8, s, 19.5, "shoe", false); }

// ...
};

// ...

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.

Esto también se puede combinar con decltype , si uno lo desea.

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; }

// ...
};

En esta situación, cambiar la implementación de SomethingElse::helper cambiará automáticamente


el typedef para nosotros, debido a decltype . Esto minimiza el número de modificaciones
necesarias cuando queremos cambiar de helper , lo que minimiza el riesgo de error humano.

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, size_t SZ, size_t D>


class Array { /* ... */ };

template<typename T, size_t SZ>


struct OneDArray {
typedef Array<T, SZ, 1> type;
};

template<typename T, size_t SZ>


struct TwoDArray {
typedef Array<T, SZ, 2> type;
};

template<typename T>
struct MonoDisplayLine {
typedef Array<T, 80, 1> type;
};

OneDArray<int, 3>::type arr1i; // arr1i is an Array<int, 3, 1>.


TwoDArray<short, 5>::type arr2s; // arr2s is an Array<short, 5, 2>.
MonoDisplayLine<char>::type arr3c; // arr3c is an Array<char, 80, 1>.

Miembros de la clase estatica

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.

// Non-static member functions can modify static member variables.


Example() { ++num_instances; }
void set_str(const std::string& str);
};

int Example::num_instances;
std::string Example::static_str = "Hello.";

// ...

Example one, two, three;


// Each Example has its own "i", such that:
// (&one.i != &two.i)
// (&one.i != &three.i)
// (&two.i != &three.i).
// All three Examples share "num_instances", such that:
// (&one.num_instances == &two.num_instances)
// (&one.num_instances == &three.num_instances)
// (&two.num_instances == &three.num_instances)

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.

// ...
};

int Example::num_instances; // Definition. Zero-initialised.


std::string Example::static_str = "Hello."; // Definition.

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.

// For Example above, either...


class Example {
// ...

public:
static int static_func() { return num_instances; }

// ...

void set_str(const std::string& str) { static_str = str; }


};

// Or...

class Example { /* ... */ };

int Example::static_func() { return num_instances; }


void Example::set_str(const std::string& str) { static_str = str; }

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.

const static double good_cd;


static const volatile int good_cvi;
};

const double ExConst::good_cd = 5; // Good.


const volatile int ExConst::good_cvi = 5; // Good.

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.
};

constexpr int ExConstexpr::bad_ci = 5; // Still an 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 ExODR::odr_used;

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, :: .

std::string str = Example::static_str;

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++) {}

static int get_total_instances() { return num_instances; }


int get_instance_number() const { return my_num; }
};

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;

ExDontNeedMutable() : immuta(-5), muta(-5) {}


};
int ExDontNeedMutable::i;

// ...

const ExDontNeedMutable dnm;


dnm.immuta = 5; // Error: Can't modify read-only object.
dnm.muta = 5; // Good. Mutable fields of const objects can be written.
dnm.i = 5; // Good. Static members can be written regardless of an instance's const-
ness.

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;

// ...

int x1 = ExAccess::prv_int; // Error: int ExAccess::prv_int is private.


int x2 = ExAccess::pro_int; // Error: int ExAccess::pro_int is protected.
int x3 = ExAccess::pub_int; // Good.

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) {}

static void bad_mutate() { ++i *= 5; } // Error.

https://riptutorial.com/es/home 118
static void good_mutate(ExInstanceRequired& e) { ++e.i *= 5; } // Good.
};

Debido a que no tienen un puntero de this , sus direcciones no se pueden almacenar en


funciones de punteros a miembros y, en cambio, se almacenan en punteros a funciones
normales.

struct ExPointer {
void nsfunc() {}
static void sfunc() {}
};

typedef void (ExPointer::* mem_f_ptr)();


typedef void (*f_ptr)();

mem_f_ptr p_sf = &ExPointer::sfunc; // Error.


f_ptr p_sf = &ExPointer::sfunc; // Good.

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.

virtual static void vsfunc() {} // Error.


static virtual void svfunc() {} // 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.

Funciones miembro no estáticas

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();

Se pueden definir dentro o fuera de la definición de clase; si se definen fuera, se especifican


como dentro del alcance de la clase.

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.

void cv_only() const volatile {}


};

CVQualifiers non_cv_instance;
const CVQualifiers c_instance;

non_cv_instance.func(); // Calls #1.


c_instance.func(); // Calls #2.

non_cv_instance.cv_only(); // Calls const volatile version.


c_instance.cv_only(); // Calls const volatile version.

C ++ 11

Los ref-qualifiers de la función miembro indican si la función se debe llamar o no en instancias de


rvalue, y usan la misma sintaxis que la función cv-qualifiers.

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.

CV-calificadores y ref-calificadores también se pueden combinar si es necesario.

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() {}
};

Base* bp = new Base;


Base* dp = new Derived;
bp.func(); // Calls Base::func().
dp.func(); // Calls Derived::func().

Para más información, ver aquí .

Estructura / clase sin nombre

Se permite la struct sin nombre (el tipo no tiene nombre)

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;

pero NO struct anónima (tipo sin nombre y objeto sin nombre)

https://riptutorial.com/es/home 121
struct InvalidCircle
{
struct /* No name */ {
float centerX;
float centerY;
}; // No member either.
float radius;
};

Nota: algunos compiladores permiten struct anónimas como extensión .

C ++ 11

• lamdba puede ser visto como una struct especial sin nombre .

• decltype permite recuperar el tipo de struct 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}
};

// for range relies on `template <class T, std::size_t N> std::begin(T (&)[N])`


for (const auto& point : points) {
std::cout << "{" << point.x << ", " << point.y << "}\n";
}

decltype(points[0]) topRightCorner{1, 1};


auto it = std::find(points, points + 4, topRightCorner);
std::cout << "top right corner is the "
<< 1 + std::distance(points, it) << "th\n";
}

Lea Clases / Estructuras en línea: https://riptutorial.com/es/cplusplus/topic/508/clases---


estructuras

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

Si los valores en un contenedor ya tienen ciertos operadores sobrecargados, std::sort puede


usarse con funtores especializados para ordenar en orden ascendente o descendente:

C ++ 11

#include <vector>
#include <algorithm>
#include <functional>

std::vector<int> v = {5,1,2,4,3};

//sort in ascending order (1,2,3,4,5)


std::sort(v.begin(), v.end(), std::less<int>());

// Or just:
std::sort(v.begin(), v.end());

//sort in descending order (5,4,3,2,1)


std::sort(v.begin(), v.end(), std::greater<int>());

//Or just:
std::sort(v.rbegin(), v.rend());

C ++ 14

En C ++ 14, no necesitamos proporcionar el argumento de plantilla para los objetos de la función


de comparación y, en su lugar, dejar que el objeto deduzca en función de lo que se pasa:

std::sort(v.begin(), v.end(), std::less<>()); // ascending order


std::sort(v.begin(), v.end(), std::greater<>()); // descending order

Clasificación de contenedores de secuencia por sobrecargado menos


operador

Si no se pasa ninguna función de ordenación, std::sort ordenará los elementos llamando al


operator< en pares de elementos, que deben devolver un tipo convertible contextualmente a bool
(o simplemente bool ). Los tipos básicos (enteros, flotadores, punteros, etc.) ya se han construido
en los operadores de comparación.

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.

// Include sequence containers


#include <vector>
#include <deque>
#include <list>

// Insert sorting algorithm


#include <algorithm>

class Base {
public:

// Constructor that set variable to the value of v


Base(int v): variable(v) {
}

// Use variable to provide total order operator less


//`this` always represents the left-hand side of the compare.
bool operator<(const Base &b) const {
return this->variable < b.variable;
}

int variable;
};

int main() {
std::vector <Base> vector;
std::deque <Base> deque;
std::list <Base> list;

// Create 2 elements to sort


Base a(10);
Base b(5);

// Insert them into backs of containers


vector.push_back(a);
vector.push_back(b);

deque.push_back(a);
deque.push_back(b);

list.push_back(a);
list.push_back(b);

// Now sort data using operator<(const Base &b) function


std::sort(vector.begin(), vector.end());
std::sort(deque.begin(), deque.end());
// List must be sorted differently due to its design
list.sort();

return 0;
}

Clasificación de contenedores de secuencia utilizando la función de


comparación

https://riptutorial.com/es/home 124
// Include sequence containers
#include <vector>
#include <deque>
#include <list>

// Insert sorting algorithm


#include <algorithm>

class Base {
public:

// Constructor that set variable to the value of v


Base(int v): variable(v) {
}

int variable;
};

bool compare(const Base &a, const Base &b) {


return a.variable < b.variable;
}

int main() {
std::vector <Base> vector;
std::deque <Base> deque;
std::list <Base> list;

// Create 2 elements to sort


Base a(10);
Base b(5);

// Insert them into backs of containers


vector.push_back(a);
vector.push_back(b);

deque.push_back(a);
deque.push_back(b);

list.push_back(a);
list.push_back(b);

// Now sort data using comparing function


std::sort(vector.begin(), vector.end(), compare);
std::sort(deque.begin(), deque.end(), compare);
list.sort(compare);

return 0;
}

Ordenando los contenedores de secuencias usando expresiones lambda (C


++ 11)

C ++ 11

// Include sequence containers


#include <vector>
#include <deque>
#include <list>
#include <array>

https://riptutorial.com/es/home 125
#include <forward_list>

// Include sorting algorithm


#include <algorithm>

class Base {
public:

// Constructor that set variable to the value of v


Base(int v): variable(v) {
}

int variable;
};

int main() {
// Create 2 elements to sort
Base a(10);
Base b(5);

// We're using C++11, so let's use initializer lists to insert items.


std::vector <Base> vector = {a, b};
std::deque <Base> deque = {a, b};
std::list <Base> list = {a, b};
std::array <Base, 2> array = {a, b};
std::forward_list<Base> flist = {a, b};

// We can sort data using an inline lambda expression


std::sort(std::begin(vector), std::end(vector),
[](const Base &a, const Base &b) { return a.variable < b.variable;});

// We can also pass a lambda object as the comparator


// and reuse the lambda multiple times
auto compare = [](const Base &a, const Base &b) {
return a.variable < b.variable;};
std::sort(std::begin(deque), std::end(deque), compare);
std::sort(std::begin(array), std::end(array), compare);
list.sort(compare);
flist.sort(compare);

return 0;
}

Clasificación y secuenciación de contenedores.

std::sort , que se encuentra en el algorithm encabezado de biblioteca estándar, es un algoritmo


de biblioteca estándar para ordenar un rango de valores, definido por un par de iteradores.
std::sort toma como último parámetro un funtor utilizado para comparar dos valores; Así es como
determina el orden. Tenga en cuenta que std::sort no es estable .

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.

Un contenedor con iteradores de acceso aleatorio se puede ordenar utilizando el algoritmo


std::sort :

C ++ 11

https://riptutorial.com/es/home 126
#include <vector>
#include <algorithm>

std::vector<int> MyVector = {3, 1, 2}

//Default comparison of <


std::sort(MyVector.begin(), MyVector.end());

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>

std::list<int> MyList = {3, 1, 2}

//Default comparison of <


//Whole list only.
MyList.sort();

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í:

void sort_sublist(std::list<int>& mylist, std::list<int>::const_iterator start,


std::list<int>::const_iterator end) {
//extract and sort half-open sub range denoted by start and end iterator
std::list<int> tmp;
tmp.splice(tmp.begin(), list, start, end);
tmp.sort();
//re-insert range at the point we extracted it from
list.splice(end, tmp);
}

clasificación con std :: map (ascendente y descendente)

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"));

for (auto const& entry: sorted_map)


{
std::cout << entry.second << " (" << entry.first << " of Earth's radius)" << '\n';
}
}

Salida:

Mercury (0.3829 of Earth's radius)


Mars (0.532 of Earth's radius)
Venus (0.9499 of Earth's radius)
Earth (1 of Earth's radius)
Neptune (3.865 of Earth's radius)
Uranus (3.981 of Earth's radius)
Saturn (9.14 of Earth's radius)
Jupiter (10.97 of Earth's radius)

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"));

for (auto const& entry: sorted_map)


{
std::cout << entry.second << " (has " << entry.first << " legs)" << '\n';
}
}

Salida

centipede (has 100 legs)

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)

Clasificación de matrices incorporadas

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

int arr1[] = {36, 24, 42, 60, 59};

// sort numbers in ascending order


sort(std::begin(arr1), std::end(arr1));

// sort numbers in descending order


sort(std::begin(arr1), std::end(arr1), std::greater<int>());

Antes de C ++ 11, el final de la matriz debía "calcularse" utilizando el tamaño de la matriz:

C ++ 11

// Use a hard-coded number for array size


sort(arr1, arr1 + 5);

// Alternatively, use an expression


const size_t arr1_size = sizeof(arr1) / sizeof(*arr1);
sort(arr1, arr1 + arr1_size);

Lea Clasificación en línea: https://riptutorial.com/es/cplusplus/topic/1675/clasificacion

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

En C ++, el bucle a través de un contenedor de secuencia c se puede hacer usando índices de la


siguiente manera:

for(size_t i = 0; i < c.size(); ++i) c[i] = 0;

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:

for(size_t i = 0; i <= c.size(); ++j) c[i] = 0;


^~~~~~~~~~~~~~^

El bucle también se puede lograr para todos los contenedores que utilizan iteradores, con
inconvenientes similares:

for(iterator it = c.begin(); it != c.end(); ++it) (*it) = 0;

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.

De acuerdo con el estándar C ++ 11, la implementación subyacente es equivalente a:

for(auto begin = c.begin(), end = c.end(); begin != end; ++begin)


{
// ...
}

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.

Lea 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 en línea: https://riptutorial.com/es/cplusplus/topic/7134/comparaciones-
lado-a-lado-de-ejemplos-clasicos-de-c-plusplus-resueltos-a-traves-de-c-plusplus-vs-c-plusplus-11-
vs-c-plusplus-14-vs-c-plusplus-17

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:

• GCC, la colección de compiladores GNU g ++


• clang: una interfaz familiar de lenguaje C para LLVM clang ++
• MSVC, Microsoft Visual C ++ (incluido en Visual Studio) visual-c ++
• C ++ Builder, Embarcadero C ++ Builder (incluido en RAD Studio) c ++ builder

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).

g++ -o app -Wall main.cpp -O0

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 ):

g++ -o app -Wall -O2 main.cpp

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).

Alternativamente, use las marcas de optimización de los grupos O (o más optimizaciones


experimentales) directamente. El siguiente ejemplo construye con la optimización de -O2 , más un
indicador del nivel de optimización de -O3 :

https://riptutorial.com/es/home 132
g++ -o app -Wall -O2 -ftree-partial-pre main.cpp

Para producir un ejecutable optimizado específico de la plataforma (para su uso en producción en


la máquina con la misma arquitectura), use:

g++ -o app -Wall -O2 -march=native 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 :

g++ -o file.o -Wall -c file.cpp

Esto produce un archivo de objeto llamado file.o que luego puede vincularse con otros archivos
para producir un binario:

g++ -o app file.o otherfile.o

Puede encontrar más información sobre las opciones de optimización en gcc.gnu.org . De


particular interés son: -Og (optimización con énfasis en la experiencia de depuración,
recomendada para el ciclo estándar de edición-compilación-depuración) y -Ofast (todas las
optimizaciones, incluidas las que no tienen en cuenta el estricto cumplimiento de estándares).

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 .

Si el código espera un estándar de C ++ específico, especifique qué estándar usar incluyendo la -


std= . Los valores admitidos corresponden al año de finalización de cada versión del estándar ISO
C ++. A partir de GCC 6.1.0, los valores válidos para el indicador std= son c++98 / c++03 , c++11 ,
c++14 y c++17 / c++1z . Los valores separados por una barra diagonal son equivalentes.

g++ -std=c++11 <file>

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.

El estándar predeterminado se utilizará si no se especifica ninguno. Para las versiones de GCC


anteriores a 6.1.0, el valor predeterminado es -std=gnu++03 ; en GCC 6.1.0 y superior, el valor
predeterminado es -std=gnu++14 .

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.

Vinculación con bibliotecas:


Use la opción -l para pasar el nombre de la biblioteca:

g++ main.cpp -lpcre2-8


#pcre2-8 is the PCRE2 library for 8bit code units (UTF-8)

Si la biblioteca no está en la ruta de la biblioteca estándar, agregue la ruta con la opción -L :

g++ main.cpp -L/my/custom/path/ -lmylib

Se pueden vincular varias bibliotecas entre sí:

g++ main.cpp -lmylib1 -lmylib2 -lmylib3

Si una biblioteca depende de otra, ponga la biblioteca dependiente antes de la biblioteca


independiente:

g++ main.cpp -lchild-lib -lbase-lib

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):

g++ main.cpp -Wl,--start-group -lbase-lib -lchild-lib -Wl,--end-group

Compilando con Visual C ++ (Línea de Comando)

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.

Suponiendo un archivo fuente adicional "niam.cpp" en el mismo directorio, use lo siguiente:

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".

También puedes usar comodines, como es de esperar:

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".

Para renombrar o reubicar el ejecutable, use uno de los siguientes:

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.

Tenga en cuenta que si bien el espacio entre /o y la carpeta y / o el nombre especificados es


opcional, no puede haber un espacio entre /Fe y la carpeta y / o el nombre especificados.

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.

cl /GL main.cpp other.cpp


// Generates special object files used for whole-program optimisation, which allows CL to
// take every module (translation unit) into consideration during optimisation.
// Passes the option "/LTCG" (Link-Time Code Generation) to LINK, telling it to call CL during
// the linking phase to perform additional optimisations. If linking is not performed at
this
// time, the generated object files should be linked with "/LTCG".
// Can be used with other CL optimisation options.

Finalmente, para producir un ejecutable optimizado específico de la plataforma (para su uso en la


producción en la máquina con la arquitectura especificada), elija el indicador de comando o el
parámetro VCVARSALL para la plataforma de destino. link debe detectar la plataforma deseada
desde los archivos de objetos; si no, use la opción /MACHINE para especificar explícitamente la
plataforma de destino.

// 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 a.cpp b.cpp c.cpp


// Generates "a.exe".

cl d.obj a.cpp q.cpp


// Generates "d.exe".

cl y.lib n.cpp o.obj


// Generates "n.exe".

cl /o yo zp.obj pz.cpp
// Generates "yo.exe".

Para compilar un archivo (s) sin vincular, use:

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".

link main.obj 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.

cl main.cpp /link /LINKER_OPTION


// "/link" passes everything following it directly to LINK, without parsing it in any way.
// Replace "/LINKER_OPTION" with any desired LINK option(s).

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.

g++ -o app src/main.cpp


cl -o app src/main.cpp

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).

Nota: en versiones anteriores de este compilador, las características específicas estaban


disponibles, sin embargo, esto se utilizaba principalmente para las vistas previas de nuevas
funciones.

Compilación con Visual Studio (interfaz gráfica) - Hello World

1. Descargue e instale Visual Studio Community 2015


2. Abrir la comunidad de Visual Studio
3. Haga clic en Archivo -> Nuevo -> Proyecto

https://riptutorial.com/es/home 138
4. Haga clic en Plantillas -> Visual C ++ -> Aplicación de consola Win32 y luego nombre el
proyecto MyFirstProgram .

5. Haga clic en Aceptar


6. Haga clic en Siguiente en la siguiente ventana.

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;
}

Tu entorno debe verse como:

https://riptutorial.com/es/home 143
11. Haga clic en Depurar -> Iniciar sin depurar (o presione ctrl + F5):

12. Hecho. Deberías obtener la siguiente salida de consola:

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 .

Véase también la introducción a Cmake .

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:

• Pega tu código en un formulario web en el navegador.


• Seleccione algunas opciones del compilador y compile el código.
• Recoger compilador y / o salida del programa.

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.

Para qué compiladores en línea no se debe utilizar:

• 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:

Descargo de responsabilidad: los autores de la documentación no están afiliados a los


recursos que se enumeran a continuación. Los sitios web están listados
alfabéticamente.

• http://codepad.org/ Compilador en línea con código compartido. Editar código después de


compilar con un código fuente de advertencia o error no funciona tan bien.
• http://coliru.stacked-crooked.com/ Compilador en línea para el que especifica la línea de

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.

El proceso de compilación de C ++.

Cuando desarrolle un programa en C ++, el siguiente paso es compilar el programa antes de


ejecutarlo. La compilación es el proceso que convierte el programa escrito en lenguaje legible por
humanos como C, C ++, etc., en un código de máquina, entendido directamente por la Unidad
Central de Procesamiento. Por ejemplo, si tiene un archivo de código fuente de C ++ llamado
prog.cpp y ejecuta el comando de compilación,

g++ -Wall -ansi -o prog prog.cpp

Hay 4 etapas principales involucradas en la creación de un archivo ejecutable desde el archivo


fuente.

1. El preprocesador de C ++ toma un archivo de código fuente de C ++ y se ocupa de los


encabezados (#include), macros (#define) y otras directivas de preprocesador.

2. El archivo de código fuente expandido de C ++ producido por el preprocesador de C ++ se


compila en el lenguaje ensamblador de la plataforma.

3. El código de ensamblador generado por el compilador se ensambla en el código de objeto


para la plataforma.

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

El preprocesador maneja las directivas del preprocesador, como #include y #define. Es un


agnóstico de la sintaxis de C ++, por lo que debe usarse con cuidado.

Funciona en un archivo fuente de C ++ a la vez al reemplazar las directivas #include con el


contenido de los archivos respectivos (que generalmente son solo declaraciones), al reemplazar
las macros (# definir) y al seleccionar diferentes porciones de texto dependiendo de #if, #ifdef y
#ifndef directivas.

El preprocesador trabaja en una secuencia de tokens de preprocesamiento. La sustitución de


macros se define como la sustitución de tokens con otros tokens (el operador ## permite fusionar
dos tokens cuando tiene sentido).

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.

Al utilizar el indicador del compilador a continuación, podemos detener el proceso en la etapa de


preprocesamiento.

g++ -E prog.cpp

Compilacion

El paso de compilación se realiza en cada salida del preprocesador. El compilador analiza el


código fuente puro de C ++ (ahora sin directivas de preprocesador) y lo convierte en código de
ensamblaje. Luego invoca el back-end subyacente (ensamblador en la cadena de herramientas)
que ensambla ese código en un código de máquina produciendo un archivo binario real en algún
formato (ELF, COFF, a.out, ...). Este archivo de objeto contiene el código compilado (en forma
binaria) de los símbolos definidos en la entrada. Los símbolos en los archivos de objetos se
denominan por nombre.

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:

g++ -Wall -ansi -S prog.cpp

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:

g++ -Wall -ansi -c prog.cpp

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.

Compilando con Code :: Blocks (interfaz gráfica)

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.

2. Abra Code :: Blocks y haga clic en "Crear un nuevo proyecto":

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 .

y para compilar y luego correr, presiona .

y para compilar y luego correr, presiona .

Compilando y ejecutando el predeterminado "¡Hola mundo!" El código da el siguiente


resultado:

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.

Tamaño de los tipos integrales.

Los siguientes tipos se definen como tipos integrales :

• char
• Tipos enteros firmados
• Tipos de enteros sin signo
• char16_t y char32_t
• bool
• wchar_t

Con la excepción de sizeof(char) / sizeof(signed char) / sizeof(unsigned char) , que se divide


entre § 3.9.1.1 [basic.fundamental / 1] y § 5.3.3.1 [expr.sizeof], y sizeof(bool) , que está
completamente definido por la implementación y no tiene un tamaño mínimo, los requisitos de
tamaño mínimo de estos tipos se indican en la sección § 3.9.1 [basic.fundamental] de la norma, y
se detallarán a continuación.

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.

Tamaño de los tipos enteros con signo y sin


signo

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.

La norma garantiza así que:

1 == sizeof(char) == sizeof(signed char) == sizeof(unsigned char)


<= sizeof(short) == sizeof(unsigned short)
<= sizeof(int) == sizeof(unsigned int)
<= sizeof(long) == sizeof(unsigned long)

C ++ 11

<= sizeof(long long) == sizeof(unsigned long long)

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

signed char -127 a 127 (- (2 7 - 1) a (2 7 - 1)) 8

unsigned char 0 a 255 (0 a 2 8 - 1) 8

signed short -32,767 a 32,767 (- (2 15 - 1) a (2 15 - 1)) dieciséis

unsigned
short 0 a 65,535 (0 a 2 16 - 1) dieciséis

signed int -32,767 a 32,767 (- (2 15 - 1) a (2 15 - 1)) dieciséis

https://riptutorial.com/es/home 156
Bits mínimos
Tipo Rango mínimo
requeridos

unsigned int 0 a 65,535 (0 a 2 16 - 1) dieciséis

-2,147,483,647 a 2,147,483,647 (- (2 31 - 1) a (2
signed long
31 - 1)) 32

unsigned long 0 a 4,294,967,295 (0 a 2 32 - 1) 32


C ++ 11
Bits mínimos
Tipo Rango mínimo
requeridos

signed long -9,223,372,036,854,775,807 a


64
long 9,223,372,036,854,775,807 (- (2 63 - 1) a (2 63 - 1))

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

Tamaño de char16_t y char32_t

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:

• char16_t es lo suficientemente grande como para representar cualquier unidad de código


UTF-16, y tiene el mismo tamaño, firmeza y alineación que uint_least16_t ; por lo tanto, se
requiere que tenga al menos 16 bits de tamaño.

• char32_t es lo suficientemente grande como para representar cualquier unidad de código


UTF-32, y tiene el mismo tamaño, firmeza y alineación que uint_least32_t ; por lo tanto, se
requiere que tenga al menos 32 bits de tamaño.

https://riptutorial.com/es/home 157
Tamaño de bool

El tamaño de bool está definido en la implementación, y puede o no ser 1 .

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.

Dependiendo de la implementación, el tamaño de wchar_t es a menudo, pero no siempre, de 8, 16


o 32 bits. Los ejemplos más comunes de estos son:

• 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:

Modelo int long puntero

LP32 (2/4/4) dieciséis 32 32

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:

• Windows de 16 bits utiliza LP32.


• Los sistemas de 32 bits * nix (Unix, Linux, Mac OSX y otros sistemas operativos similares a
Unix) y Windows usan ILP32.
• Windows de 64 bits utiliza LLP64.
• Los sistemas de 64 bits * nix utilizan LP64.

Tenga en cuenta, sin embargo, que estos modelos no se mencionan específicamente en la norma
en sí.

Número de bits en un byte

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.

Valor numérico de un puntero

El resultado de convertir un puntero a un entero usando reinterpret_cast está definido por la


implementación, pero "... no es sorprendente para aquellos que conocen la estructura de
direccionamiento de la máquina subyacente".

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` was not in C++03. It's in C99, in <stdint.h>, as an optional type


#include <stdint.h>

uintptr_t uip;

C ++ 11

// There is an optional `std::uintptr_t` in C++11


#include <cstdint>

std::uintptr_t uip;

C ++ 11 hace referencia a C99 para la definición uintptr_t (estándar C99, 6.3.2.3):

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:

Todos los tipos de punteros de función tendrán la misma representación que el


puntero de tipo para anular. La conversión de un puntero de función a void * no
alterará la representación. Un valor nulo * resultante de dicha conversión se
puede convertir de nuevo al tipo de puntero de función original, utilizando una
conversión explícita, sin pérdida de información.

• C99 §7.18.1:

Cuando se definen los nombres typedef que difieren solo en la ausencia o


presencia de la u inicial, denotarán los tipos correspondientes firmados y no
firmados como se describe en 6.2.5; una implementación que provea uno de
estos tipos correspondientes también proporcionará el otro.

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.

Rangos de tipos numéricos

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> .

• std::numeric_limits<signed char>::min() es igual a SCHAR_MIN , que es menor o igual que -


127.
• std::numeric_limits<signed char>::max() es igual a SCHAR_MAX , que es mayor o igual a 127.
• std::numeric_limits<unsigned char>::max() es igual a UCHAR_MAX , que es mayor o igual a 255.
• std::numeric_limits<short>::min() es igual a SHRT_MIN , que es menor o igual que -32767.
• std::numeric_limits<short>::max() es igual a SHRT_MAX , que es mayor o igual que 32767.
• std::numeric_limits<unsigned short>::max() es igual a USHRT_MAX , que es mayor o igual a
65535.

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

• std::numeric_limits<long long>::min() es igual a LLONG_MIN , que es menor o igual que -


9223372036854775807.
• std::numeric_limits<long long>::max() es igual a LLONG_MAX , que es mayor o igual que
9223372036854775807.
• std::numeric_limits<unsigned long long>::max() es igual a ULLONG_MAX , que es mayor o igual
que 18446744073709551615.

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 digits10 da el número de dígitos decimales de precisión.


○std::numeric_limits<float>::digits10 es igual a FLT_DIG , que es al menos 6.
○std::numeric_limits<double>::digits10 es igual a DBL_DIG , que es al menos 10.
○std::numeric_limits<long double>::digits10 es igual a LDBL_DIG , que es al menos 10.
• El miembro min_exponent10 es el E negativo mínimo tal que 10 a la potencia E es normal.
○std::numeric_limits<float>::min_exponent10 es igual a FLT_MIN_10_EXP , que es como
máximo -37.
○std::numeric_limits<double>::min_exponent10 es igual a DBL_MIN_10_EXP , que es como
máximo -37. std::numeric_limits<long double>::min_exponent10 es igual a
LDBL_MIN_10_EXP , que es como máximo -37.
• El miembro max_exponent10 es el E máximo, de modo que 10 para la potencia E es finito.
○std::numeric_limits<float>::max_exponent10 es igual a FLT_MIN_10_EXP , que es al menos
37.
○std::numeric_limits<double>::max_exponent10 es igual a DBL_MIN_10_EXP , que es al
menos 37.
○std::numeric_limits<long double>::max_exponent10 es igual a LDBL_MIN_10_EXP , que es al
menos 37.
• Si el miembro is_iec559 es verdadero, el tipo cumple con IEC 559 / IEEE 754 y, por lo tanto,
su rango está determinado por esa norma.

Representación del valor de los tipos de punto flotante

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.

Para un tipo de punto flotante T , std::numeric_limits<T>::radix especifica el radix utilizado por la


representación de T

Si std::numeric_limits<T>::is_iec559 es verdadero, entonces la representación de T coincide con


uno de los formatos definidos por IEC 559 / IEEE 754.

Desbordamiento al convertir de entero a entero con signo

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)

Tipo subyacente (y, por tanto, tamaño) de una enumeración

Si el tipo subyacente no se especifica explícitamente para un tipo de enumeración sin ámbito, se


determina de una manera definida por la implementación.

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.

Lea Comportamiento definido por la implementación en línea:


https://riptutorial.com/es/cplusplus/topic/1363/comportamiento-definido-por-la-implementacion

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.

Una implementación puede documentar el resultado de una operación que produce un


comportamiento indefinido de acuerdo con el estándar, pero un programa que depende de dicho
comportamiento documentado no es portátil.

¿Por qué existe un comportamiento indefinido?

Intuitivamente, el comportamiento indefinido se considera algo malo, ya que tales errores no


pueden manejarse con amabilidad mediante, por ejemplo, controladores de excepciones.

Pero dejar un comportamiento indefinido es en realidad una parte integral de la promesa de C ++


"no pagas por lo que no usas". El comportamiento indefinido permite que un compilador asuma
que el desarrollador sabe lo que está haciendo y no introduce código para verificar los errores
resaltados en los ejemplos anteriores.

Encontrar y evitar comportamientos indefinidos.

Algunas herramientas se pueden usar para descubrir un comportamiento indefinido durante el


desarrollo:

• 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.

Comportamiento indefinido, no especificado y definido por la implementación

De la sección 1.9 (Ejecución del programa) de la norma C ++ 14 (ISO / IEC 14882: 2014):

1. Las descripciones semánticas en esta Norma Internacional definen una máquina


abstracta no determinista parametrizada. [CORTAR]

2. Ciertos aspectos y operaciones de la máquina abstracta se describen en esta


Norma Internacional como definidos por la implementación (por ejemplo,
sizeof(int) ). Estos constituyen los parámetros de la máquina abstracta . Cada
implementación deberá incluir documentación que describa sus características y
comportamiento en estos aspectos. [CORTAR]

3. Ciertos otros aspectos y operaciones de la máquina abstracta se describen en


esta Norma Internacional como no especificados (por ejemplo, evaluación de
expresiones en un nuevo inicializador si la función de asignación no puede
asignar memoria). Donde sea posible, esta norma internacional define un
conjunto de comportamientos permitidos. Estos definen los aspectos no
deterministas de la máquina abstracta. Una instancia de la máquina abstracta
puede tener más de una ejecución posible para un programa dado y una entrada
dada.

4. Ciertas otras operaciones se describen en esta Norma Internacional como


indefinidas (o ejemplo, el efecto de intentar modificar un objeto const ). [ Nota :
esta Norma Internacional no impone requisitos sobre el comportamiento de los
programas que contienen un comportamiento indefinido. - nota final ]

Examples
Leer o escribir a través de un puntero nulo.

int *ptr = nullptr;


*ptr = 1; // Undefined behavior

Este es un comportamiento indefinido , porque un puntero nulo no apunta a ningún objeto


válido, por lo que no hay ningún objeto en *ptr para escribir.

Aunque esto causa con mayor frecuencia un fallo de segmentación, no está definido y puede
pasar cualquier cosa.

No hay declaración de retorno para una función con un tipo de retorno no

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
}

La mayoría de los compiladores de hoy en día emiten una advertencia en el momento de la


compilación para este tipo de comportamiento indefinido.

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.

Modificar un literal de cadena

C ++ 11

char *str = "hello world";


str[0] = 'H';

"hello world" es una cadena literal, por lo que modificarlo da un comportamiento indefinido.

La inicialización de str en el ejemplo anterior fue obsoleta formalmente (programada para su


eliminación de una versión futura del estándar) en C ++ 03. Varios compiladores antes de 2003
podrían emitir una advertencia sobre esto (por ejemplo, una conversión sospechosa). Después de
2003, los compiladores suelen advertir sobre una conversión obsoleta.

C ++ 11

El ejemplo anterior es ilegal y da como resultado un diagnóstico del compilador, en C ++ 11 y


versiones posteriores. Se puede construir un ejemplo similar para mostrar un comportamiento
indefinido permitiendo explícitamente la conversión de tipo, como:

char *str = const_cast<char *>("hello world");


str[0] = 'H';

Accediendo a un índice fuera de límites

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`

En general, no se le permite crear un puntero fuera de límites. Un puntero debe apuntar a un


elemento dentro de la matriz, o uno más allá del final.

División entera por cero

int x = 5 / 0; // Undefined behavior

La división por 0 está definida matemáticamente y, como tal, tiene sentido que se trate de un
comportamiento indefinido.

Sin embargo:

float x = 5.0f / 0.0f; // x is +infinity

La mayoría de la implementación implementa IEEE-754, que define la división de punto flotante


por cero para devolver NaN (si el numerador es 0.0f ), el infinity (si el numerador es positivo) o el
-infinity (si el numerador es negativo).

Desbordamiento de enteros firmado

int x = INT_MAX + 1;

// x can be anything -> Undefined behavior

Si durante la evaluación de una expresión, el resultado no está definido


matemáticamente o no está en el rango de valores representables para su tipo, el
comportamiento no está definido.
(C ++ 11 párrafo 5/4 estándar)

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.

Por otra parte:

unsigned int x = UINT_MAX + 1;

https://riptutorial.com/es/home 166
// x is 0

está bien definido ya que:

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)

A veces los compiladores pueden explotar un comportamiento indefinido y optimizar

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"

Usando una variable local sin inicializar

int a;
std::cout << a; // Undefined behavior!

Esto da como resultado un comportamiento indefinido , porque a no está inicializado.

A menudo, incorrectamente, se afirma que esto se debe a que el valor es "indeterminado", o


"cualquier valor que haya en esa ubicación de memoria antes". Sin embargo, es el hecho de
acceder al valor de a en el ejemplo anterior lo que da un comportamiento indefinido. En la
práctica, la impresión de un "valor de basura" es un síntoma común en este caso, pero esa es
solo una forma posible de comportamiento indefinido.

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

El uso de un valor indeterminado de tipo de unsigned char no produce un comportamiento


indefinido si el valor se utiliza como:

• el segundo o tercer operando del operador condicional ternario;


• el operando derecho del operador de coma incorporado;
• el operando de una conversión a caracteres unsigned char ;

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 ;

o si el valor es descartado. En tales casos, el valor indeterminado simplemente se propaga al


resultado de la expresión, si corresponde.

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

Múltiples definiciones no idénticas (la regla de una definición)

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
}

El programa anterior muestra un comportamiento indefinido porque contiene dos definiciones de


la clase ::Foo , que tiene un enlace externo, en diferentes unidades de traducción, pero las dos
definiciones no son idénticas. A diferencia de la redefinición de una clase dentro de la misma
unidad de traducción, este compilador no requiere que este problema sea diagnosticado.

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.

Si el argumento para free no fue devuelto por malloc , el comportamiento es indefinido.

int* p1 = new int;


delete p1; // correct
// delete[] p1; // undefined
// free(p1); // undefined

int* p2 = new int[10];


delete[] p2; // correct
// delete p2; // undefined
// free(p2); // undefined

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[] .

Accediendo a un objeto como el tipo equivocado

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);

El resultado es un comportamiento indefinido.

Hay algunas excepciones a esta regla estricta de aliasing :

• 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

Desbordamiento de punto flotante

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
}

Llamando (Puro) a los Miembros Virtuales del Constructor o Destructor

La Norma (10.4) establece:

Las funciones miembro se pueden llamar desde un constructor (o destructor) de una


clase abstracta; el efecto de hacer una llamada virtual (10.3) a una función virtual pura
directa o indirectamente para el objeto que se está creando (o destruyendo) desde tal
constructor (o destructor) no está definido.

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.

Considere el siguiente ejemplo, modificado desde el enlace anterior:

class transaction
{
public:
transaction(){ log_it(); }
virtual void log_it() const = 0;
};

class sell_transaction : public transaction


{
public:
virtual void log_it() const { /* Do something */ }

https://riptutorial.com/es/home 170
};

Supongamos que creamos un objeto sell_transaction :

sell_transaction s;

Esto implícitamente llama al constructor de sell_transaction , que primero llama al constructor de


la transaction . Sin embargo, cuando se llama al constructor de la transaction , el objeto todavía
no es del tipo sell_transaction , sino más bien del tipo transaction .

En consecuencia, la llamada en transaction::transaction() a log_it , no hará lo que pueda


parecer algo intuitivo, es decir, call sell_transaction::log_it .

• Si log_it es puramente virtual, como en este ejemplo, el comportamiento no está definido.

• Si log_it no es virtual puro, se transaction::log_it .

Eliminar un objeto derivado a través de un puntero a una clase base que no


tiene un destructor virtual.

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 :

Si el tipo estático del objeto que se va a eliminar es diferente de su tipo dinámico, el


tipo estático será una clase base del tipo dinámico del objeto que se eliminará y el tipo
estático tendrá un destructor virtual o el comportamiento es indefinido.

Este es el caso, independientemente de la pregunta sobre si la clase derivada agregó algún


miembro de datos a la clase base.

Accediendo a una referencia colgante

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.

Extendiendo el espacio de nombres `std` o` posix`

El estándar (17.6.4.2.1 / 1) generalmente prohíbe extender el std nombres estándar :

El comportamiento de un programa en C ++ no está definido si agrega declaraciones o


definiciones al espacio de nombres estándar o a un espacio de nombres dentro del
espacio de nombres estándar, a menos que se especifique lo contrario.

Lo mismo ocurre con posix (17.6.4.2.2 / 1):

El comportamiento de un programa de C ++ no está definido si agrega declaraciones o


definiciones al espacio de nombres posix o a un espacio de nombres dentro del
espacio de nombres posix a menos que se especifique lo contrario.

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
};

Entonces lo siguiente está bien

namespace std
{
template<>
struct hash<foo>
{

https://riptutorial.com/es/home 172
public:
size_t operator()(const foo &f) const;
};
}

Desbordamiento durante la conversión hacia o desde el tipo de punto flotante

Si, durante la conversión de:

• un tipo entero a un tipo de punto flotante,


• un tipo de punto flotante a un tipo entero, o
• un tipo de punto flotante a un tipo de punto flotante más corto,

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

Conversión estática de base a derivada no válida

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 .

Función de llamada a través del tipo de puntero de función no coincidente

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

Modificar un objeto const

Cualquier intento de modificar un objeto const produce un comportamiento indefinido. Esto se


aplica a las variables const , miembros de objetos const y miembros de clase declarados const .
(Sin embargo, un mutable miembro de un const objeto no es const .)

Tal intento se puede hacer a través de const_cast :

const int x = 123;


const_cast<int&>(x) = 456;
std::cout << x << '\n';

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.

El siguiente programa oculta un error mucho más sutil:

#include <iostream>

class Foo* instance;

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();
};

const Foo& getFoo() {


static const Foo foo(123, instance);
return foo;
}

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.

Por lo tanto, el comportamiento indefinido ocurre aunque no hay construcciones obviamente


peligrosas en este programa.

Acceso a miembro inexistente a través de puntero a miembro

Cuando se accede a un miembro no estático de un objeto a través de un puntero a miembro, si el


objeto no contiene realmente al miembro indicado por el puntero, el comportamiento no está

https://riptutorial.com/es/home 174
definido. (Este puntero a miembro se puede obtener a través de static_cast ).

struct Base { int x; };


struct Derived : Base { int y; };
int Derived::*pdy = &Derived::y;
int Base::*pby = static_cast<int Base::*>(pdy);

Base* b1 = new Derived;


b1->*pby = 42; // ok; sets y in Derived object to 42
Base* b2 = new Base;
b2->*pby = 42; // undefined; there is no y member in Base

Conversión derivada a base no válida para punteros a miembros

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.

Aritmética de puntero no válido

Los siguientes usos de la aritmética de punteros provocan un comportamiento indefinido:

• Suma o resta de un entero, si el resultado no pertenece al mismo objeto de matriz que el


operando de puntero. (Aquí, se considera que el elemento uno más allá del final todavía
pertenece a la matriz.)

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

• Resta dos punteros si el resultado se desborda std::ptrdiff_t .

• 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".

struct Base { int x; };


struct Derived : Base { int y; };
Derived a[10];
Base* p1 = &a[1]; // ok
Base* p2 = p1 + 1; // UB; p1 points to Derived
Base* p3 = p1 - 1; // likewise
Base* p4 = &a[2]; // ok
auto p5 = p4 - p1; // UB; p4 and p1 point to Derived
const Derived* p6 = &a[1];
const Derived* p7 = p6 + 1; // ok; cv-qualifiers don't matter

Desplazando por un número de posiciones no válido

Para el operador de cambio incorporado, el operando derecho debe ser no negativo y


estrictamente menor que el ancho de bits del operando izquierdo promovido. De lo contrario, el
comportamiento es indefinido.

const int a = 42;


const int b = a << -1; // UB
const int c = a << 0; // ok
const int d = a << 32; // UB if int is 32 bits or less
const int e = a >> 32; // also UB if int is 32 bits or less
const signed char f = 'x';
const int g = f << 10; // ok even if signed char is 10 bits or less;
// int must be at least 16 bits

Volviendo de una función [[noreturn]]

C ++ 11

Ejemplo de la Norma, [dcl.attr.noreturn]:

[[ noreturn ]] void f() {


throw "error"; // OK
}
[[ noreturn ]] void q(int i) { // behavior is undefined if called with an argument <= 0
if (i > 0)
throw "positive";
}

Destruyendo un objeto que ya ha sido destruido.

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

Se produce un problema similar cuando se std::unique_ptr<T> un std::unique_ptr<T> para apuntar


a una T con duración de almacenamiento automática o estática.

void f(std::unique_ptr<S> p);


int main() {
S s;
std::unique_ptr<S> p(&s);
f(std::move(p)); // s destroyed upon return from f
} // UB: s destroyed

Otra forma de destruir un objeto dos veces es tener dos shared_ptr gestionen el objeto sin
compartir la propiedad entre ellos.

void f(std::shared_ptr<S> p1, std::shared_ptr<S> p2);


int main() {
S* p = new S;
// I want to pass the same object twice...
std::shared_ptr<S> sp1(p);
std::shared_ptr<S> sp2(p);
f(sp1, sp2);
} // UB: both sp1 and sp2 will destroy s separately
// NB: this is correct:
// std::shared_ptr<S> sp(p);
// f(sp, sp);

Recursión de plantilla infinita

Ejemplo de la Norma, [temp.inst] / 17:

template<class T> class X {


X<T>* p; // OK
X<T*> a; // implicit generation of X<T> requires
// the implicit instantiation of X<T*> which requires
// the implicit instantiation of X<T**> which ...
};

Lea Comportamiento indefinido en línea:


https://riptutorial.com/es/cplusplus/topic/1812/comportamiento-indefinido

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.

Así programa con los siguientes archivos

• foo.cpp

#include <iostream>

int dummyFoo = ((std::cout << "foo"), 0);

• bar.cpp

#include <iostream>

int dummyBar = ((std::cout << "bar"), 0);

• main.cpp

int main() {}

podría producir como salida:

foobar

barfoo

Eso puede llevar a Fiasco Orden de Inicialización Estática .

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);

Aquí s tendrá el valor 3 y será igual a ONE , TWO y FOUR .

Reparto estático a partir de un valor falso *

Si un valor void* se convierte en un puntero al tipo de objeto, T* , pero no se alinea correctamente


para T , el valor del puntero resultante no se especifica. Ejemplo:

// Suppose that alignof(int) is 4


int x = 42;
void* p1 = &x;
// Do some pointer arithmetic...
void* p2 = static_cast<char*>(p1) + 2;
int* p3 = static_cast<int*>(p2);

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

El resultado de un reinterpret_cast de un tipo de puntero de función a otro, o un tipo de referencia


de función a otro, no está especificado. Ejemplo:

int f();
auto fp = reinterpret_cast<int(*)(int)>(&f); // fp has unspecified value

C ++ 03

El resultado de un reinterpret_cast de un tipo de puntero de objeto a otro, o un tipo de referencia


de objeto a otro, no se especifica. Ejemplo:

int x = 42;
char* p = reinterpret_cast<char*>(&x); // p has unspecified value

Sin embargo, con la mayoría de los compiladores, esto era equivalente a


static_cast<char*>(static_cast<void*>(&x)) por lo que el puntero resultante p apuntaba al primer
byte de x . Esto se hizo el comportamiento estándar en C ++ 11. Ver tipo de conversión de puntos
para más detalles.

Resultado de algunas comparaciones de punteros

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.

Una referencia no es un objeto y, a diferencia de un objeto, no se garantiza que ocupe algunos


bytes contiguos de memoria. El estándar deja sin especificar si una referencia requiere algún
almacenamiento. Una serie de características del lenguaje conspiran para hacer que sea
imposible examinar de manera portátil cualquier almacenamiento que la referencia pueda ocupar:

• 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.

En la práctica, en algunos casos, una variable de referencia puede implementarse de manera


similar a una variable de puntero y, por lo tanto, ocupar la misma cantidad de almacenamiento
que un puntero, mientras que en otros casos una referencia puede no ocupar ningún espacio ya
que puede optimizarse. Por ejemplo, en:

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 .

Orden de evaluacion de argumentos de funcion.

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.

int f(int x, int y) {


printf("x = %d, y = %d\n", x, y);
}
int get_val() {
static int x = 0;
return ++x;
}
int main() {
f(get_val(), get_val());

https://riptutorial.com/es/home 181
}

C ++ 17

En C ++ 17, el orden de evaluación de los argumentos de la función permanece sin especificar.

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; }

void foo(from_int a, from_int b) {


}
void bar(from_int a, from_int b) {
}

auto which_func(bool b){


std::cout << b?"foo":"bar" << "\n";
return b?foo:bar;
}

int main(int argc, char const*const* argv) {


which_func( true )( make_int(1), make_int(2) );
}

esto debe imprimir:

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.

Estado movido de la mayoría de las clases de biblioteca estándar

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.

Lea Comportamiento no especificado en línea:


https://riptutorial.com/es/cplusplus/topic/4939/comportamiento-no-especificado

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 .

Paralelismo o concurrencia implica la ejecución de código al mismo tiempo.

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.

• GCC utiliza -fopenmp


• Clang usa -fopenmp
• MSVC usa /openmp

Examples
OpenMP: Secciones paralelas

Este ejemplo ilustra los conceptos básicos de la ejecución de secciones de código en paralelo.

Como OpenMP es una característica de compilador incorporada, funciona en cualquier


compilador compatible sin incluir bibliotecas. Si lo desea, puede incluir omp.h si desea usar
cualquiera de las funciones de la API openMP.

Código de muestra

std::cout << "begin ";


// This pragma statement hints the compiler that the
// contents within the { } are to be executed in as
// parallel sections using openMP, the compiler will
// generate this chunk of code for parallel execution
#pragma omp parallel sections
{
// This pragma statement hints the compiler that
// this is a section that can be executed in parallel
// with other section, a single section will be executed
// by a single thread.
// Note that it is "section" as opposed to "sections" above
#pragma omp section

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

empieza hola fin mundo comienza el mundo hola fin

OpenMP: Secciones paralelas

Este ejemplo muestra cómo ejecutar trozos de código en paralelo

std::cout << "begin ";


// Start of parallel sections
#pragma omp parallel sections
{
// Execute these sections in parallel
#pragma omp section
{
... do something ...
std::cout << "hello ";
}
#pragma omp section
{
... do something ...
std::cout << "world ";
}
#pragma omp section
{
... do something ...
std::cout << "forever ";
}
}
// end of parallel sections
std::cout << "end";

Salida

• comienza hola mundo para siempre termina

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.

OpenMP: Parallel For Loop

Este ejemplo muestra cómo dividir un bucle en partes iguales y ejecutarlas en paralelo.

// Splits element vector into element.size() / Thread Qty


// and allocate that range for each thread.
#pragma omp parallel for
for (size_t i = 0; i < element.size(); ++i)
element[i] = ...

// Example Allocation (100 element per thread)


// Thread 1 : 0 ~ 99
// Thread 2 : 100 ~ 199
// Thread 2 : 200 ~ 299
// ...

// 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 .

OpenMP: Recopilación paralela / Reducción

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.

// The Master vector


// We want a vector of results gathered from slave threads
std::vector<int> Master;

// Hint the compiler to parallelize this { } of code


// with all available threads (usually the same as logical processor qty)
#pragma omp parallel
{
// In this area, you can write any code you want for each

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(...);
}

// Slaves that finished their part of the job


// will perform this thread by thread one at a time
// critical section ensures that only 0 or 1 thread performs
// the { } at any time
#pragma omp critical
{
// Merge slave into master
// use move iterators instead, avoid copy unless
// you want to use it for something else after this section
Master.insert(Master.end(),
std::make_move_iterator(Slave.begin()),
std::make_move_iterator(Slave.end()));
}
}

// Have fun with Master vector


...

Lea Concurrencia con OpenMP en línea:


https://riptutorial.com/es/cplusplus/topic/8222/concurrencia-con-openmp

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

Véndeme en constancia correcta

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.
};

// Parameter is const: Doesn't modify parameter.


int const_correct_reader(const ConstCorrectClass& c) {
return c.getX();
}

// Parameter isn't const: Modifies parameter.


void const_correct_writer(ConstCorrectClass& c) {
c.setX(42);
}

const ConstCorrectClass invariant; // Instance is const: Can't be modified.


ConstCorrectClass variant; // Instance isn't const: Can be modified.

// ...

const_correct_reader(invariant); // Good. Calling non-modifying function on const instance.


const_correct_reader(variant); // Good. Calling non-modifying function on modifiable
instance.

const_correct_writer(variant); // Good. Calling modifying function on modifiable instance.


const_correct_writer(invariant); // Error. Calling modifying function on const 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.

Diseño correcto de la clase de Const

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:

// Assume class Field, with member function "void insert_value(int);".

class ConstIncorrect {
Field fld;

public:
ConstIncorrect(Field& f); // Modifies.

Field& getField(); // Might modify. Also exposes member as non-const reference,


// allowing indirect modification.
void setField(Field& f); // Modifies.

void doSomething(int i); // Might modify.


void doNothing(); // Might modify.
};

ConstIncorrect::ConstIncorrect(Field& f) : fld(f) {} // Modifies.


Field& ConstIncorrect::getField() { return fld; } // Doesn't modify.
void ConstIncorrect::setField(Field& f) { fld = f; } // Modifies.
void ConstIncorrect::doSomething(int i) { // Modifies.
fld.insert_value(i);
}
void ConstIncorrect::doNothing() {} // Doesn't modify.

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.

void doSomething(int i); // Modifies.


void doNothing() const; // Doesn't modify.
};

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 {}

// This won't work.


// No member functions can be called on const ConstIncorrect instances.
void const_correct_func(const ConstIncorrect& c) {
Field f = c.getField();
c.do_nothing();
}

// But this will.


// getField() and doNothing() can be called on const ConstCorrectCVQ instances.
void const_correct_func(const ConstCorrectCVQ& c) {
Field f = c.getField();
c.do_nothing();

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.

void doSomething(int i); // Modifies. Doesn't modify parameter (passed by value).


void doNothing() const; // Doesn't modify.
};

ConstCorrect::ConstCorrect(const Field& f) : fld(f) {}


Field& ConstCorrect::getField() const { return fld; }
void ConstCorrect::setField(const Field& f) { fld = f; }
void ConstCorrect::doSomething(int i) {
fld.insert_value(i);
}
void ConstCorrect::doNothing() const {}

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]; }

// ...
};

Esto es comúnmente utilizado en la biblioteca estándar, con la mayoría de los recipientes


proporcionando sobrecargas para tomar const Ness en cuenta.

Constar los parámetros de función correcta

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; }
};

void const_incorrect_function(Example& one, Example* two) {


one.func();
two->func();
}

void const_correct_function(const Example& one, const Example* two) {


one.func();
two->func();
}

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.]

// Read value from vector, then compute & return a value.


// Caches return values for speed.
template<typename T>
const T& bad_func(std::vector<T>& v, Helper<T>& h) {
// Cache values, for future use.
// Once a return value has been calculated, it's cached & its index is registered.
static std::vector<T> vals = {};

int v_ind = h.get_index(); // Current working index for v.


int vals_ind = h.get_cache_index(v_ind); // Will be -1 if cache index isn't registered.

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];
}

// Const correct version. Is identical to above version, so most of it shall be skipped.


template<typename T>
const T& good_func(const std::vector<T>& v, Helper<T>& h) {
// ...

// We're feeling tired all of a sudden, and this happens.


if (vals_ind != -1) {
vals[vals_ind] = temp;
} else {
v.push_back(temp); // Error: discards qualifiers.
vals_ind = vals.size() - 1;
h.register_index(v_ind, vals_ind);
}

return vals[vals_ind];
}

Constancia de la corrección como documentación

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.

Funciones de miembros calificados para CV const :


• Se puede asumir que cualquier función miembro que sea const tiene intención de leer la
instancia, y:
○ No modificará el estado lógico de la instancia a la que se llama. Por lo tanto, no deben
modificar ninguna variable miembro de la instancia a la que se llama, excepto las
variables mutable .
○ No debe llamar a ninguna otra función que pueda modificar ninguna variable miembro
de la instancia, excepto las variables mutable .
• A la inversa, se puede asumir que cualquier función miembro que no sea const tiene la
intención de modificar la instancia, y:
○ Puede o no puede modificar el estado lógico.
○ Puede o no llamar a otras funciones que modifican el estado lógico.

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) {}

// Our assumption was correct.


int ConstMemberFunctions::calc() const {
if (state_changed) {
cache = 3 * val;
state_changed = false;
}

return cache;
}

// Our assumption was correct.


int ConstMemberFunctions::squared_calc() const {
return calc() * calc();
}

// Our assumption was incorrect.


// Function fails to compile, due to `this` losing qualifiers.
void ConstMemberFunctions::bad_func() const {
set_val(863);
}

// Our assumption was correct.


void ConstMemberFunctions::set_val(int v) {
if (v != val) {
val = v;

https://riptutorial.com/es/home 194
state_changed = true;
}
}

Parámetros de la función const :


• Se puede suponer que cualquier función con uno o más parámetros que sean const tiene
intención de leer esos parámetros, y:
○ No modificará esos parámetros ni llamará a ninguna función miembro que los
modifique.
○ No debe pasar esos parámetros a ninguna otra función que los modifique y / o llame a
ninguna función miembro que los modifique.
• A la inversa, se puede suponer que cualquier función con uno o más parámetros que no son
const tiene la intención de modificar esos parámetros, y:
○ Puede o no modificar esos parámetros, o llamar a cualquier función miembro que los
modifique.
○ Pueden o no pasar esos parámetros a otras funciones que los modificarían y / o
llamarían a cualquier función miembro que los modificaría.

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:


// l is not modified, and l.set_val() won't be called.
// l may or may not be passed to const_function_parameter().
// r is modified, and/or r.set_val() may be called.
// r may or may not be passed to either of the preceding functions.
void one_const_one_not(const ConstMemberFunctions& l, ConstMemberFunctions& r);

// 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

// Our assumption was correct.


void const_function_parameter(const ConstMemberFunctions& c) {
std::cout << "With the current value, the output is: " << c.calc() << '\n'
<< "If squared, it's: " << c.squared_calc()

https://riptutorial.com/es/home 195
<< std::endl;
}

// Our assumption was correct.


void non_qualified_function_parameter(ConstMemberFunctions& c) {
c.set_val(42);
std::cout << "For the value 42, the output is: " << c.calc() << '\n'
<< "If squared, it's: " << c.squared_calc()
<< std::endl;
}

// Our assumption was correct, in the ugliest possible way.


// Note that const correctness doesn't prevent encapsulation from intentionally being broken,
// it merely prevents code from having write access when it doesn't need it.
void one_const_one_not(const ConstMemberFunctions& l, ConstMemberFunctions& r) {
// Let's just punch access modifiers and common sense in the face here.
struct Machiavelli {
int val;
int unimportant;
bool state_changed;
};
reinterpret_cast<Machiavelli&>(r).val = l.calc();
reinterpret_cast<Machiavelli&>(r).state_changed = true;

const_function_parameter(l);
const_function_parameter(r);
}

// Our assumption was incorrect.


// Function fails to compile, due to `this` losing qualifiers in c.set_val().
void bad_parameter(const ConstMemberFunctions& c) {
c.set_val(18);
}

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.

class DealBreaker : public ConstMemberFunctions {


public:
DealBreaker(int v = 0);

// A foreboding name, but it's const...


void no_guarantees() const;
}

DealBreaker::DealBreaker(int v /* = 0 */) : ConstMemberFunctions(v) {}

// Our assumption was incorrect.


// const_cast removes const-ness, making the compiler think we know what we're doing.
void DealBreaker::no_guarantees() const {
const_cast<DealBreaker*>(this)->set_val(823);
}

// ...

const DealBreaker d(50);


d.no_guarantees(); // Undefined behaviour: d really IS const, it may or may not be modified.

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.

Lea Const Correccion en línea: https://riptutorial.com/es/cplusplus/topic/7217/const-correccion

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.

Comparación con #define

Un constexpr es un reemplazo de tipo seguro para expresiones de tiempo de compilación basadas


en #define . Con constexpr la expresión evaluada en tiempo de compilación se reemplaza con el
resultado. Por ejemplo:

C ++ 11

int main()
{
constexpr int N = 10 + 2;
cout << N;
}

producirá el siguiente código:

cout << 12;

Una macro en tiempo de compilación basada en un preprocesador sería diferente. Considerar:

#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.

Por ejemplo (con #define ):

cout << N * 2;

formas:

cout << 10 + 2 * 2; // 14

Pero un constexpr daría correctamente 24 .

Comparacion con const

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.

Por lo tanto simplemente use:

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

constexpr int size = abs(10);

A menos que la función ( abs ) esté devolviendo un constexpr .

Todos los tipos básicos se pueden inicializar con constexpr .

C ++ 11

constexpr bool FailFatal = true;


constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";

De manera interesante y conveniente, también puede usar auto :

C ++ 11

constexpr auto domain = ".COM"; // const char * const domain = ".COM"


constexpr auto PI = 3.14; // constexpr double

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

constexpr int Sum(int a, int b)


{
return a + b;
}

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
}

Tenga en cuenta que si elimina constexpr de la especificación de tipo de retorno de la función, la


asignación a S no funcionará, ya que S es una variable constexpr , y debe asignarse una constante
de tiempo de compilación. De manera similar, el tamaño de la matriz tampoco será una expresión
constante, si la función Sum no es constexpr .

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

constexpr int Sum(int a, int b)


{
int a1 = a; // ERROR
return a + b;
}

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

constexpr int Sum(int a, int b)

https://riptutorial.com/es/home 201
{
constexpr int a1 = a; // ERROR
..

Además, lo siguiente tampoco compilará:

C ++ 11

constexpr int Sum(int a, int b)


{
return abs(a) + b; // or abs(a) + abs(b)
}

Dado que abs(a) no es una expresión constante (incluso abs(10) no funcionará, ya que abs no
devuelve constexpr int !

Que hay de esto

C ++ 11

constexpr int Abs(int v)


{
return v >= 0 ? v : -v;
}

constexpr int Sum(int a, int b)


{
return Abs(a) + b;
}

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:

template<class T, class ... Rest>


void g(T &&p, Rest &&...rs)
{
// ... handle p
if constexpr (sizeof...(rs) > 0)
g(rs...); // never instantiated with an empty argument list
}

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 es distinto de #ifdef . #ifdef compila condicionalmente el código, pero solo en


función de las condiciones que se pueden evaluar en el momento del preprocesamiento. Por
ejemplo, #ifdef no podría usarse para compilar condicionalmente el código dependiendo del valor
de un parámetro de plantilla. Por otro lado, if constexpr no puede usarse para descartar un
código sintácticamente no válido, mientras que #ifdef puede.

if constexpr(false) {
foobar; // error; foobar has not been declared
std::vector<int> v("hello, world"); // error; no matching constructor
}

Lea constexpr en línea: https://riptutorial.com/es/cplusplus/topic/3899/constexpr

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.

Además, algunos entornos de desarrollo integrados (IDE) también incluyen sistemas de


compilación especializados para ser utilizados específicamente con el IDE nativo. Ciertos
generadores de sistemas de compilación pueden generar estos formatos de proyecto / sistema de
compilación nativos IDE, como CMake para Eclipse y Microsoft Visual Studio 2012.

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" .

Los archivos CMake siempre se denominan "CMakeLists.txt" y ya deberían existir en el directorio


raíz de cada proyecto (y posiblemente también en los subdirectorios). Un archivo básico de
CMakeLists.txt tiene el siguiente aspecto:

cmake_minimum_required(VERSION 2.4)

project(HelloWorld)

add_executable(HelloWorld main.cpp)

Véalo en vivo en Coliru .

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 .

Genere un entorno de compilación para su compilador / IDE instalado desde la línea de

https://riptutorial.com/es/home 204
comandos:

> cmake .

Construye la aplicación con:

> cmake --build .

Esto genera el entorno de compilación predeterminado para el sistema, según el sistema


operativo y las herramientas instaladas. Mantenga el código fuente limpio de cualquier artefacto
de construcción con el uso de compilaciones "fuera de la fuente":

> mkdir build


> cd build
> cmake ..
> cmake --build .

CMake también puede abstraer los comandos básicos del shell de la plataforma del ejemplo
anterior:

> cmake -E make_directory build


> cmake -E chdir build cmake ..
> cmake --build build

CMake incluye generadores para una serie de herramientas de construcción e IDE comunes.
Para generar makefiles para nmake Visual Studio :

> cmake -G "NMake Makefiles" ..


> nmake

Compilando con GNU make

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

# Set some variables to use in our command


# First, we set the compiler to be g++
CXX=g++

# Then, we say that we want to compile with g++'s recommended warnings and some extra ones.
CXXFLAGS=-Wall -Wextra -pedantic

# This will be the output file


EXE=app

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)

# This is equivalent to saying


# app: $(SRCS)
# $(SRCS) can be separated, which means that this target would depend on each file.
# Note that this target has a "method body": the part indented by a tab (not four spaces).
# When we build this target, make will execute the command, which is:
# g++ -Wall -Wextra -pedantic -o app main.cpp
# I.E. Compile main.cpp with warnings, and output to the file ./app
$(EXE): $(SRCS)
@$(CXX) $(CXXFLAGS) -o $@ $(SRCS)

# This target should reverse the `all` target. If you call


# make with an argument, like `make clean`, the corresponding target
# gets called.
clean:
@rm -f $(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.

Para ejecutar esto desde la línea de comandos, haga lo siguiente:

$ 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.

Con la siguiente estructura de directorios:

.
+-- src
| +-- a.cpp
| +-- a.hpp
| +-- b.cpp
| +-- b.hpp
+-- Makefile

Esto sería un buen 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 .

Construyendo con scons

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')

Ahora, desde la terminal, ejecute scons . Deberías ver algo como

$ 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.

(aunque los detalles variarán dependiendo de su sistema operativo y compilador instalado).

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.

NMAKE (Utilidad de mantenimiento de programas de Microsoft)

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 ++.

NMAKE es un sistema de compilación que forma parte de la familia Make de sistemas de


compilación, pero tiene ciertas características distintas que divergen de los programas Make de
Unix, como la sintaxis de rutas de archivos específicas de Windows (que a su vez difiere de las
rutas de archivos de estilo Unix)

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.

Algunos programas notables de Autotools incluyen:

• 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):

./configure && make && make install

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

Lea Contenedores C ++ en línea: https://riptutorial.com/es/cplusplus/topic/10848/contenedores-c-


plusplus

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

Según la norma C ++,

La instrucción de switch hace que el control se transfiera a una de varias


declaraciones, según el valor de una condición.

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

La palabra clave catch introduce un controlador de excepciones, es decir, un bloque al que se


transferirá el control cuando se lance una excepción de tipo compatible. A la palabra clave catch
le sigue una declaración de excepción entre paréntesis, que es similar en forma a una declaración
de parámetro de función: el nombre del parámetro se puede omitir, y se permiten los puntos
suspensivos ... que coinciden con cualquier tipo. El controlador de excepciones solo manejará la
excepción si su declaración es compatible con el tipo de excepción. Para más detalles, ver
excepciones de 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

En una declaración de cambio, introduce una etiqueta a la que se saltará si el valor de la


condición no es igual a ninguno de los valores de las etiquetas de caso.

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

Define un constructor predeterminado, un constructor de copia, un constructor de movimiento, un

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

La primera subestación de una sentencia if puede ir seguida de la palabra clave else . La


subestación después de la palabra clave else se ejecutará cuando la condición sea falsey (es
decir, cuando no se ejecute la primera subestación).

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.

bool f(int arg) {


bool result = false;
hWidget widget = get_widget(arg);
if (!g()) {
// we can't continue, but must do cleanup still
goto end;
}
// ...
result = true;
end:

https://riptutorial.com/es/home 216
release_widget(widget);
return result;
}

regreso

Devuelve el control de una función a su interlocutor.

Si el return tiene un operando, el operando se convierte al tipo de retorno de la función y el valor


convertido se devuelve al llamante.

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).

int main(int argc, char** argv) {


if (argc < 2) {
std::cout << "Missing argument\n";
return EXIT_FAILURE; // equivalent to: exit(EXIT_FAILURE);
}
}

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.

void print_asterisks(int count) {


if (count < 0) {

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;
}

3. Cuando se produce el throw en un declarador de función, introduce una especificación de


excepción dinámica, que enumera los tipos de excepciones que la función puede propagar.

// this function might propagate a std::runtime_error,


// but not, say, a std::logic_error
void risky() throw(std::runtime_error);
// this function can't propagate any exceptions
void safe() throw();

Las especificaciones de excepciones dinámicas están en desuso a partir de C ++ 11.

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:

unsigned int predecessor(unsigned int x) {


return (x > 0) ? (x - 1) : (throw std::invalid_argument("0 has no predecessor"));
}

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.

std::vector<int> v(N); // if an exception is thrown here,


// it will not be caught by the following catch block
try {
std::vector<int> v(N); // if an exception is thrown here,
// it will be caught by the following catch block
// do something with v
} catch (const std::bad_alloc&) {
// handle bad_alloc exceptions from the try block

https://riptutorial.com/es/home 218
}

Estructuras condicionales: if, if..else

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,

Si queremos comprobar si hay varias expresiones, podemos hacerlo de dos maneras:

utilizando operadores binarios :

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

usando if / ifelse / else :

para un simple cambio ya sea si o bien

if (a== "test") {
//will execute if a is a string "test"
} else {
// only if the first failed, will execute
}

para opciones múltiples:

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

Saltar declaraciones: romper, continuar, goto, salir.

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;
}

en este caso, si se satisface el caso 1, entonces se ejecuta el bloque 1, lo que realmente


queremos es que solo se procese el bloque1, pero una vez que se procesa el bloque1, los
bloques restantes, el bloque2, el bloque3 y el bloqueo por defecto también se procesan, aunque
solo el caso 1 fue satisfecho Para evitar esto, usamos break al final de cada bloque como:

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;

Ejemplo considera lo siguiente:

for(int i=0;i<10;i++){
if(i%2==0)
continue;
cout<<"\n @"<<i;
}

que produce la salida:

@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;

Nota: El uso de la instrucción goto es altamente desaconsejable porque dificulta el seguimiento


del flujo de control de un programa, lo que hace que el programa sea difícil de entender y de
modificar.

Ejemplo:

int num = 1;
STEP:
do{

if( num%2==0 )
{
num = num + 1;
goto STEP;
}

cout << "value of num : " << num << endl;


num = num + 1;
}while( num < 10 );

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:

void exit (int exit code);

cstdlib define los códigos de salida estándar EXIT_SUCCESS y EXIT_FAILURE .

Lea Control de flujo en línea: https://riptutorial.com/es/cplusplus/topic/7837/control-de-flujo

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.

C ++ también admite la notación de conversión de estilo de función, T(expr) y la notación de


conversión de estilo de C, (T)expr .

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:

• La dynamic_cast<Derived&>(base) a un tipo de referencia dynamic_cast<Derived&>(base) , como


en dynamic_cast<Derived&>(base) , produce un lvalue. Por lo tanto, cuando desea hacer algo
con el mismo objeto pero tratarlo como un tipo diferente, se convertiría a un tipo de
referencia de valor l.
• La conversión a un tipo de referencia de rvalue, como en static_cast<string&&>(s) , produce
un rvalue.
• La conversión a un tipo que no es de referencia, como en (int)x , produce un prvalue, que
puede considerarse como una copia del valor que se está emitiendo, pero con un tipo
diferente del original.

La palabra clave reinterpret_cast es responsable de realizar dos tipos diferentes de conversiones


"inseguras":

• 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:

• Base a conversiones derivadas

• 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.

• Para void , lo que descarta el valor de la expresión.

// on some compilers, suppresses warning about x being unused


static_cast<void>(x);

• 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

• Desde un lvalue a un xvalue, como en std::move . Ver movimiento semántico .

Examples
Base a conversión derivada

Un puntero a la clase base se puede convertir en un puntero a la clase derivada usando


static_cast . static_cast no realiza ninguna comprobación en tiempo de ejecución y puede
provocar un comportamiento indefinido cuando el puntero no apunta al tipo deseado.

struct Base {};


struct Derived : Base {};
Derived d;
Base* p1 = &d;
Derived* p2 = p1; // error; cast required
Derived* p3 = static_cast<Derived*>(p1); // OK; p2 now points to Derived object
Base b;
Base* p4 = &b;
Derived* p5 = static_cast<Derived*>(p4); // undefined behaviour since p4 does not
// point to a Derived object

Del mismo modo, una referencia a la clase base se puede convertir en una referencia a la clase
derivada usando static_cast .

struct Base {};


struct Derived : Base {};
Derived d;
Base& r1 = d;

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 ).

struct Base { virtual ~Base(); }; // Base is polymorphic


struct Derived : Base {};
Base* b1 = new Derived;
Derived* d1 = dynamic_cast<Derived*>(b1); // OK; d1 points to Derived object
Base* b2 = new Base;
Derived* d2 = dynamic_cast<Derived*>(b2); // d2 is a null pointer

Arrojando constness

Un puntero a un objeto const se puede convertir en un puntero a un objeto no constante utilizando


la palabra clave const_cast . Aquí usamos const_cast para llamar a una función que no es const-
correcta. Solo acepta un argumento no const char* aunque nunca escribe a través del puntero:

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.

const int x = 123;


int& mutable_x = const_cast<int&>(x);
mutable_x = 456; // may compile, but produces *undefined behavior*

Tipo de conversión de punning

Un puntero (referencia de referencia) a un tipo de objeto se puede convertir en un puntero


(referencia de referencia) a cualquier otro tipo de objeto utilizando reinterpret_cast . Esto no llama
a ningún constructor o funciones de conversión.

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

El resultado de reinterpret_cast no está especificado, excepto que un puntero (referencia


respectiva) sobrevivirá un viaje de ida y vuelta desde el tipo de origen al tipo de destino y
viceversa, siempre que el requisito de alineación del tipo de destino no sea más estricto que el del
tipo de origen.

int x = 123;
unsigned int& r1 = reinterpret_cast<unsigned int&>(x);
int& r2 = reinterpret_cast<int&>(r1);
r2 = 456; // sets x to 456

En la mayoría de las implementaciones, reinterpret_cast no cambia la dirección, pero este


requisito no se estandarizó hasta C ++ 11.

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.

El uso de reinterpret_cast se considera peligroso porque la lectura o escritura a través de un


puntero o una referencia obtenida mediante reinterpret_cast puede desencadenar un
comportamiento indefinido cuando los tipos de origen y destino no están relacionados.

Conversión entre puntero y entero

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.

void register_callback(void (*fp)(void*), void* arg); // probably a C API


void my_callback(void* x) {
std::cout << "the value is: " << reinterpret_cast<long>(x); // will probably compile
}
long x;
std::cin >> x;
register_callback(my_callback,
reinterpret_cast<void*>(x)); // hopefully this doesn't lose information...

Conversión por constructor explícito o función de conversión explícita

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

static_castpuede realizar cualquier conversión implícita. Este uso de static_cast ocasionalmente


puede ser útil, como en los siguientes ejemplos:

• Cuando se pasan argumentos a una elipsis, el tipo de argumento "esperado" no se conoce


estáticamente, por lo que no se producirá una conversión implícita.

const double x = 3.14;


printf("%d\n", static_cast<int>(x)); // prints 3
// printf("%d\n", x); // undefined behaviour; printf is expecting an int here
// alternative:
// const int y = x; printf("%d\n", y);

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.

• Un operador de asignación de clase derivada puede llamar a un operador de asignación de


clase base de la siguiente manera:

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;
}
};

Enum las conversiones

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.

• La conversión de un tipo de enumeración sin ámbito a un tipo aritmético es una conversión


implícita; es posible, pero no necesario, usar static_cast .

C ++ 11

• Cuando un tipo de enumeración de ámbito se convierte en un tipo aritmético:

○ Si el valor de la enumeración se puede representar exactamente en el tipo de destino,


el resultado es ese valor.
○ De lo contrario, si el tipo de destino es un tipo entero, el resultado no se especifica.
○ De lo contrario, si el tipo de destino es un tipo de punto flotante, el resultado es el
mismo que el de la conversión al tipo subyacente y luego al tipo de punto flotante.

Ejemplo:

enum class Format {


TEXT = 0,
PDF = 1000,
OTHER = 2000,
};
Format f = Format::PDF;
int a = f; // error
int b = static_cast<int>(f); // ok; b is 1000
char c = static_cast<char>(f); // unspecified, if 1000 doesn't fit into char
double d = static_cast<double>(f); // d is 1000.0... probably

• Cuando un entero o tipo de enumeración se convierte en un tipo de enumeración:

○ Si el valor original está dentro del rango de la enumeración de destino, el resultado es


ese valor. Tenga en cuenta que este valor puede ser desigual para todos los
enumeradores.
○ De lo contrario, el resultado es no especificado (<= C ++ 14) o indefinido (> = C ++
17).

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

• Cuando un tipo de punto flotante se convierte en un tipo de enumeración, el resultado es el


mismo que la conversión al tipo subyacente de la enumeración y luego al tipo de
enumeración.

enum Direction {
UP = 0,
LEFT = 1,
DOWN = 2,
RIGHT = 3,
};
Direction d = static_cast<Direction>(3.14); // d is RIGHT

Derivado a conversión base para punteros a miembros

Un puntero a miembro de clase derivada se puede convertir en un puntero a miembro de clase


base usando static_cast . Los tipos apuntados deben coincidir.

Si el operando es un puntero nulo al valor miembro, el resultado también es un puntero nulo al


valor miembro.

De lo contrario, la conversión solo es válida si el miembro al que apunta el operando existe


realmente en la clase de destino, o si la clase de destino es una clase base o derivada de la clase
que contiene el miembro al que apunta el operando. static_cast no comprueba la validez. Si la
conversión no es válida, el comportamiento no está definido.

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 *

En C ++, void* no se puede convertir implícitamente a T* donde T es un tipo de objeto. En su


lugar, static_cast debe usarse para realizar la conversión explícitamente. Si el operando

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.

// allocating an array of 100 ints, the hard way


int* a = malloc(100*sizeof(*a)); // error; malloc returns void*
int* a = static_cast<int*>(malloc(100*sizeof(*a))); // ok
// int* a = new int[100]; // no cast needed
// std::vector<int> a(100); // better

const char c = '!';


const void* p1 = &c;
const char* p2 = p1; // error
const char* p3 = static_cast<const char*>(p1); // ok; p3 points to c
const int* p4 = static_cast<const int*>(p1); // unspecified in C++03;
// possibly unspecified in C++11 if
// alignof(int) > alignof(char)
char* p5 = static_cast<char*>(p1); // error: casting away constness

Casting estilo c

El casting de estilo C se puede considerar el casting de "Mejor esfuerzo" y se denomina así


porque es el único casting que se podría usar en C. La sintaxis de este casting es la
(NewType)variable .

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.

Lea Conversiones de tipo explícito en línea:


https://riptutorial.com/es/cplusplus/topic/3090/conversiones-de-tipo-explicito

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.

Considera la siguiente función:

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.

Sin embargo, tiene dos efectos visibles en el usuario:

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.

2. Los efectos secundarios de los constructores de copia / movimiento no están garantizados


en circunstancias en las que puede producirse la elección. Considera lo siguiente:

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

Y, por lo tanto, no puede depender de la llamada de los constructores de copiar / mover en


contextos donde es posible la elecció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á.

Copia garantizada elision

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.

C ++ 17 ordena la elisión al redefinir efectivamente el significado mismo de ciertas expresiones


para que no se realicen copias / movimientos. Considere el código anterior.

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:

std::lock_guard the_lock = get_lock();

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;
};

void foo (trivially_copyable a) {}

foo(trivially_copyable{}); //copy elision not mandated

Valor de retorno elision

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");
}

Casi todos los compiladores eludirán la construcción temporal en este caso.

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.

void func(std::string str) { ... }

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.

Valor de retorno con nombre elision

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.

std::string func(std::string str)


{
str.assign("foo");
//Do stuff
return str; //No elision possible
}

Copia inicializacion elision

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 str = std::string("foo");

La inicialización de la copia efectivamente transforma esto en std::string str("foo"); (hay


diferencias menores).

Esto también funciona con valores de retorno:

std::string func()
{
return std::string("foo");
}

std::string str = func();

Sin la elección de copia, esto provocaría 2 llamadas al constructor de movimientos de std::string


. Copy elision permite que esto llame al constructor de movimientos 1 o cero veces, y la mayoría
de los compiladores optarán por este último.

Lea Copia elision en línea: https://riptutorial.com/es/cplusplus/topic/2489/copia-elision

https://appwritefunc.yet-another-testing-