0% encontró este documento útil (0 votos)
81 vistas22 páginas

Capítulo 02 C++

Este documento presenta una introducción básica al lenguaje de programación C++. Explica que C++ es un lenguaje compilado que consiste en archivos fuente que se compilan en archivos de objeto y luego se enlazan en un programa ejecutable. También describe los tipos básicos en C++ como enteros, caracteres y números de punto flotante, y explica que cada nombre y expresión tiene un tipo que determina las operaciones permitidas. Finalmente, presenta un programa "Hola Mundo" básico como ejemplo.

Cargado por

José Espinoza
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)
81 vistas22 páginas

Capítulo 02 C++

Este documento presenta una introducción básica al lenguaje de programación C++. Explica que C++ es un lenguaje compilado que consiste en archivos fuente que se compilan en archivos de objeto y luego se enlazan en un programa ejecutable. También describe los tipos básicos en C++ como enteros, caracteres y números de punto flotante, y explica que cada nombre y expresión tiene un tipo que determina las operaciones permitidas. Finalmente, presenta un programa "Hola Mundo" básico como ejemplo.

Cargado por

José Espinoza
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
Está en la página 1/ 22

2

Un recorrido por C++: lo básico


Lo primero que hacemos,
matemos a todos los
abogados de idiomas. –
Enrique VI, Parte II

2.1 Introducción
El objetivo de este capítulo y los tres siguientes es darte una idea de lo que es C++, sin entrar en
muchos detalles. Este capítulo presenta informalmente la notación de C++, el modelo de memoria y
computación de C++, y los mecanismos básicos para organizar el código enunprograma. Estas son
las instalaciones del lenguaje que soportan los estilos más vistos en C y a veces llamados
programación procedimental. El capítulo 3 sigue presentando los mecanismos de abstracción de
C++. Los capítulos 4 y 5 dan ejemplos de facilidadesde libra estándar.
La suposición es que has programado antes. De lo contrario, considere leer un libro de texto,
como Programming: Principles and Practice Using C++ [Stroustrup, 2009], antes de continuar
aquí. Incluso si ha programado antes, el lenguaje que utilizó o las aplicaciones que escribió pueden
ser muy diferentes del estilo de C ++ presentado aquí. Si encuentra que este "recorrido relámpago"es
confuso, vaya a la presentación másystemática que comienza en el Capítulo 6.
Este recorrido por C ++ nos ahorra una presentación estrictamente ascendente del lenguaje y las
instalaciones de la biblioteca al permitir el uso de un rico conjunto de instalaciones incluso en los
primeros capítulos. Por ejemplo, los bucles no sediscuten en detalle hasta el Capítulo 10, pero se
usarán de manera obvia mucho antes de eso. Del mismo modo, la descripción detallada de las clases,
las plantillas, el uso de almacenamiento libre y la biblioteca estándar se distribuyen en muchos
capítulos, pero los tipos de biblioteca estándar, como vector, cadena, complejo, mapa, unique_ptry
ostream,se utilizanlibremente cuando es necesario para mejorar los ejemplos de código.
Como analogía, piense en un breve recorrido turístico por una ciudad, como Copenhague o Nueva
York. En solo unas horas, se le da un vistazo rápido a las principales atracciones, se le cuentan algunas
38 Un recorrido por C++: lo básico Capítulo 2
historias de fondo y, por lo general, se le dan algunas sugerencias sobre qué ver a continuación. No
conoces la ciudad después de tal recorrido. No entiendes todo lo que has visto y oído. Para conocer
realmente una ciudad, hay que vivir en ella, a menudo durante años. Sin embargo, con un poco de
suerte, habrás obtenido un poco de una visión general, una noción de lo que es especial acerca de la
ciudad, e ideas de lo que podría ser de tu interés. Después del recorrido, la verdadera exploración
puede comenzar.
Este recorrido presenta C ++ como untodo integrado, en lugar de como un pastel de capas. En
consecuencia, no identifica las características del lenguaje como presentes en C, parte de C++98, o
nuevas en C++11. Dicha información histórica se puede encontrar en §1.4 y Capítulo 44.

2.2 Lo básico
C++ es un lenguaje compilado. Para que un program se ejecute, su texto de origen tiene que ser
procesado por un compilador, produciendo archivos de objeto, que son combinados por un enlazador
que produce un programa ejecutable. Un programa de C ++ generalmente consta de muchos archivos
de código fuente (generalmente simplemente llamados archivos fuente).

archivo source compilar archivo de


enla
1 objeto 1
archivo fuente 2 compilar archivo de ce archivo
objeto 2 ejecutable

Se crea un programa ejecutable para una combinación específica de hardware/sistema; no es portátil,


por ejemplo, desde una Mac a una PC con Windows. Cuando hablamos de portabilidad de programas
C++, generalmente nos referimos a la portabilidad del código fuente; es decir, el código fuente puede
compilarsede forma completa y ejecutarse en una variedad de sistemas.
La norma ISO C++ define dos tipos de entidades:
• Características principales dellenguaje, como tipos integrados (por ejemplo, char e int)ybucles
(por ejemplo, parainstrucciones -y instrucciones while)
• Componentes de bibliotecaestándar, como contenedores (por ejemplo, vector y mapa)y
operaciones de E/S
(por ejemplo, << y getline())
Los componentes de la biblioteca estándar son código C++ perfectamente ordinario proporcionado
por cada implementación de C++. Es decir, la biblioteca estándar de C++ se puede implementar en
C++(y es con usos muy menores de código máquina para cosas como el cambio de contexto de
subprocesos). Esto implica que C++ es lo suficientemente expresivo y eficiente para las tareas de
programación de sistemas más exigentes.
C++ es un lenguaje de tipo estático. Es decir, el tipo de cada entidad (por ejemplo, objeto, valor,
nombre y expresión) debe ser conocido por el compilador en su punto de uso. El tipo de objeto
determina el conjunto de operaciones que le son aplicables.
Sección 2.2.1 ¡Hola, mundo!
39
2.2.1 ¡Hola, mundo!
El programa mínimo de C++ es int main() { } // el
programa mínimo de C++

Esto define una función llamada principal, que no toma argumentos y no hace nada (§15.4).
Curly braces, { }, agrupación expresa en C++. Aquí, indican el inicio y el final de la función body.
La barra diagonal doble, //, comienza un comentario que se extiende hasta el final de la línea. Un
comentario es para el lector humano; el compilador ignora los comentarios.
Cada programa de C++ debe tener exactamente una función global denominada main(). El
programa comienza ejecutando esa function. El valor int devuelto por main(), si lo hay, es el valor
devuelto del programa a ''el sistema''. Si no se devuelve ningún valor, el sistema recibirá un valor que
indica la finalización correcta. Un valor distinto de cero de main() indica un error. El sistema
operativo y el entorno de ejecución no utilizan ese valor de retorno: los entornos basados en
Linux/Unix a menudo lo hacen, pero los entornos basados en Windows rara vez lo hacen.
Normalmente, un programa produce algunos resultados. Aquí hay un programa que escribe ¡Hola,
Mundo! :
#include<iostream>

int main()
{ std::cout << "¡Hola, Mundo!\n";
}

La línea #include <iostream>instruye al compilador para que incluya las declaraciones de las
instalaciones de E/S de flujo estándar tal como se encuentran en iostream. Sin estas declaraciones, la
expresión std::cout << "¡Hola, mundo!\n"

no tendría sentido. El operador << (''put to'') escribe su segundo argumento en su primero. En este
caso, el literal de cadena "Hello, World!\n" se escribe en el flujo de salida estándar std::cout. Una
cadena literal es una secuencia de caracteres rodeados de comillas dobles. En un literal de cadena, el
carácter de barra diagonal inversa \ seguido de otro carácter denota un solo "carácter especial". En
este caso, \n es el carácter de la nueva línea, de modo que los caracteres escritos son Hello, World!
seguido de una nueva línea.
El std:: especifica que el nombre cout se encuentra en el espacio de nombres de la biblioteca
estándar (§2.4.2, Capítulo 14). Por lo general, omito el std:: cuando se discuten las características
estándar; §2.4.2 muestra cómo hacer quelos nombres de un nombre sea visible sin unacalificación
explícita.
Esencialmente, todo el código ejecutable se coloca en funciones y se llama directa o
indirectamente desde main(). Por ejemplo:
#include<iostream>

usar el espacio de nombres std; makenames de std visible sin std:: (§2.4.2)
40 Un recorrido por C++: lo básico Capítulo 2
cuadrado un número de coma flotante de doble precisión
cuadrado doble (doble x)
{ devolver x∗x;
}
print_square vacío (doble x)
{ cout << "el cuadrado de " << x << " es " << cuadrado(x) << "\n";
}

int main()
{
print_square (1.234); // imprimir:el cuadrado de 1.234 es 1.52276
}

Un vacío ''return type'' indica que una función no devuelve un valor.

2.2.2 Tipos, variables y aritmética


Cada nombre y cada expresión tiene un tipo que determina las operaciones que se pueden realizar en
él. Por ejemplo, la declaración
pulgada int;

especifica que inch es oftype int; esdecir, inch es una variable entera.
Una declaración es una declaración que introduce un nombre en el programa. Especifica un tipo
para la entidad con nombre:
• Un tipo define un conjunto de valores posibles y un conjunto de operaciones (para un objeto).
• Un objeto es una memoria que contiene un valor de algún tipo.
• Un valor es un conjunto de bits interpretados según un tipo.
• Una variable es un objeto con nombre.
C++ ofrece una variedad de tipos fundamentales. Por ejemplo:
bool // Booleano, los valores posibles son verdadero y falso
carácter // carácter, por ejemplo, 'a', 'z' y '9' int
// entero, por ejemplo,1,42 y 1066
número de coma flotante de doble precisión // de doble precisión, por ejemplo, 3.14 y 299793.0

Cada tipo fundamental correspondedirectamente a las instalaciones de hardware y tiene un tamaño


fijo que determina el rango de valores que se pueden almacenar en él:

bool:

char:

int:

doble:
41
Una variable char es del tamaño natural para contener un carácter en una máquina dada (típicamente
un byte de 8 bits), y los tamaños de otros tipos se citan en múltiplos del tamaño de un char. El tamaño
de un tipo está definido por la implementación (es decir, puede variar entre diferentesmáquinas) y
puede obtenerse mediante el tamaño del operador; por ejemplo, sizeof(char) es igual a 1 y sizeof(int)
es a menudo 4.
Los operadores aritméticos se pueden utilizar para combinaciones apropiadas de estos tipos:
Sección 2.2.2 Tipos, variables y aritmética

x+y más
+x unar yplus
x−y menos
−x unar yminus
x∗y multiplicar
x/a dividir
x%y resto (módulo) para enteros
También lo pueden hacer los operadores de comparación:
x==y // igual x!=y // no
igual x<y // menor que x>y
// mayor que x<=y
// menor o igual x>=y
// mayor o igual que o
igual

En asignaciones y en operaciones aritméticas, C++ realiza todas las conversiones significativas


(§10.5.3) entre los tipos básicos para que puedan mezclarse libremente:
some_function vacío() función que no devuelve Valor
{
doble d = 2,2; inicializennúmero de punto de registro
int i = 7; initializeinteger
d = d+i; asignar suma a d
i = d∗i; asignar el producto a i (truncando el doble d*i a un int)
}
Tenga en cuenta que = es el operador de asignación y == prueba la igualdad.
C++ ofrece una variedad de notaciones para expresar la inicialización, como el = utilizado
anteriormente, y una forma universal basada en listas de inicializadores delimitadas por corsés
rizadas:
doble d1 = 2,3; double d2
{2.3};
complejo<doble> z = 1; acomplexnumber con escalares de coma flotante de doble precisión
complejo<doble> z2 {d1,d2};
complejo<doble> z3 = {1,2}; el = es opcional con { ... }

vector<int> v {1,2,3,4,5,6}; avector de ints


La forma = es tradicional y se remonta a C, pero en caso de duda, use la forma general {}-list
(§6.3.5.2). Si nada más, le ahorra conversiones que pierden información (reducción de conversiones;
§10.5):
42 Un recorrido por C++: lo básico Capítulo 2
int i1 = 7,2; i1 se convierte en 7
int i2 {7.2}; error: conversión de coma flotante a entero
int i3 = {7.2}; error : conversión de coma flotante a entero (el = es redundante)
Una constante (§2.2.3) no puede dejarse sin inicializar y una variable sólo debe dejarse sin inicializar
encircunstancias extremas y raras. No introduzcas un nombre hasta que tengas un valor adecuado
para ello. Los tipos definidos por el usuario (como cadena, vector, matriz, Motor_controllery
Orc_warrior)se pueden definir para inicializarse implícitamente (§3.2.1.1).
Al definir una variable, en realidad no es necesario indicar su tipo explícitamente cuando se puede
deducir del inicializador:
auto b = verdadero; abool
auto ch = 'x'; achar
auto i = 123; un int
auto d = 1,2; adouble
auto z = sqrt(y); zhas el tipo de whateversqr t(y)retur ns
Con auto, usamos la sintaxis = porque no hay ninguna conversión de tipo involucrada que pueda
causar problemas (§6.3.6.2).
Usamos auto donde no tenemos una razón específica para mencionar el tipo explícitamente. Las
"razones específicas" incluyen:
• La definición está en un amplio ámbito donde queremos make el tipo claramente visible para
los lectores de nuestro código.
• Queremos ser explícitos sobre el rango o la precisión de una variable (por ejemplo, doble en
lugar de flotar). Usando auto,evitamos la redundancia y escribir nombres de tipo largos. Esto es
especialmente importante enla mezcla de programasgenéricos donde el tipo exacto de un objeto
puede ser difícil de conocer para el programador y los nombres de tipo pueden ser bastante
largos (§4.5.1).
Además de los operadores aritméticos y lógicos convencionales (§10.3), C++ ofrece operaciones
más específicas para modificar unavaria ble:
x+=y x=x+y
++x incremento: x = x+1
x−=y x=x-y
−−x decremento: x = x-1
x∗=y escala: x = x*y
x/=y escala: x = x/y
x%=y x=x%y
Estos operadores son concisos, convenientes y de uso muy frecuente.

2.2.3 Constantes
C++ admite dos nociones de inmutabilidad (§7.5):
• const: significa aproximadamente ''Prometo no cambiar este valor'' (§7.5). Esto se utiliza
principalmente para especificar interfaces, de modo que los datos se puedan pasar a las
funciones sin temor a que se modifiquen. El compilador hace cumplir la promesa hecha por
const.
• constexpr: significa aproximadamente "ser evaluado en tiempo de compilación" (§10.4). Esto
se utiliza principalmente para especificar constantes, para permitir la colocación de data en
la memoria donde es poco probable que se corrompa, y para el rendimiento.
43
Por ejemplo:
const int dmv = 17; DMV es una constante con nombre
int var = 17; varisnot una constante
constexpr doble max1 = 1,4∗cuadrado (dmv); Ok si cuadrado(17) es una expresión
constante
constexpr doble max2 = 1,4∗cuadrado(var); Error :Varisnot una expresión constante
const doble max3 = 1,4∗cuadrado(var); OK, maybeevaluado en tiempo de
ejecución
Sección 2.2.3 Constantes

suma doble(vector const<double>&); sum no modificará su argumento (§2.2.5)


vector<double> v {1.2, 3.4, 4.5}; visnot una constante
const doble s1 = suma(v); Aceptar: evaluado en tiempo de ejecución
constexpr doble s2 = suma(v); Error :sum(v) no es expresión constante
Para que una función sea utilizable en una expresión constante,es decir, en una expresión que será
evaluada por el compilador, debe definirse constexpr. Por ejemplo:
constexpr double square(double x) { return x∗x; }

Para ser constexpr,una función debe ser bastante simple: solo una declaración de retornoque calculaun
valor. Una función constexpr se puede usar para argumentos no constantes, pero cuando se hace eso,
el resultado no es una expresión constante. Permitimos que se llame a una función constexpr con
argumentos de expresión no constante en contextos que no requieren expresiones constantes, de
modo que no tengamos que definir esencialmente la misma función dos veces: una para expresiones
constantes y otra para variables.
En algunos lugares, las expresiones constantes son requeridas por las reglas de lenguaje (por
ejemplo, límites de matriz (§2.2.5, §7.3), etiquetas de mayúsculas y minúsculas (§2.2.4, §9.4.2),
algunos argumentos de plantilla (§25.2) y constantes declaradas usando constexpr). En otros casos, la
ev aluation en tiempo de compilaciónes importante para el rendimiento. Independientemente de los
problemas de rendimiento, la noción de inmutabilidad (de un objeto con un estado inmutable) es una
preocupación importante en materia de diseño (§10.4).

2.2.4 Pruebas y bucles


C++ proporciona un conjunto convencional de instrucciones para expresar la selección y el bucle.
Por ejemplo, aquí hay una función simple que solicita al usuario y devuelve un booleano que indica
la respuesta:
bool accept()
{
cout << "¿Desea continuar (y o n)?\n"; // writequestion

respuesta char = 0;
cin >> respuesta; // leer respuesta

si (respuesta == 'y') devuelve true;


devolver false;
}
44 Un recorrido por C++: lo básico Capítulo 2
Para que coincida con el operador de salida << (''put to''), se utiliza el operador de >> (''get from'') para
la entrada; cin es el flujo de entrada estándar. El tipo de operando de la derecha de >> determina qué
entrada se acepta, y su operando de la derecha es el objetivo de la operación de entrada. El carácter
\n al final de la cadena de salida representa una nueva línea (§2.2.1).
El ejemploe podría mejorarse teniendo en cuenta una respuesta n (para ''no''):
bool accept2()
{
cout << "¿Desea continuar (y o n)?\n"; // writequestion

respuesta char = 0;
cin >> respuesta; // leer respuesta
switch (respuesta)
{ caso 'y': return
true;
caso 'n': devolver
falso;
predeterminado:
cout << "Tomaré eso por un no.\n";
devolver false;
}
}

Una instrucción switch-statement prueba un valor con un conjunto de constantes. Las constantes de caso
deben ser distintas, y si el valor probado no coincide con ninguna de ellas, se elige el valor
predeterminado. Si no se proporciona ningún valor predeterminado, no se realiza ninguna acción si el
valor no coincide con ninguna constante de caso.
Pocos programas se escriben sin bucles. Por ejemplo, nos gustaría dar a conocer al usuario
algunos intentos de producir una entrada aceptable:
bool accept3()
{ int tries = 1; while (tries<4) { cout << "¿Quieres proceder (y o n)?\n";
writequestion char answer = 0;
cin >> respuesta; // leer respuesta

switch (respuesta)
{ caso 'y': return
true;
caso 'n': devolver
falso;
predeterminado:
cout << "Lo siento, no lo entiendo.\n";
++intenta; // incremento
}
}
cout << "Tomaré eso por un no.\n";
devolver false;
}

La instrucción while-se ejecuta hasta que su condición se vuelve falsa.


45
2.2.5 Punteros, matrices y bucles
Una matriz de elementos de tipo char se puede declarar así: char
v[6]; // matriz de6caracteres

Del mismo modo, un puntero se puede declarar

así: char∗ p; // puntero al carácter

En las declaraciones, [] significa ''matriz de'' y ∗ significa ''puntero a''. Todas las matrices tienen 0
como sus punteros, matrices y bucles inferiores de la sección 2.2.5

enlazado, por lo que v tiene seis elementos, v[0] a v[5]. El tamaño de una matriz debe ser una
expresión constante (§2.2.3). Una variable de puntero puede contener la dirección de un objeto del
tipo apropiado:
char∗ p = &v[3]; // ppoints to v'sfour thelement char x = ∗p; //
*p es el objeto al que p apunta

En una expresión, prefijo unario ∗ significa ''contenido de'' y prefijo unario y significa ''dirección
de''. Podemos representar gráficamente el resultado de esa definición inicializada:
p:

0: 1: 2: 3: 4: 5:
v:

Considere la posibilidad de copiar diez elementos de una matriz a otra:


copy_fct vacío()
{ int v1[10] = {0,1,2,3,4,5,6,7,8,9};
int v2[10]; // para convertirse en un copyofv1

para (auto i=0; i!=10; ++i) // copyelements


v2[i]=v1[i];
// ...
}

Esto para-statement se puede leer como ''set i to zero; while i is not 10, copieel elemento iésimo e
incremente i.'' Cuando se aplica a una variable entera, el operador de incremento, ++, simplemente
suma 1. C++ también ofrece una instrucciónmás simple para-, llamada rango-para-instrucción, para
bucles que atraviesan una secuencia de la manera más simple:
void print()
{ int v[] = {0,1,2,3,4,5,6,7,8,9};

for (auto x : v) // foreach x in v


cout << x << '\n';
46 Un recorrido por C++: lo básico Capítulo 2
para (auto x : {10,21,32,43,54,65}) cout
<< x << '\n';
// ...
}

El primerrango- para-declaración se puede leer como ''para cada elemento de v, desde el primero hasta
elúltimo, coloque una copia en x e imprímala''. Tenga en cuenta que no tenemos que especificar una
matriz enlazada cuando lanitializamos con una lista. La instrucción range-for-se puede utilizar para
cualquier secuencia de elementos (§3.4.1).
Si no quisiéramos copiar los valores de v en la variable x, sinosimplemente hacer que x se refiera
a un elemento, podríamos escribir:
void increment()
{ int v[] = {0,1,2,3,4,5,6,7,8,9};

para (auto& x : v)
++x;
// ...
}

En una declaración, el sufijo unario & significa ''referencia a.'' Una referencia es similar a un puntero,
excepto que no es necesario usar un prefijo ∗ para acceder al valor al que hace referencia la referencia.
Además, no se puede hacer una referencia para referirse a un objeto diferente después de su
inicialización. Cuando se utilizan en declaraciones, los operadores (como &, ∗y []) sedenominan
operadores declaradores:
T a[n]; T[n]: matriz ofnTs(§7.3)
T∗ p; T*: puntero a T (§7.2)
T&r; T&: referencia a T (§7.7)
T f(A); T(A): función que toma un argumento de tipo A que devuelve un resultado del tipo T
(§2.2.1)
Intentamos asegurarnos de que un puntero siempre apunte a un objeto, para que desreferenciarlo sea
válido. Cuando no tenemos un objeto al que apuntar o si necesitamos representar el notion de ''ningún
objeto disponible'' (por ejemplo, para un final de una lista), le damos al puntero el valor nullptr (''el
puntero nulo''). Solo hay un nullptr compartido por todos los tipos de puntero:
doble∗ pd = nullptr;
Enlace<Record>∗ lst = nullptr; // puntero a un enlace a un registro int
x = nullptr; // error :nullptr es un puntero, no un entero

A menudo es aconsejable verificar que un argumento de puntero que se supone que apunta a algo, en
realidad apunta a algo:
int count_x(char∗ p, char x)
contar el número de ocurrencias de x en p[]
pisassumed para señalar a un cero-ter minatedarray ofchar (o a nada)
{ if (p==nullptr) devuelve 0;
int count = 0; for (;
∗p!=0; ++p) if (∗p==x)
++contar;
recuento de devoluciones;
}
47
Nótese cómo podemos mover un puntero para apuntar al siguiente elemento de una matriz usando
++ y que podemos dejar fuera el inicializador en una instrucción for-si no lo necesitamos.
La definición de count_x() supone que el char∗ es una cadena de estilo C,es decir, que el puntero
apunta a una matriz de charcon terminacióncero.
En el código más antiguo, normalmente se usa 0 o NULL en lugar de nullptr (§7.2.2). Sin
embargo, el uso de nullptr elimina la confusión potencial entre enteros (como 0 o NULL)y punteros
(como nullptr).
Sección 2.3 Tipos definidos por el usuario

2.3 Tipos definidos por el usuario


Llamamos a los tipos que se pueden construir a partir de los tipos fundamentales (§2.2.2), el
modificador const (§2.2.3) y los operadores declaradores (§2.2.5) tipos incorporados. El conjunto
de tipos y operaciones integrados de C++ es rico, pero deliberadamente de bajo nivel. Reflejan directa
y eficientemente las capacidades del hardware informático convencional. Sin embargo, no
proporcionan al programador instalaciones de alto nivel para escribir convenientemente aplicaciones
avanzadas. En cambio, C ++ aumenta los tipos y operaciones integrados con un sofisticado conjunto
de mecanismos de abstracción a partir de los cuales los programadores pueden construir
instalaciones de tan alto nivel. Los mecanismos de abstracción de C++ están diseñados
principalmente para permitir a los programadores diseñar e implementar sus propios tipos, con
representaciones y operaciones adecuadas, y para que los programadores utilicen de manera simple
y elegante dichos tipos. Los tipos construidos a partir de los tipos incorporados using los mecanismos
de abstracción de C++ se denominan tipos definidos por el usuario. Se denominan clases y
enumeraciones. La mayor parte de este libro está dedicado al diseño, implementación y uso de tipos
definidos por el usuario. El resto de este capítulo presenta las facilidades más simples y
fundamentalespara eso. El capítulo 3 es una descripción más completa de los mecanismos de
abstracción y los estilos de programación que admiten. Los capítulos 4 y 5 presentan una visión
general de la biblioteca estándar, y dado que la biblioteca estándar presenta principalmentec onsistas
de tipos definidos por el usuario, proporcionan ejemplos de lo que se puede construir utilizando las
instalaciones del lenguaje y las técnicas de programación presentadas en los capítulos 2 y 3.

2.3.1 Estructuras
El primer paso en la construcción de un nuevo tipo es a menudo organizar loselementos que necesita
en una estructura de datos, una estructura:
struct Vector {
int sz; // número de elementos
doble∗ elem; // puntero a los elementos
};

Esta primera versión de Vector consiste en un int y un doble∗.


Una variable de tipo Vector se puede definir así:
Vector v;
48 Un recorrido por C++: lo básico Capítulo 2
Sin embargo, por sí solo eso no es de mucha utilidad porque el puntero elem de v no apunta a nada.
Para ser útiles, debemos dar v algunos elementos a los que apuntar. Por ejemplo, podemos construir
un vector como este:
void vector_init(Vector& v, int s)
{
v.elem = nuevo doble[s]; asignar una matriz dedosdos v.sz = s;
}

Es decir, el miembro elem de v obtiene un puntero producido por el nuevo operador y el miembro de
tamaño de vobtiene el número de elementos. El & en Vector& indica que pasamos v por referencia
noconst (§2.2.5, §7.7); de esa manera, vector_init() puede modificar el vector que se le pasa.
El nuevo operador asigna memoria desde un área llamada almacén libre (unlso conocido como
memoria dinámica y montón;§11.2).
Un simple uso de Vector se ve así:
doble read_and_sum(int s)
leer s enteros de cin y devolver su suma; s se supone que es positivo
{
Vector v;

vector_init (v,s); para asignar elementos S para V


(int i=0; i!=s; ++i)
cin>>v.elem[i]; leer en elementos

suma doble = 0;
para (int i=0; i!=s; ++i)
suma+=v.elem[i]; suma tomela suma de los elementos
de retorno;
}

Hay un largo camino por recorrer antes de que nuestro Vector sea tan elegante y flexible como el vector
de biblioteca estándar. En particular, un usuario de Vector tiene que conocer cada detalle de la
representación de Vector. El resto de este capítulo y el siguiente mejoran gradualmente Vector como
ejemplo de características y técnicas del lenguaje. El Capítulo 4 presenta el vectorde
bibliotecaestándar, que contiene muchas mejoras agradables, y el Capítulo 31 presenta el vector
completo en el contexto de otras instalaciones de biblioteca estándar.
Utilizo vector y otros componentes de standard-library como ejemplos
• para ilustrar las características del lenguaje y las técnicas de diseño, y
• para ayudarle a aprender y utilizar los componentes de la biblioteca estándar.
No reinvente los componentes de la biblioteca estándar, como el vector y la cadena;úselos.
Utilizamos . (punto) paraacess struct members a través de un nombre (y a través de una referencia)
y −> para acceder a struct members a través de un puntero. Por ejemplo:
void f(Vector v, Vector& rv, Vector∗ pv)
{

int i1 = v.sz; acceso a través del nombre


int i2 = rv.sz; acceso a través de referencia
49
int i4 = pv−>sz; } acceso a través del puntero

2.3.2 Clases
Tener los datos especificados por separado de las operaciones en él tiene ventajas, como la capacidad
de usar los datos de manera arbitraria. Sin embargo, se necesita una conexión más estrecha entre la
representación y las operaciones para que un tipo definido por el usuario tenga todas las propiedades
esperadas de un "tipo real". En particular, a menudo queremos mantener la representación inaccesible
para los usuarios, a fin de facilitar el uso, garantizar un uso coherente de los datos y permitirnos
mejorar posteriormente la representación. Para ello tenemos que distinguirentre la interfaz a un tipo
(para ser utilizado por todos) y su implementación (que tiene acceso a los datos que de otro modo
serían inaccesibles). El mecanismo de lenguaje para eso se llama clase. Una clase se define para
tener un conjunto de miembros,que pueden ser miembros de datos,función o tipo. La interfaz es
definida por los miembros públicos de una clase, y los miembros privados son accesibles sólo a
través de esa interfaz. Por ejemplo:
Sección 2.3.2 Clases

class Vector { public:


Vector(int s) :elem{new double[s]}, sz{s} { } // constr uctaVector double&
operator[](int i) { return elem[i]; } // element access: subscripting int size() {
return sz; }
privado:
doble∗ elem; // puntero a los elementos
int sz; // el número de elementos
};

Dado que, podemos definir una variable de nuestro nuevo tipo Vector:
Vector v(6); // aVector con 6 elementos

Podemos ilustrar gráficamente un objeto Vector:

0: 1: 2: 3: 4: 5:
6
Vector:
Elem:
sz:

Básicamente, el objeto Vector es un ''handle'' que contiene un puntero a los elementos (elem) más el
número de elementos (sz). El número de elementos (6 en el ejemplo) puede variar de un objeto
Vector a otro, y un objeto Vector puede tener un número diferente de elementos en diferentesmomentos
(§3.2.1.3). Sin embargo, el objeto Vector en sí es siempre del mismo tamaño. Esta es la técnica básica
para manejar cantidades variables de información en C++: un identificador de tamaño fijo que se
50 Un recorrido por C++: lo básico Capítulo 2
refiere a una cantidad variable de datos "en otro lugar" (por ejemplo, en el tore libreasignado por el
nuevo; §11.2). Cómo diseñar y usar tales objetos es el tema principal del Capítulo 3.
Aquí, la representación de un Vector (los miembros elem y sz)es accesible solo através de la
interfaz proporcionada por los miembros públicos: Vector(), operator[](), y siz e() . El ejemplo
read_and_sum() del §2.3.1 se simplifica a:
doble read_and_sum(int s)
{
Vector v(s); makeavector de s elementos
para (int i=0; i!=v.siz e(); ++i)
cin>>v[i]; leer en elementos

suma doble = 0;
para (int i=0; i!=v.siz e(); ++i)
suma+=v[i]; suma tomela suma de los elementos
de retorno;
}

Una ''función'' con el mismo nombre que su clase se llama constructor,es decir, una función utilizada
para construir objetos de una clase. Por lo tanto, el constructor, Vector(), reemplaza vector_init() de
§2.3.1. A diferencia de una función ordinaria, se garantiza que un constructor se utilizará para
inicializar objetos de su clase. Por lo tanto, definir un constructor elimina el problema de las variables
no inicializadas para una clase.
Vector(int) define cómo se construyen los objetos de tipo Vector. En particular, afirma que necesita un
entero para hacer eso. Ese entero se usa como el número de elementos. El constructor inicializa los
miembros Vector mediante una lista de inicializadores de miembros:
:elem{new double[s]}, sz{s}

Es decir, primero initializamos elem con un puntero a s elementos de tipo doble obtenidos de la
tienda libre. Luego, inicializamos sz a s.
El acceso a los elementos es proporcionado por una función de subíndice, llamada operator[].
Devuelve una referencia al elemento apropiado (un doble&).
La función size() se suministra para dar a los usuarios el número de elementos.
Obviamente, el manejo de errores falta por completo, pero volveremos a eso en §2.4.3. Del mismo
modo, no proporcionamos un mecanismo para "devolver" la matriz de dobles s adquiridas por los
nuevos;§3.2.1.2 muestra cómo usar un destructor para hacer eso elegantemente.

2.3.3 Enumeraciones
Además de las clases, C++ admite una forma simple de tipo definido por el usuario para el que
podemos enumerar los valores:
clase enum Color { rojo, azul, verde }; enum clase
Traffic_light { green, amarillo, rojo };

Color col = Color::rojo;


luz Traffic_light = Traffic_light::rojo;
51
Tenga en cuenta que los enumeradores (por ejemplo, rojo)están en el ámbito de su clase enum,por lo
que sepueden usar repetidamente en diferentes clases de enumeraciónes sin confusión. Por ejemplo,
Color::red es el rojo de Color, que es diferente de Traffic_light::red.
Las enumeraciones se utilizan para representar pequeños conjuntos de valores enteros. Se utilizan
para hacer que el código sea más legible y menos propenso a errores de lo que habría sido si no se
hubieran utilizado los nombres simbólicos (y mnemotécnicos)de enumeración.
La clase después de la enumeración especifica que una enumeración está fuertemente tipada y que
sus enumeradores tienen un ámbito. Al ser tipos separados, la clase enumayuda a prevenir el mal uso
accidental deconstantes. En particular, no podemos mezclar Traffic_ligvalores deht y Color:
Color x = rojo; // error :¿qué rojo?
Color y = Traffic_light::rojo; error :que el rojo no es un color Color z=
Color::rojo; // OK

Del mismo modo, no podemos mezclar implícitamente los valores color e enteros:
int i = Color::rojo; // error :Color ::redis not an int
Color c = 2; // error :2isnot a Color

Si no desea calificar explícitamente los nombres de enumerador y desea que los valores de
enumerator sean ints (sin necesidad de una conversión explícita), puede quitar la clase de la clase enum
para obtener una ''enumeración simple '' (§8.4.2).
De forma predeterminada, una clase enum solo tiene definidas la asignación, la inicialización y las
comparaciones (por ejemplo, == y <;§2.2.2). Sin embargo, una enumeración es un tipo definido por
el usuario, por lo que podemos definir operadores para ella:
Sección 2.3.3 Enumeraciones

Traffic_light& operador++(Traffic_light&t) //
incremento del prefijo: ++
{ interruptor (t) {

caso Traffic_light::verde: return t=Traffic_light::amarillo;


caso Traffic_light::amarillo: return t=Traffic_light::red;
caso Traffic_light::rojo: return t=Traffic_light::green;
}
}
siguiente se convierte en
Traffic_light siguiente = ++luz; Traffic_light::verde
C++ también ofrece una enum "simple" con un tipo menos fuerte (§8.4.2).

2.4 Modularidad
Un programa de C++ consta de muchas partes desarrolladas por separado, como funciones (§2.2.1,
Capítulo 12), tipos definidos por el usuario (§2.3, §3.2, Capítulo 16), jerarquías de clases (§3.2.4,
Capítulo 20) y plantillas (§3.4, Capítulo 23). La clave para gestionar esto es definir claramente las
interacciones entre esas partes. El primer y más importante paso es distinguir entre la interfaz de una
52 Un recorrido por C++: lo básico Capítulo 2
pieza y su implementación. A nivel de lenguaje, C++ representa interfaces por declaraciones. Una
declaración especifica todo lo que es needed para usar una función o un tipo. Por ejemplo:
doble sqrt (doble); // la función de raíz cuadrada toma un doble y devuelve un doble

class Vector { public:


Vector(int s); double&
operator[](int i);
int size();
privado: doble∗ elem; // elem apunta a una matriz dezdoubles int
sz;
};

El punto clave aquí es que los cuerpos de funciones, las definicionesde funciones, están "en otro
lugar". Para este ejemplo, nos gustaría que la representación de Vector fuera ''en otro lugar'' también,
pero trataremos con that más adelante(tipos abstractos; §3.2.2). La definición de sqrt() se verá así:
doble sqrt(doble d) // definición de sqrt()
{
// ... algoritmo como se encuentra en el libro de
texto de matemáticas ... }

Para Vector,necesitamos definir las tres funciones miembro:


Vector::Vector(int s) // definición del constructor
:elem{new double[s]}, sz{s} // initializemembers
{
}
double& Vector::operator[](int i) // definición de subíndice
{ return elem[i];
}

int Vector::siz e() // definición de tamaño()


{ volver sz;
}

Debemos definirlas funciones de Vector, pero no sqrt() porque forma parte de la biblioteca estándar.
Howev er, eso no hace ninguna diferencia real: una biblioteca es simplemente un "otro código que
usamos" escrito con las mismas facilidades de lenguaje que usamos.

2.4.1Compilación separ ate


C++ admite una noción de compilación independiente en la que el código de usuario solo ve las
declaraciones de los tipos y funciones utilizados. Las definiciones de esos tipos y funciones se
encuentran en archivos de origen separados y se compilan por separado. Esto se puede utilizar para
organizar un programa en un conjunto de fragmentos de código semi-independientes. Dicha
separación se puede utilizar para minimizar los tiempos de compilación y para hacer cumplir
estrictamente la separación de partes lógicamente distintas de un programa (minimizando así la
posibilidad de errores). Una biblioteca es a menudo una compilación separadade fragmentos de
código d (por ejemplo, funciones).
53
Normalmente, colocamos las declaraciones que especifican la interfaz de un módulo en un
archivo con un nombre que indica su uso previsto. Por ejemplo:
Vector.h:

class Vector { public:


Vector(int s); double&
operator[](int i);
entamaño t();
privado: doble∗ elem; // elem apunta a una matriz dezdoubles int sz;
};

Esta declaración se colocaría en un archivo Vector.h,y los usuarios incluirán ese archivo, llamado
archivo de encabezado,para acceder a esainterfaz. Por ejemplo:
usuario.cpp:

#include "Vector.h" // obtener Vector'sinterface


#include <cmath>// obtener la interfaz de la función ymath de librar estándar, incluido sqrt() utilizando el
espacio de nombres std; // hacer visibles los miembros de la biblioteca (§2.4.2)
Sección 2.4.1 Compilación
separada

doble sqrt_sum(Vector& v)
{
suma doble = 0;
para (int i=0; i!=v.siz e(); ++i)
suma+=sqrt(v[i]); suma de raíces cuadradas
suma de retorno;
}
Para ayudar al compilador a garantizar la coherencia, el archivo .cpp que proporciona la
implementación de Vector también incluirá el archivo .h que proporciona su interfaz:
Vector.cpp:

#include "Vector.h" // obtener la interfaz

Vector::Vector(int s)
:elem{new double[s]}, sz{s}
{
}

double& Vector::operator[](int i)
{ return elem[i];
}

int Vector::siz e()


{ volver sz;
}
54 Un recorrido por C++: lo básico Capítulo 2
El código en user.cpp y Vector.cpp comparte la información de la interfaz Vector presentada en
Vector.h,pero los dos archivos son independientes y se pueden compilar por separado. Gráficamente,
los fragmentos del programa se pueden representar así:
Vector.h :

Vector interfaz

:
usuario.cpp Vector.cpp :
#include "Vector.h" #include "Vector.h"
uso Vector definirVector

Estrictamente hablando, el uso de compilaciones separadas no es un problema de idioma; es una


cuestión de la mejor manera de aprovechar una implementación de lenguaje en particular. Sin
embargo, es de gran importancia práctica. El mejor enfoque es maximizar la modularidad, representar
esa modularidad lógicamente a través de las características del lenguaje y luego explotar la
modularidad físicamente a través de archivos para una compilación separada efectiva (Capítulo 14,
Capítulo 15).
2.4.2 Espacios de nombres
Además de las funciones (§2.2.1, Capítulo 12), las clases (Capítulo 16)y las enumeraciones (§2.3.3,
§8.4), C++ ofrece espacios de nombres (Capítulo 14) como un mecanismo para expresar que algunas
declaraciones pertenecen juntas y que sus nombres no deben chocar con otros nombres. Por ejemplo,
es posible que desee experimentar con mi propiotipo de tecnología compleja(§3.2.1.1, §18.3, §40.4):
namespace My_code { class
complex { /* ... */ }; sqr
complejo t (complejo);
// ...
int main();
}

int My_code::main()
{
complejo z {1,2};
auto z2 = sqrt(z);
std::cout << '{' << z2.real() << ',' << z2.imag() << "}\n";
// ...
};

int main()
{
volver My_code::main();
}

Al poner mi código en el espacio denombres My_code, me aseguro de que mis nombres no entren en
conflicto con los nombres de biblioteca estándar en el espacio de nombres std (§4.1.2). La precaución
55
es prudente, porque la biblioteca estándar proporciona soporte para aritmética compleja (§3.2.1.1,
§40.4).
La forma más sencilla de acceder a un nombre en otro espacio de nombres es calificarlo con el
nombre del espacio de nombres (por ejemplo, std::cout y My_code::main). El ''real main()'' se define en
el espacio de nombres global, es decir, no local a un espacio de nombres, clase o función definida.
Para obtener acceso a los nombres en el espacio de nombres de la biblioteca estándar, podemos usar
una directiva using-(§14.2.3):
usar el espacio de nombres std;

Los espacios de nombres se utilizan principalmente para organizar componentes de programas más
grandes, como bibliotecas. Simplifican la composición de un programa a partir de partes
desarrolladas por separado.

2.4.3 Manejo de errores


El manejo de errores es un tema grande y complejo con preocupaciones yramificaciones que van
mucho más allá de las instalaciones del lenguaje en técnicas y herramientas de programación. Sin
embargo, C++ proporciona algunas características para ayudar. La herramienta principal es el sistema
de tipos en sí. En lugar de construir minuciosamente nuestras aplicaciones a partir de los tipos
incorporados (porejemplo, char, int y double)y las instrucciones (por ejemplo, if, while y for),
construimos más tipos que son apropiados para nuestras aplicaciones (por ejemplo, string, mapy
regex)yalgoritmos (por ejemplo, sort(), find_if()y draw_all()). Tales construcciones de nivel superior
simplifican nuestra programación, limitan nuestras oportunidades de errores (por ejemplo, es poco
probable que intente aplicar un recorrido de árbol a un cuadro de diálogo), Sección 2.4.3
Manejo de errores

y aumentar las posibilidades del compilador de atrapar tales errors. La mayoría de las construcciones
de C++ están dedicadas al diseño e implementación de abstracciones elegantes y eficientes (por
ejemplo, tipos definidos por el usuario y algoritmos que los utilizan). Un efecto de esta modularidad
y abstracción (en particular, el uso de bibliotecas)es que el punto donde se puede detectar un error en
tiempo de ejecución está separado del punto donde se puede manejar. A medida que los programas
crecen, y especialmente cuando las bibliotecas se utilizan ampliamente, los estándares para el manejo
de errores se vuelven importantes.

2.4.3.1 Excepciones
Consider de nuevo el ejemplo vector. ¿Qué se debe hacer cuando intentamos acceder a un elemento
que está fuera del rango para el vector de §2.3.2?
• El escritor de Vector no sabe lo que el usuario le gustaría haber hecho en este caso (el escritor
de Vector normalmente nisiquiera sabe en qué programa se ejecutará el vector).
• El usuario de Vector no puede detectar consistentemente el problema (si el usuario pudiera, el
acceso fuera de rango no ocurriría en primer lugar).
La solución es que el implementador de Vector detecte elacceso fuera de rango tentado yluego le
informe al usuario al respecto. A continuación, el usuario puede tomar las medidas adecuadas. Por
56 Un recorrido por C++: lo básico Capítulo 2
ejemplo, Vector::operator[]() puede detectar un intento de acceso fuera de rango y lanzar una excepción
out_of_range:
double& Vector::operator[](int i)
{ if (i<0 || size()<=i) throw out_of_rang e{"Vector::operator[]"}; return
elem[i];
}

El lanzamiento transfiere el control a un controlador para excepciones de tipo out_of_range en alguna


función que directa o indirectamente llama Vector::operator[](). Para hacer eso, la implementación
desenrollará la pila de llamadas a la función según sea necesario para volver al contexto de esa
persona que llama (§13.5.1). Por ejemplo:
void f(Vector& v)
{
// ...
try { // Exceptions here son manejados por el controlador definido a continuación

v[v.siz e()] = 7; tr yto acceso más allá del final de v


}
catch (out_of_rang e) { // oops: error de out_of_range
// ... error de rango de manejo ...
}
// ...
}

Ponemos el código para el que estamos interesados en manejar excepciones en un try-block. Ese
intento de asignación a v[v.siz e()] fracasará. Por lo tanto, se introducirá la cláusula catch-clause que
proporciona un manipulador para out_of_range. El tipo de out_of_range se define en la biblioteca
estándar y, de hecho, es utilizado por algunas funciones de acceso a contenedores de biblioteca
estándar.
El uso de los mecanismos de control de excepciones puede hacer que el manejo de errores sea
más simple, más sistemático y más legible. Consulte el Capítulo 13 para obtener más información,
detalles yexámenes.
2.4.3.2 Invariantes
El uso de excepciones para señalar el acceso fuera del rango es un ejemplo de una función que
comprueba su argumento y se niega a actuar porque una suposición básica, una condición previa,no
se sostuvo. Si hubiéramos especificado formalmente el subíndice operator de Vector,habríamos dicho
algo así como ''el índice debe estar en elrango [ 0:size())'', y eso fue de hecho lo que probamos en
nuestro operador [](). Siempre que definamos una función, debemos considerar cuáles son sus
condiciones previas y, si es factible, probarlas (ver §12.4, §13.4).
Sin embargo, operator[]() opera sobre objetos de tipo Vector y nada de lo que hace tiene sentido a
menos que los miembros de Vector tengan valores ''razonables''. En particular, dijimos que "elem
apunta a una serie de dobles sz", pero solo lo dijimos en uncomunicado. Tal declaración de lo que se
supone que es cierto para una clase se llama invariante de clase,o simplemente un invariante. Es el
trabajo de un constructor establecer el invariante para su clase (de modo que las funciones miembro
puedan confiar en él) y para el miembro functions asegurarse de que el invariante se mantenga cuando
salgan. Desafortunadamente, nuestro constructor Vector solo hizo su trabajo parcialmente. Inicializó
57
correctamente los miembros de Vector, pero no pudo comprobar que los argumentos que se le pasaron
tenían sentido. Considere:
Vector v(−27);

Es probable que esto cause caos.


Aquí hay una definición más apropiada:
Vector::Vector(int s)
{ if (s<0) throw length_error{}; elem
= new double[s];
sz = s;
}

Utilizo la excepción de biblioteca estándar length_error para informar de un número no positivo de


elementos porque algunas operaciones de biblioteca estándar utilizan esa excepción para informar de
problemas de este tipo. Si el operador new no puede encontrar memoria para asignar, arroja un
std::bad_alloc. Ahora podemos escribir:
prueba de vacío()
{ probar {
Vector v(−27);
}
captura (std::length_error) {
manejar el tamaño negativo
}
captura (std::bad_alloc) {
manejar el agotamiento de la memoria
}
}

Puede definir sus propias clases para que se usen como excepciones y hacer que lleven información
arbitraria desde un punto en el que se detecta un error hasta un punto en el que se puede controlar
(§13.5).
A menudo, una función no tiene forma de completar su tarea asignada después de que se lanza
una excepción. Entonces, "manejar" una excepción simplemente significa hacer una limpieza local
mínima y volver a presentar la excepción (§13.5.2.1).
Sección 2.4.3.2 Invariantes

La noción de invariants es fundamental para el diseño de clases, y las condiciones previas


desempeñan un papel similar en el diseño de funciones. Invariantes
• nos ayuda a entender exactamente lo que queremos
• nos obliga a ser específicos; que nos da una mejor oportunidad de obtener nuestro código
correcto (after depuración y pruebas).
La noción de invariantes subyace a las nociones de C++ de gestión de recursos apoyadas por
constructores (§2.3.2) y destructores (§3.2.1.2, §5.2). Véanse también §13.4, §16.3.1 y §17.2.
58 Un recorrido por C++: lo básico Capítulo 2
2.4.3.3 Aserciones estáticas
Las excepciones informan de errores encontrados en tiempo de ejecución. Si se puede encontrar un
error en tiempo de compilación, generalmente es preferible hacerlo. Para eso está gran parte del
sistema de tipos y las facilidades para especificar las interfaces a los tipos definidos por el usuario.
Howev er, también podemos realizar comprobaciones simples en otras propiedades que se conocen
en tiempo de compilación e informar de errores como mensajes de error del compilador. Por ejemplo:
static_assert(4<=sizeof(int), "los enteros son demasiado pequeños"); // checkinteger size

Esto escribirá que los enteros son demasiado pequeños si 4<=sizeof(int) no se mantiene, es decir, si un int
en este sistema no tiene al menos 4 bytes. Llamamos a tales declaraciones de expectativas
afirmaciones.
El mecanismo static_assert se puede utilizar para cualquier cosa que pueda expresarse en términos
de expresiones constantes (§2.2.3, §10.4). Para example:
constexpr doble C = 299792.458; km/s

void f (doble velocidad)


{
const doble local_max = 160.0/(60∗60); 160 km/h == 160.0/(60*60) km/s

error : la velocidad debe ser una


static_assert (velocidad<C, "no puede ir tan rápido"); constante
static_assert (local_max<C, "no puede ir tan rápido"); Aceptar
// ...
}

En general, static_assert(A,S) imprime S como un mensaje de error del compilador si A no es true.


Los usos más importantes de static_assert vienen cuando hacemos afirmaciones sobre los tipos
utilizados como parámetros en la programación genérica (§5.4.2, §24.3).
Para las aserciones comprobadas en tiempo de ejecución, consulte §13.4.

2.5 Posdata
Los temas tratados en este capítulo corresponden aproximadamente al contenido de la Parte II
(Capítulos 6-15). Esas son las partes de C ++ que subyacen a todas las técnicas y estilos de
programación compatibles con C ++. Programadores experimentados de C y C++, tenga en cuenta
que esta base no se corresponde estrechamente con los subconjuntos de C o C++98 de C++ (es decir,
C++11).
2.6 Consejos
[1] ¡Nocunda el pánico! Todo se aclarará con el tiempo; §2.1.
[2] No tienes que conocer cada detalle de C++ para escribir buenos programas; §1.3.1.
[3] Centrarse en las técnicas de programación, no en las características del lenguaje; §2.1.

También podría gustarte