Alcides Iván Meza - 200 Ejercicios de Visual C++ 2019

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 365

200 ejercicios en Visual c++ 2019

Copyright © 2020 Alcides Iván Meza

Todos los derechos reservados. El usuario de esta publicación podrá utilizar el material en la forma en la
que expresamente sea referida por el autor, incluyendo los usos gratuitos o libres, los usos comerciales
y otros tipos de usos.
La obra está dirigida a apoyar la práctica de aprendizaje del lenguaje c++ en su versión más actualizada,
y con el mayor detalle disponible, para que los estudiantes tomen como punto de partida una versión
probada, la modifiquen según sus propias ideas y experimenten.
Todos los programas presentados han sido confirmados previamente en una versión expresa del Visual
c++ 2019. La versión que se edita de cada programa proviene exactamente de la comprobación en el
Visual c++.
La información que se expone en esta obra no contiene ninguna garantía de que igual funcionará en el
compilador particular que utilice el usuario. Ni el autor, ni el editor, ofrece compensación por perjuicios
de cualquier tipo que ocurran por la utilización de estos programas. El material se ofrece estrictamente
como ejemplo de ejercicios de programación que se pueden trabajar en la plataforma del Visual c++ en
su última versión.
CONTENIDO
Pág.
IH01.cpp: Estructura anterior de las versiones del c++ …………………………………………………………………… 1
IH02.cpp: Programa a partir de un proyecto vacío. …………………………………………………………………………… 1
IH03.cpp: Salida de una corriente de caracteres a la consola. …………………………………………………………… 2
IH04.cpp: Este archivo inicia la práctica de salida de corrientes de datos a la consola. …………………… 2
IH05.cpp: Usar por primera vez el manipulador de salida de caracteres "setw(n)". ………………………… 3
IH06.cpp: Usar por primera vez el manipulador de salida de caracteres "setw(n)" (2). ……………………… 4
IH07.cpp: Usando la secuencia de escape: \character. \ es el backslash character. …………………………… 5
IH08.cpp: The assignment statement. La instrucción de asignación. ………………………………………………… 5
IH09.cpp: Binary operations. Unary operations. Operaciones binarias. Operaciones unarias. ………… 6
IH10.cpp: Calculating a remainder. Calculando el residuo. ……………………………………………………………… 6
IH11.cpp: Type conversion and casting. Conversión de tipos de datos y simulación de tipos. ………… 7
IH12.cpp: Conversión de tipos de datos en las asignaciones. …………………………………………………………… 7
IH13.cpp: Forzar la conversión del tipo de datos mediante el intérprete "cast". ……………………………… 8
IH14.cpp: The "auto" keyword. La palabra clave "auto". …………………………………………………………………… 9
IH15.cpp: The "auto" keyword. La palabra clave "auto". The "typeid" operator. El operador
"typeid". …………………………………………………………………………………………………………………………… 10
IH16.cpp: Bitwise operators. Operadores de bits. ……………………………………………………………………………… 11
IH17.cpp: Operaciones con bits. Uso de máscaras de bits ………………………………………………………………… 15
IH18.cpp: Bitwise operator OR, |. Operador de bits OR. ………………………………………………………………… 16
IH19.cpp: Bitwise operator XOR, ^. Operador de bits XOR, exclusive OR. …………………………………… 18
IH20.cpp: XOR - ^. Exclusive OR. OR exclusivo. …………………………………………………………………………… 19
IH21.cpp: The bitwise NOT ~. El operador de bits NOT ~. …………………………………………………………… 20
IH22.cpp: Bitwise shift operators. Operadores de desplazamiento de bits. ……………………………………… 20
IH23.cpp: Lvalues and Rvalues. Valores a la izquierda y valores a la derecha. ………………………………… 22
IH24.cpp: Todas las variables tienen un tiempo de vida finito. ………………………………………………………… 23
IH25.cpp: Scope resolution operator. Operador de resolución de alcance. :: ……………………………………… 24
IH26.cpp: Static variables. Variables estáticas. ………………………………………………………………………………… 25
IH27.cpp: Enumerations. Enumeraciones. ………………………………………………………………………………………… 26
IH28.cpp: Nuevo procedimiento para las enumeraciones. ………………………………………………………………… 27
IH29.cpp: Creating your own namespaces. La creación de sus propios nombres de dominios. ………… 28
IH30.cpp: The if statement. La sentencia if. ……………………………………………………………………………………… 29
IH31.cpp: The if statement. La sentencia condicional if. …………………………………………………………………… 29
IH32.cpp: The nested if. La sentencia anidada if. ……………………………………………………………………………… 30
IH33.cpp: Convert to lowercase. Convertir a letra minúscula. ………………………………………………………… 31
IH34.cpp: The extended if statement. La sentencia if extendida. If, else. ………………………………………… 31
IH35.cpp: The nested if-else. La sentencia anidada if-else. ………………………………………………………………… 32
IH36.cpp: The logical operators. Los operadores lógicos. AND, OR, NOT. …………………………………… 33
IH37.cpp: The conditional operator. El operador condicional. ………………………………………………………… 35
IH38.cpp: The switch statement. La instrucción switch. switch-case-break-default. ……………………… 36
IH39.cpp: Sharing the cases. Compartir las sentencias case. …………………………………………………………… 38
IH40.cpp: Unconditional branching. La sentencia de redireccionamiento incondicional. Goto. ……… 39
IH41.cpp: Loop with if and goto. Los ciclos con las sentencias if y goto. ………………………………………… 40
IH42.cpp: The for loop. El ciclo for. …………………………………………………………………………………………………… 41
IH43.cpp: Using multiple counters. El uso de contadores múltiples. For loop. Ciclo for. ………………… 42
IH44.cpp: The indefinite for loop. El ciclo for infinito. ……………………………………………………………………… 43
IH45.cpp: The continue statement. La sentencia continue. ……………………………………………………………… 44
IH46.cpp: Using other types in loops. El uso de otros tipos de datos en los ciclos. ………………………… 45
IH47.cpp: Floating point loop counters. Contadores de ciclo de punto flotante. ……………………………… 46
IH48.cpp: The while statement. La cláusula while. …………………………………………………………………………… 46
IH49.cpp: The while(true) loop. El ciclo while(true). ………………………………………………………………………… 47
IH50.cpp: The do-while loop. El ciclo do-while. ……………………………………………………………………………… 48
IH51.cpp: The range-based for loop. El ciclo for basado en rango. …………………………………………………… 49
IH52.cpp: Nested loops. Ciclos anidados. ………………………………………………………………………………………… 49
IH53.cpp: Using nested loops to generate a multiplication table. El uso de los ciclos
anidados para generar una tabla de multiplicar. ……………………………………………………………… 51
IH54.cpp: Arrays. Arreglos o matrices. …………………………………………………………………………………………… 52
IH55.cpp: Initializing arrays. La inicialización de las matrices. ………………………………………………………… 53
IH56.cpp: Using the range-based for loop. El uso del ciclo for basado en el rango. (1) …………………… 55
IH57.cpp: Using the range-based for loop. El uso del ciclo for basado en el rango.(2)
Auto keyword .………………………………………………………………………………………………………………… 56
IH58.cpp: Multidimensional arrays. Matrices multidimensionales. ………………………………………………… 57
IH59.cpp: Multidimensional arrays. Matrices multidimensionales. cin.getline() …………………………… 58
IH60.cpp: Initializing multidimensional Arrays. La inicialización de las matrices
multidimensionales. ………………………………………………………………………………………………………… 58
IH61.cpp: Using multidimensional matrices. El uso de las matrices multidimensionales.
setiosflags(ios::fixed). setprecision(2). for (double prom : promedios) ………………………… 59
IH62.cpp: Declaring pointers. La declaración de punteros. The address-of operator, &.
El operador de dirección de, &. The reference operator. El operador de referencia.
The indirection operator, *. El operador de indirección, *.
The dereference operator, *. El operador de dereferencia, *.
De-referencing the pointer. La dereferenciación del puntero. ………………………………………… 60
IH63.cpp: Pointer operations. Operaciones con punteros. ………………………………………………………………… 61
IH64.cpp: Pointers of type const char*. Punteros del tipo const char*. …………………………………………… 62
IH65.cpp: Initializing pointers with strings. La inicialización de punteros con cadenas. ………………… 63
IH66.cpp: The sizeof operator. El operador sizeof. …………………………………………………………………………… 64
IH67.cpp: Constant pointers and pointers to constants. Punteros constantes ………………………………… 66
IH68.cpp: Pointers and arrays. Punteros y matrices. ……………………………………………………………………… 67
IH69.cpp: Pointer arithmetic. Aritmética de punteros. …………………………………………………………………… 68
IH70.cpp: Pointer arithmetic. Aritmética de punteros. Continuación. …………………………………………… 69
IH71.cpp: Pointers as array names. Punteros como nombres de matrices. ……………………………………… 70
IH72.cpp: Counting string characters using a pointer. Contar los caracteres de una cadena
usando un puntero. …………………………………………………………………………………………………………… 71
IH73.cpp: Using pointers with multidimensional arrays. El uso de punteros con matrices
multidimensionales. ………………………………………………………………………………………………………… 72
IH74.cpp: Dynamic memory allocation. Asignación dinámica de memoria. …………………………………… 74
IH75.cpp: Allocating memory for an array dynamically. La asignación de memoria para una
matriz en forma dinámica. ………………………………………………………………………………………………… 75
IH76.cpp: Smart pointers. Punteros inteligentes. …………………………………………………………………………… 76
IH77.cpp: Using free store. El uso de la tienda libre (memoria dinámica). Calculating primes
using dynamic memory allocation. Calcular números primos usando la memoria
dinámica. …………………………………………………………………………………………………………………………… 77
IH78.cpp: Dynamic allocation of multidimensional arrays. Asignación dinámica
de matrices multidimensionales. ……………………………………………………………………………………… 78
IH79.cpp: Using references. El uso de las referencias. ……………………………………………………………………… 80
IH80.cpp: Using references. El uso de las referencias. Punteros y referencias. ………………………………… 81
IH81.cpp: Using references in a range-based for loop. El uso de las referencias en un
ciclo for basado en rango. Temperaturas. ………………………………………………………………………… 82
IH82.cpp: Creating Rvalue references. Crear las referencias del tipo dValor. ………………………………… 83
IH83.cpp: Library Functions for Strings. Librería de funciones para cadenas de caracteres.
cstring standard header. strnlen(cdnB, _countof(cdnB)). Archivo de cabecera
estándar para cadenas tipo c. …………………………………………………………………………………………… 84
IH84.cpp: Joining null-terminated strings. Unir las cadenas terminadas en carácter nulo.
Las funciones strncat_s() y wstrncat_s() .………………………………………………………………………… 86
IH85.cpp: Copying null-terminated strings. Copiar las cadenas de caracteres terminadas
en un carácter nulo \0. ……………………………………………………………………………………………………… 88
IH86.cpp: Comparing null-terminated Strings. La comparación de cadenas de caracteres
terminadas en un carácter nulo \0. …………………………………………………………………………………… 90
IH87.cpp: Searching null-terminated strings. Buscar dentro de cadenas de caracteres
terminadas en un carácter nulo \0. …………………………………………………………………………………… 91
IH88.cpp: Searching null-terminated strings. Buscar dentro de cadenas de caracteres
terminadas en un carácter nulo \0. strstr() .……………………………………………………………………… 92
IH89.cpp: Searching null-terminated strings. Buscar dentro de cadenas de caracteres
terminadas en un carácter nulo \0. …………………………………………………………………………………… 93
IH90.cpp: How to declare and write your own c++ functions. Cómo declarar y escribir
sus propias funciones en c++. …………………………………………………………………………………………… 94
IH91.cpp: The function header. El encabezado de la función. double potencia(double, int);
double potencia(double x, int n){}. …………………………………………………………………………………… 95
IH92.cpp: The function header. El encabezado de la función. Void mi_funcion(void). …………………… 96
IH93.cpp: The function header. El encabezado de la función. Alternative function syntax.
Sintaxis alternativa de la función. ….………………………………………………………………………………… 97
IH94.cpp: Function prototypes. Prototipos de funciones. ……………………………………………………………… 98
IH95.cpp: Passing arguments to a function. El paso de argumentos a la función. Paso por
valor.………………………………………………………………………………………………………………………………… 100
IH96.cpp: Pointers as arguments to a function. Punteros como argumentos de una función.
Paso por punteros. …………………………………………………………………………………………………………… 101
IH97.cpp: Passing arrays to a function. El paso de matrices a una función. …………………………………… 103
IH98.cpp: Using pointer notation when passing arrays. El uso de notación de punteros cuando
se pasa una matriz. …………………………………………………………………………………………………………… 104
IH99.cpp: Passing multidimensional arrays to a function. El paso de matrices
multidimensionales a una función. …………………………………………………………………………………… 105
IH100.cpp: References as arguments to a function. Las referencias como argumentos de una
función. …………………………………………………………………………………………………………………………… 107
IH101.cpp: Constant references as arguments to a function. Las referencias constantes como
argumentos de una función. …………………………………………………………………………………………… 109
IH102.cpp: Using rvalue reference parameters. El uso de un dValor como parámetro de
referencia. ……………………………………………………………………………………………………………………… 110
IH103.cpp: Arguments to main(). Argumentos pasados a la función main() .…………………………………… 112
IH104.cpp: Accepting a variable number of function arguments. Recibir una cantidad
variable de argumentos en la función. …………………………………………………………………………… 113
IH105.cpp: A function that returns a pointer. Una función que devuelve un puntero.
El puntero termina vacío. ……………………………………………………………………………………………… 115
IH106.cpp: A function that returns a pointer. Una función que devuelve un puntero.
El puntero ahora sí es eficiente. ……………………………………………………………………………………… 117
IH107.cpp: Returning a reference. Devolver una referencia. Referencia a un iValor. ……………………… 119
IH108.cpp: Static variables in a function. Variables estáticas en una función. .………………………………… 121
IH109.cpp: A recursive function call. Una llamada a una función recursiva. .…………………………………… 122
IH110.cpp: Pointers to functions. Punteros a funciones. Sintaxis. .………………………………………………… 123
IH111.cpp: Pointers to functions. Punteros a funciones. Ejercicios. .……………………………………………… 125
IH112.cpp: A Pointer to a function as an argument. Un puntero a una función como un
argumento. …………………………………………………………………………………………………………………… 127
IH113.cpp: Arrays of pointers to functions. Matrices de punteros a funciones. .……………………………… 128
IH114.cpp: Omitir argumentos en las llamadas de funciones. .………………………………………………………… 130
IH115.cpp: Exceptions. Excepciones. ……………………………………………………………………………………………… 133
IH116.cpp: Exceptions. Throwing and catching exceptions. Excepciones. Lanzar y
capturar excepciones. (2). probarThrow() ..…………………………………………………………………… 137
IH117.cpp: Exceptions. Throwing and catching exceptions. Excepciones. Lanzar y
capturar excepciones. (3). Catching exceptions. Nested try blocks. Capturar
excepciones. Bloques try anidados. ..……………………………………………………………………………… 135
IH118.cpp: Exceptions. Throwing and catching exceptions. Excepciones. Lanzar y
capturar excepciones. (4). Rethrowing exceptions. Relanzar excepciones. …………………… 138
IH119.cpp: Exception handling in the MFC (Microsoft Foundation Classes). Manejo de
excepciones dentro de MFC. ………………………………………………………………………………………… 141
IH120.cpp: Handling memory allocation errors. Manipular errores de asignación de
memoria. ………………………………………………………………………………………………………………………… 142
IH121.cpp: Function overloading. Sobrecarga de funciones. ..………………………………………………………… 144
IH122.cpp: Reference types and overload selection. Tipos de parámetros de referencia y
selección de funciones por sobrecarga. ..………………………………………………………………………… 146
IH123.cpp: Using a function template. Usar una plantilla de funciones. .………………………………………… 148
IH124.cpp: Using the decltype operator. Usar el operador decltype(expression). ………………………… 150
IH125.cpp: Eliminating blanks from a string. Eliminar los espacios en blanco de una
cadena de caracteres. ……………………………………………………………………………………………………… 154
IH126.cpp: The struct in C++. La estructura en c++. ..…………………………………………………………………… 155
IH127.cpp: The RECT windows structure. La estructura RECT en Windows. .…………………………… 156
IH128.cpp: Using pointers with a struct. Usar punteros con una estructura. .……………………………… 158
IH129.cpp: Accessing structure members through a pointer. El acceso a miembros de una
estructura mediante un puntero. …..………………………………………………………………………………… 159
IH130.cpp: Accessing structure members through a pointer. El acceso a miembros de una
estructura mediante un puntero. The indirect member selection operator. El
operador indirecto de selección de miembros de estructuras. .……………………………………… 160
IH131.cpp: Types, objects, classes, and instances. Tipos de datos, objetos, clases, e
instancias. ……………………………………………………………………………………………………………………… 161
IH132.cpp: Operations on classes. Operaciones con las clases. Understanding classes.
Entender las clases. ……………………………………………………………………………………………………… 163
IH133.cpp: Memberwise initialization of an object. La inicialización de un objeto miembro
a miembro. …………………………………………………………………………………………………………………… 165
IH134.cpp: Initializing class members. La inicialización de los miembros de la clase. ………………… 166
IH135.cpp: Defining a member function outside a class. Definir una función miembro
afuera de la clase. ………………………………………………………………………………………………………… 169
IH136.cpp: Inline functions. Las funciones miembros en línea. …………………………………………………… 170
IH137.cpp: Class constructors. Los constructores de las clases. ……………………………………………………… 172
IH138.cpp: The default constructor. El constructor por defecto. …………………………………………………… 174
IH139.cpp: Supplying a default constructor. Proporcionar un constructor por defecto. ………………… 176
IH140.cpp: Default parameter values in constructors. Valores por defecto en los parámetros
de los constructores. Supplying default values for constructor arguments.
Proporcionar valores por defecto a los argumentos del constructor. …………………………… 179
IH141.cpp: Using a constructor initialization list. Usar una lista de inicialización del
constructor. …………………………………………………………………………………………………………………… 181
IH142.cpp: Making a constructor explicit; implicit conversion. Hacer explícito a un
constructor; conversión implícita. ………………………………………………………………………………… 184
IH143.cpp: Making a constructor explicit; implicit conversion. Hacer explícito a un
constructor. …………………………………………………………………………………………………………………… 186
IH144.cpp: Delegating constructors. Delegar constructores. ………………………………………………………… 188
IH145.cpp: Private members of a class. Miembros privados de una clase. ……………………………………… 190
IH146.cpp: Accessing private class members. Tener acceso a los miembros privados
de la clase. ……………………………………………………………………………………………………………………… 192
IH147.cpp: Accessing private class members. Tener acceso a los miembros privados
de la clase. (2) Inline functions. Funciones en línea. ……………………………………………………… 194
IH148.cpp: The friend functions of a class. Las funciones amigas de una clase. ……………………………… 196
IH149.cpp: The friend functions of a class. Las funciones amigas de una clase. (2)
Placing friend function definitions inside the class. Colocar las
definiciones de las funciones amigas adentro de la clase. ……………………………………………… 199
IH150.cpp: The default copy constructor. El constructor de copia por defecto. ……………………………… 201
IH151.cpp: The pointer "this". El puntero "this" (esto, este, esta). El operador de
acceso indirecto -> .………………………………………………………………………………………………………… 204
IH152.cpp: The pointer "this". El puntero "this" (esto, este, esta). (2) Acceso directo. ………………… 206
IH153.cpp: The pointer "this". El puntero "this" (esto, este, esta). (3) …………………………………………… 209
IH154.cpp: Const objects. Objetos constantes. ………………………………………………………………………………… 211
IH155.cpp: Const member functions of a class. Funciones miembro constantes de una clase. ……… 213
IH156.cpp: Member function definitions outside the class. Definiciones de funciones
miembros afuera de la clase. …………………………………………………………………………………………… 215
IH157.cpp: The pointer "this". El puntero "this" (esto, este, esta). El operador de acceso
indirecto ->. Prueba con objetos constantes. ………………………………………………………………… 217
IH158.cpp: The pointer "this". El puntero "this" (esto, este, esta). (2) Acceso directo.
Prueba con objetos constantes. ……………………………………………………………………………………… 220
IH159.cpp: Constructor separately defined. Un constructor definido separadamente. …………………… 222
IH160.cpp: Arrays of class objects. Matrices de objetos del tipo clase. …………………………………………… 224
IH161.cpp: Static members of a class. Los miembros estáticos de una clase. Static data
members. Los datos miembros estáticos. ……………………………………………………………………… 226
IH162.cpp: Static function members of a class. Las funciones miembros estáticas de una
clase. ……………………………………………………………………………………………………………………………… 228
IH163.cpp: Pointers and references to objects. Pointers to objects. Los punteros y las
referencias a los objetos. Los punteros a los objetos. …………………………………………………… 231
IH164.cpp: Pointers to classes. Exercising the indirect member access operator. Los punteros
a las clases. Practicar el operador indirecto de acceso a los miembros de una
clase. ……………………………………………………………………………………………………………………………… 234
IH165.cpp: References to class objects. Las referencias a los objetos de las clases. ………………………… 236
IH166.cpp: Implementing a copy constructor. Implementar un constructor de copia. …………………… 238
IH167.cpp: Class destructors. The default destructor. Los destructores de las clases.
El destructor por defecto. ……………………………………………………………………………………………… 242
IH168.cpp: Destructors and dynamic memory allocation. Los destructores y la asignación
dinámica de memoria. …………………………………………………………………………………………………… 244
IH169.cpp: Implementing a copy constructor. Implementar un constructor de copia. …………………… 247
IH170.cpp: Implementing a copy constructor. Implementar un constructor de copia. (2). ……………… 251
IH171.cpp: Operator overloading. Sobrecarga de operadores. ………………………………………………………… 255
IH172.cpp: Exercising the overloaded 'less than' and equality operators. Practicar los
operadores sobrecargados para 'menor que' e 'igual que'. ……………………………………………… 258
IH173.cpp: Implementing full support for comparison operators. Implementar soporte total
para los operadores de comparación. ……………………………………………………………………………… 260
IH174.cpp: Incomplete class declaration. Forward declaration of the class type. Declaración
incompleta de la clase. Declaración adelantada del tipo de la clase. ……………………………… 264
IH175.cpp: Overloading the assignment operator. La sobrecarga del operador de asignación "=". 267
IH176.cpp: Overloading the addition operator. La sobrecarga del operador de adición "+".………… 272
IH177.cpp: Overloading the addition operator. La sobrecarga del operador de adición "+".
Friend function. La función amiga. ………………………………………………………………………………… 276
IH178.cpp: Overloading the increment and decrement operators. La sobrecarga de los
operadores de incremento y decremento. ……………………………………………………………………… 281
IH179.cpp: Overloading the function call operator. La sobrecarga del operador de llamada
a función "()".…………………………………………………………………………………………………………………… 284
IH180.cpp: The object copying problem. Avoiding unnecessary copy operations. El problema de
copiar objetos. Evitar las operaciones innecesarias de copiado. ……………………………………… 286
IH181.cpp: Applying rvalue reference parameters. La aplicación de parámetros de referencia
del tipo dValor. ……………………………………………………………………………………………………………… 290
IH182.cpp: Named objects are lvalues. Los objetos que son nombrados constituyen iValores.
Con errores. …………………………………………………………………………………………………………………… 295
IH183.cpp: Class templates. Defining a class template. Las plantillas de las clases.
La definición de la plantilla de una clase. ……………………………………………………………………… 297
IH184.cpp: Template member functions. Las funciones miembros de la plantilla de la clase. ………… 301
IH185.cpp: To define a constructor or a destructor outside of a class template. Definir un
constructor o un destructor por fuera de la plantilla de la clase. …………………………………… 304
IH186.cpp: Creating objects from a class template. La creación de objetos a partir de la
plantilla de una clase. ……………………………………………………………………………………………………… 308
IH187.cpp: Class templates with multiple parameters. Las plantillas de clases con múltiples
parámetros. …………………………………………………………………………………………………………………… 312
IH188.cpp: Parameters that require constants or constant expressions to be substituted
in the class definition. Parámetros que requieren constantes o expresiones
constantes que serán sustituidas en la definición de la clase. ………………………………………… 313
IH189.cpp: Templates for function objects. Las plantillas para funciones objetos. (1) …………………… 316
IH190.cpp: Templates for function objects. Las plantillas para funciones objetos. (2) …………………… 317
IH191.cpp: Perfect forwarding concept. El concepto del adelanto perfecto. …………………………………… 318
IH192.cpp: Default arguments for template parameters. Default function template arguments.
Los argumentos por defecto para los parámetros de las plantillas. Los argumentos
por defecto de las plantillas de funciones. Desviación estándar, media. ………………………… 322
IH193.cpp: Default arguments for template parameters. Default function template arguments.
New data return type. Los argumentos por defecto para los parámetros de las
plantillas. Los argumentos por defecto de las plantillas de funciones.
Nuevo tipo de retorno de los datos. Desviación estándar, media. …………………………………… 325
IH194.cpp: Default class template arguments. Los argumentos por defecto en las plantillas
de las clases. Con errores. ……………………………………………………………………………………………… 328
IH195.cpp: Aliases for class templates. Los alias para las plantillas de las clases. Con errores. ……… 333
IH196.cpp: Template specialization. La especialización de las plantillas. (1). …………………………………… 338
IH197.cpp: Template specialization. La especialización de las plantillas. (2).
Parámetro explícito en la lista de argumentos de la plantilla. ………………………………………… 341
IH198.cpp: A specialization for a class template. A complete specialization. Una
especialización para una plantilla de una clase. Una especialización completa. ……………… 344
IH199.cpp: A specialization for a class template. A partial specialization. Una especialización
para una plantilla de una clase. Una especialización parcial. ………………………………………… 350
IH200.cpp: A specialization for a class template. A partial specialization with remaining
variable parameter. Una especialización para una plantilla de una clase. Una
especialización parcial con parámetro que permanece variable. …………………………………… 353
Preámbulo
En el aprendizaje del lenguaje c++, uno de los obstáculos más tediosos es la ausencia de obras que
ofrezcan los compendios de programas para ejercitarse en la programación, que sean el resultado de
comprobaciones en las plataformas más recientes.
Para proveer una pequeña solución a este valladar, presento al lector una compilación de 200 ejercicios
en Visual c++, versión 2019, que he preparado siguiendo los temas propuestos por el Profesor Ivor
Horton, en su obra “Ivor Horton’s Beginning c++ 2013”, publicada por John Wiley and Sons, Inc.
Los ejercicios comprenden todos los temas e ideas principales expuestos por el Profesor Horton, según
la interpretación y el desarrollo que pude aplicar.
La explicación detallada del contexto conceptual involucrado en cada tema sólo está expuesto
magistralmente en el libro del Profesor Horton. Se recomienda fervientemente la adquisición de este
texto, para alcanzar el dominio conveniente del lenguaje c++.
Espero que el pequeño aporte expuesto en esta obra “200 ejercicios en Visual c++ 2019” sea de utilidad
para quienes desean avanzar rápidamente en el aprendizaje de la programación en este lenguaje.
1

// IH01.cpp: Estructura anterior de las versiones del c++.


//
// Este es el primer ejemplo de programa en c++, que ponen como ejemplo todos los
// autores.
// El programa es muy sencillo. Usualmente incluye una salida del tipo:
// “Hola todos!”.
// La salida se ha eliminado para mostrar los elementos más importantes que tienen
// cambios en el Visual c++ 2019.
// Se abre una plantilla seleccionando "console App", después de haber pulsado
// "Create a new project".
// Aquí se muestra la plantilla antigua, la que pone por defecto el Visual c++ 2010.
//
#include "stdafx.h" // VS2019 no reconoce el archivo "stdafx.h"
// Este archivo de cabecera ya no es
// necesario incluirlo

int _tmain(int argc, _TCHAR* argv[]) // Tampoco reconoce la definición de este


// puntero _TCHAR*
{
return 0; // Esta instrucción viene por defecto en
// Visual c++ 2019.
}

// IH02.cpp: Programa a partir de un proyecto vacío.


//
// Este programa empezó como un "proyecto vacío" (empty project).
// Para aumentar el tamaño de las letras en esta ventana de edición, use
// Tools/Options.../Fonts and colors.

#include <iostream>

int main()
{
std::cout << "Este es un programa sencillo que escribe un poco de texto."
<< std::endl << std::endl;
std::cout << "El texto que aquí se escribe, aparecerá en la pantalla de la computadora."
<< std::endl << std::endl;
std::cout << "Ahora regresaremos al sistema operativo, apretando una tecla."
<< std::endl << std::endl;
// system("pause"); Ya no se necesita. El sistema operativo se detiene al final.
// return 0; Ya no es necesario incluirlo al final; el Visual c++ 2019 lo
// presupone.
}
2

// IH03.cpp: Salida de una corriente de caracteres a la consola.


//
// Practicando la salida de la corriente de caracteres a la consola de la computadora.
//

#include <iostream> // Librería c++ para entrada y salida de cadenas de


// caracteres (stream), i=input, o=output.

using namespace std; // Para no tener que estar escribiendo: "std::cout",


// "std::endl".

int main()
{
int numero1{ 123 }, numero2{ 456 }, numero3{ 789 };
cout << endl << endl << endl;
cout << numero1;
cout << numero1 << numero2;
cout << numero1 << numero2 << numero3;
}

// IH04.cpp: Este archivo inicia la práctica de salida de corrientes de datos a la consola.


//

#include <iostream> // Entre esta instrucción y la siguiente debe existir


// una línea en blanco.
using namespace std;

int main()
{
cout << "\n\n\n"; // Tres líneas en blanco.

std::cout << "Hola todos!\n";

int numero1{ 123 }, numero2{ 456 }, numero3{ 789 };

cout << "\n\n\n";

cout << numero1 << endl << endl;


cout << numero1 << numero2 << endl << endl;
cout << numero1 << numero2 << numero3;

cout << "\n\n\n";


}
3

// IH05.cpp: Usar por primera vez el manipulador de salida de caracteres "setw(n)".


// String manipulators. Manipuladores de cadenas de caracteres.
//

#include <iostream>
#include <iomanip> // Para utilizar setw(n).

using namespace std;

int main()
{
cout << "\n\n\n";

std::cout << "Hola todos!\n";

int numero1{ 123 }, numero2{ 456 }, numero3{ 789 };

cout << "\n\n\n";

cout << numero1 << endl << endl;


cout << numero1 << numero2 << endl << endl;
cout << numero1 << numero2 << numero3;

cout << "\n\n\n";

cout << setw(6) << numero1 << setw(6) << numero2 << setw(6) << numero3;

cout << "\n\n\n";


}
4

// IH06.cpp: Usar por primera vez el manipulador de salida de caracteres "setw(n)" (2).
// También usar cout << std::setiosflags(std::ios::left);

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
cout << "\n\n\n";

std::cout << "Hola todos!\n";

int numero1{ 123 }, numero2{ 456 }, numero3{ 789 };

cout << "\n\n\n";

cout << numero1 << endl << endl;


cout << numero1 << numero2 << endl << endl;
cout << numero1 << numero2 << numero3;

cout << "\n\n\n";

cout << setw(6) << numero1 << setw(6) << numero2 << setw(6) << numero3;

cout << "\n\n\n";

// Esta instrucción determina que los caracteres se escriban alineados


// a la izquierda de cada campo.
// ios significa "input-output-stream".

cout << std::setiosflags(std::ios::left);


cout << setw(6) << numero1 << setw(6) << numero2 << setw(6) << numero3;

cout << "\n\n\n";


}
5

// IH07.cpp: Usando la secuencia de escape: \character. \ es el backslash character.


//
#include <iostream>
using std::cout;
int main()
{
std::cout << "Hola todos!\n";
char nuevaLinea{ '\n' }; // Secuencia de escape que define una nuevaLinea.
// Debe estar escrito entre ' y no entre comillas.
cout << nuevaLinea;
// Se usa el ejemplo en inglés, para utilizar los apóstrofes.
cout << "\"I\'d stay out of your\'s anytime\", she wrote.";
cout << nuevaLinea;
cout << nuevaLinea;
cout << nuevaLinea;
// Se usa el ejemplo en inglés, para utilizar los apóstrofes.
cout << "\n\tOur teacher\'s pet, in it\'s astonishing joy.\a\a";
cout << nuevaLinea;
cout << nuevaLinea;
cout << nuevaLinea;
return 0;
}

// IH08.cpp: The assignment statement. La instrucción de asignación.


//
#include <iostream>

using namespace std;

int main()
{
cout << endl << endl << endl;

int parte1, parte2, parte3;

parte1 = 5; parte2 = 5; parte3 = 5;

int total = parte1 + parte2 + parte3;

cout << "El total es: " << total;

cout << endl << endl << endl;


}
6

// IH09.cpp: Binary operations. Unary operations. Operaciones binarias. Operaciones unarias.


//
#include <iostream>
using namespace std;
int main()
{
cout << endl << endl << endl;
int a{}; int c{};
int b{-5};

a = -b; // signo menos unitario; unary minus

c = a - b; // operación binaria

cout << "Este programa usa operaciones binarias y unarias.";


cout << endl << endl << endl;
}

// IH10.cpp: Calculating a remainder. Calculando el residuo.


//
#include <iostream>
using namespace std;
int main()
{
system("title Calculando el residuo.");
system("cls");
system("color F1");
cout << endl << endl << endl;
int residuo{}, galletas{ 19 }, bichas{ 5 };

residuo = galletas % bichas;

int galletas_por_bicha = galletas / bichas;

cout << "Total de galletas: " << galletas << endl << endl;

cout << "Total de niñas: " << bichas << endl << endl;

cout << "Galletas por cada bicha: " << galletas_por_bicha << endl << endl;

cout << "El residuo es: " << residuo << endl << endl;

cout << endl << endl << endl;


}
7

// IH11.cpp: Type conversion and casting. Conversión de tipos de datos y simulación de tipos.
//
#include <iostream>
using namespace std;
int main()
{
system("title Conversión y simulación de tipos de datos");
system("cls");
system("color F1");
cout << endl << endl << endl;
unsigned int a{ 10U };// Valor entero, sin signo.
signed int b{ 20 }; // Valor entero, con signo.
cout << a - b; // El valor con signo de la variable b es convertido a variable sin
// signo, para hacer equivalente el tipo de dato de b al de a.
// El resultado no es -10, sino 4294967286.
// El compilador hace una conversión implícita.
cout << endl << endl << endl;
}

// IH12.cpp: Conversión de tipos de datos en las asignaciones.


//
#include <iostream>
using namespace std;
int main()
{
system("title Conversión de tipos de datos en las asignaciones");
system("cls");
system("color F2");
cout << endl << endl << endl;
int numero{}; // Número entero vacío o cero
float decimal{ 2.5F }; // Número decimal flotante, precisión simple
numero = decimal; // El valor decimal 2.5 es asignado a un valor entero
8

// La variable numero sólo recibe el dato 2; la parte


// decimal o.5 se pierde debido a la conversión.
cout << "Número es igual a " << numero;
cout << endl << endl << endl;
}

// IH13.cpp: Forzar la conversión del tipo de datos mediante el intérprete "cast".


//
// Esto se llama también "conversión explícita del tipo de dato".
#include <iostream>
using namespace std;
int main()
{
system("title Interpretación por cast para convertir explícitamente el tipo de datos");
system("cls");
system("color FD");
cout << endl << endl << endl;
/* Tipos de "cast" que se pueden usar:
static_cast<tipo_de_datos_al_que_se_quiere_convertir>(expresión_o_variable)
la interpretación se realiza por el compilador en forma estática, no se realizan
chequeos para ver si la conversión es segura de aplicar cuando se ejecuta el pro-
grama.
dynamic_cast...
la interpretación se realiza en forma dinámica, cuando el programa se está ejecu-
tando.
const_cast...
se usa para remover la constancia de una expresión.
reinterpret_cast...
es una conversión incondicional.
*/
double valor1{ 10.5 };
double valor2{ 15.5 };
9

int numero_entero{};
numero_entero = static_cast<int>(valor1) + static_cast<int>(valor2);
cout << "El valor de la suma de ambas cantidades convertidas a enteros, es: "
<< numero_entero;
cout << "\n\nLos números que se sumaron fueron 10.5 y 15.5";
cout << endl << endl << endl;
}

// IH14.cpp: The "auto" keyword. La palabra clave "auto".


//
#include <iostream>
using namespace std;
int main()
{
system("title La palabra clave <auto>");
system("cls");
system("color FD");
cout << endl << endl << endl;
auto n = 16; // El tipo de dato es entero
auto pi = 3.14159; // El tipo es doble
auto x = 3.5F; // El tipo es flotante
auto hallado = false; // El tipo de dato es booleano (bool)
auto m2{ 2 }; // El tipo será initializer_list<int>
auto k(45); // El tipo será entero
const auto e = 2.71828L; // El tipo es const long double
const auto docena(12); // El tipo es const int
auto factor(n * pi * pi); // El tipo es double
cout << endl << endl << endl;
}
10

// IH15.cpp: The "auto" keyword. La palabra clave "auto". The "typeid" operator. El operador
// "typeid".
//
#include <iostream>
using namespace std;
int main()
{
system("title La palabra clave <auto>");
system("cls");
system("color FD");
cout << endl << endl << endl;
auto n = 16; // El tipo de dato es entero
auto pi = 3.14159; // El tipo es doble
auto x = 3.5F; // El tipo es flotante
auto hallado = false; // El tipo de dato es booleano (bool)
auto m2{ 2 }; // El tipo será initializer_list<int>
// El programa lo saca como int nada más
auto k(45); // El tipo será entero
const auto e = 2.71828L; // El tipo es const long double
const auto docena(12); // El tipo es const int
auto factor(n * pi * pi); // El tipo es double
cout << "El tipo de dato de n es " << typeid(n).name() << endl << endl;
cout << "El tipo de dato de pi es " << typeid(pi).name() << endl << endl;
cout << "El tipo de dato de hallado es " << typeid(hallado).name() << endl << endl;
cout << "El tipo de dato de m2 es " << typeid(m2).name() << endl << endl;
cout << "El tipo de dato de k es " << typeid(k).name() << endl << endl;
cout << "El tipo de dato de e es " << typeid(e).name() << endl << endl;
cout << "El tipo de dato de docena es " << typeid(docena).name() << endl << endl;
cout << "El tipo de dato de factor es " << typeid(factor).name() << endl << endl;
cout << endl << endl << endl;
}
11

// IH16.cpp: Bitwise operators. Operadores de bits.


//
#include <iostream>
using namespace std;
int main()
{
system("title Operadores de bits");
system("cls");
system("color FD");
cout << endl << endl << endl;
char letraA{ 'A' };
char letraB{ 'B' };
char letraC{ 'C' };
char letraD{ 'D' };
char letraE{ 'E' };
char letraF{ 'F' };
char letraG{ 'G' };
char letraH{ 'H' };
char letraI{ 'I' };
char letraJ{ 'J' };
char letraK{ 'K' };
char letraL{ 'L' };
char letraM{ 'M' };
char letraN{ 'N' };
char letraNN{ 'Ñ' };
// short int letraNN{ 0xa5 }; // Letra Ñ, para estar seguros
char letraO{ 'O' };
char letraP{ 'P' };
char letraQ{ 'Q' };
char letraR{ 'R' };
char letraS{ 'S' };
char letraT{ 'T' };
char letraU{ 'U' };
12

char letraV{ 'V' };


char letraW{ 'W' };
char letraX{ 'X' };
char letraY{ 'Y' };
char letraZ{ 'Z' };
char resultado{};
resultado = letraA & letraB;
cout << "El resultado de A AND B es " << resultado << endl;
cout << 0x41 << " " << 0x42 << " = " << 0x40 << endl << endl;
// El resultado de A AND B es @
resultado = letraA & letraC;
cout << "El resultado de A AND C es " << resultado << endl;
cout << 0x41 << " " << 0x43 << " = " << 0x41 << endl << endl;
// El resultado de A AND C es A
resultado = letraA & letraD;
cout << "El resultado de A AND D es " << resultado << endl;
cout << 0x41 << " " << 0x44 << " = " << 0x40 << endl << endl;
// El resultado de A AND D es @
resultado = letraA & letraE;
cout << "El resultado de A AND E es " << resultado << endl;
cout << 0x41 << " " << 0x45 << " = " << 0x41 << endl << endl;
// El resultado de A AND E es A
resultado = letraA & letraF;
cout << "El resultado de A AND F es " << resultado << endl;
cout << 0x41 << " " << 0x46 << " = " << 0x40 << endl << endl;
// El resultado de A AND F es @
resultado = letraA & letraG;
cout << "El resultado de A AND G es " << resultado << endl;
cout << 0x41 << " " << 0x47 << " = " << 0x41 << endl << endl;
// El resultado de A AND G es A
resultado = letraA & letraH;
cout << "El resultado de A AND H es " << resultado << endl;
cout << 0x41 << " " << 0x48 << " = " << 0x40 << endl << endl;
13

// El resultado de A AND H es @
resultado = letraA & letraI;
cout << "El resultado de A AND I es " << resultado << endl;
cout << 0x41 << " " << 0x49 << " = " << 0x41 << endl << endl;
// El resultado de A AND I es A
resultado = letraA & letraJ;
cout << "El resultado de A AND J es " << resultado << endl;
cout << 0x41 << " " << 0x4A << " = " << 0x40 << endl << endl;
// El resultado de A AND J es @
resultado = letraA & letraK;
cout << "El resultado de A AND K es " << resultado << endl;
cout << 0x41 << " " << 0x4B << " = " << 0x41 << endl << endl;
// El resultado de A AND K es A
resultado = letraA & letraL;
cout << "El resultado de A AND L es " << resultado << endl;
cout << 0x41 << " " << 0x4C << " = " << 0x40 << endl << endl;
// El resultado de A AND L es @
resultado = letraA & letraM;
cout << "El resultado de A AND M es " << resultado << endl;
cout << 0x41 << " " << 0x4D << " = " << 0x41 << endl << endl;
// El resultado de A AND M es A
resultado = letraA & letraN;
cout << "El resultado de A AND N es " << resultado << endl;
cout << 0x41 << " " << 0x4E << " = " << 0x40 << endl << endl;
// El resultado de A AND N es @
resultado = letraA & letraO;
cout << "El resultado de A AND O es " << resultado << endl;
cout << 0x41 << " " << 0x4F << " = " << 0x41 << endl << endl;
// El resultado de A AND O es A
resultado = letraA & letraP;
cout << "El resultado de A AND P es " << resultado << endl;
cout << 0x41 << " " << 0x50 << " = " << 0x40 << endl << endl;
// El resultado de A AND P es @
14

resultado = letraA & letraQ;


cout << "El resultado de A AND Q es " << resultado << endl;
cout << 0x41 << " " << 0x51 << " = " << 0x41 << endl << endl;
// El resultado de A AND Q es A
resultado = letraA & letraR;
cout << "El resultado de A AND R es " << resultado << endl;
cout << 0x41 << " " << 0x52 << " = " << 0x40 << endl << endl;
// El resultado de A AND R es @
resultado = letraA & letraS;
cout << "El resultado de A AND S es " << resultado << endl;
cout << 0x41 << " " << 0x53 << " = " << 0x41 << endl << endl;
// El resultado de A AND S es A
resultado = letraA & letraT;
cout << "El resultado de A AND T es " << resultado << endl;
cout << 0x41 << " " << 0x54 << " = " << 0x40 << endl << endl;
// El resultado de A AND T es @
resultado = letraA & letraU;
cout << "El resultado de A AND U es " << resultado << endl;
cout << 0x41 << " " << 0x55 << " = " << 0x41 << endl << endl;
// El resultado de A AND U es A
resultado = letraA & letraV;
cout << "El resultado de A AND V es " << resultado << endl;
cout << 0x41 << " " << 0x56 << " = " << 0x40 << endl << endl;
// El resultado de A AND V es @
resultado = letraA & letraW;
cout << "El resultado de A AND W es " << resultado << endl;
cout << 0x41 << " " << 0x57 << " = " << 0x41 << endl << endl;
// El resultado de A AND W es A
resultado = letraA & letraX;
cout << "El resultado de A AND X es " << resultado << endl;
cout << 0x41 << " " << 0x58 << " = " << 0x40 << endl << endl;
// El resultado de A AND X es @
resultado = letraA & letraY;
15

cout << "El resultado de A AND Y es " << resultado << endl;
cout << 0x41 << " " << 0x59 << " = " << 0x41 << endl << endl;
// El resultado de A AND Y es A
resultado = letraA & letraZ;
cout << "El resultado de A AND Z es " << resultado << endl;
cout << 0x41 << " " << 0x5A << " = " << 0x40 << endl << endl;
// El resultado de A AND Z es @
cout << endl << endl << endl;
resultado = letraA & letraNN;
cout << "El resultado de A AND eNiee es " << resultado << endl;
cout << 0x41 << " " << 0xA5 << " = " << 0x41 << endl << endl;
// El resultado de A AND Ñ es A
cout << endl << endl << endl;
}

// IH17.cpp: Operaciones con bits. Uso de máscaras de bits


//
#include <iostream>
using namespace std;
int main()
{
system("title Uso de máscaras de bits con AND &"); // Esto no está funcionando
system("cls");
system("color FD");
cout << endl << endl << endl;
/* Suponga que Usted tiene una letra o dato tipo char, y desea poner a cero
los 4 bits de menor significancia, es decir, los 4 bits de la derecha.
Para hacerlo, puede usar una máscara con los 4 bits menores poniéndolos
a cero, o sea 0xF0. 1111 0000. */
char letraA{ 'A' };
letraA &= 0xF0; // Aplicación de la máscara 0xF0
cout << "La letra A, 0x41, 0100 0001, con la máscara 0xF0: " << letraA;
16

char letraB{ 'B' };


/* Ahora vamos a conservar los 4 bits de la derecha y pondremos a cero los
4 bits de la izquierda. La máscara será 0x0F. 0000 1111. El resultado no
es legible dentro del código ascii. */
letraB &= 0x0F; // Aplicación de la máscara 0x0F
cout << endl << endl << endl;
cout << "La letra B, 0x42, 0100 0010, con la máscara 0x0F: " << letraB;
cout << endl << endl << endl;
}

// IH18.cpp: Bitwise operator OR, |. Operador de bits OR.


//
// Este es un ejercicio de cómo poner banderas (flags) en los bits individuales
// de un número entero.
// También usa la clase "numeric_limits<type>::min()" para determinar el valor
// mínimo.
//
#include <iostream>
using namespace std;
int main()
{
system("cls");
system("color FD");
cout << endl << endl << endl;
unsigned short estilo0{ 0U }; // "0000 0000 0000 0000" = 0
unsigned short estilo1{ 65535U }; // "1111 1111 1111 1111" = 65535
short estilo2{ 0 };
short estilo3{ 1024 };

cout << "Límite inferior de estilo2, short, " << numeric_limits<short>::min() << endl;
cout << "Límite superior de estilo2, short, " << numeric_limits<short>::max()
<< endl << endl;
17

cout << "Límite inferior de estilo0, unsigned short, "


<< numeric_limits<unsigned short>::min() << endl;
cout << "Límite superior de estilo2, unsigned short, "
<< numeric_limits<unsigned short>::max() << endl << endl;

unsigned short vector_ultimo_bit{ 0x01U }; // "0000 0000 0000 0001" = 1


unsigned short vector_penultimo_bit{ 0x02U }; // "0000 0000 0000 0010" = 2
unsigned short vector_antepenultimo_bit{ 0x04U }; // "0000 0000 0000 0100" = 4

cout << "El valor de estilo0 es: " << estilo0 << endl;
cout << "El valor de estilo1 es: " << estilo1 << endl << endl;

estilo0 = vector_penultimo_bit | vector_ultimo_bit; // "0000 0000 0000 0011" = 3

cout << "El valor de estilo0 es: " << estilo0 << endl << endl;

estilo0 = 0U;
estilo0 = vector_antepenultimo_bit | vector_penultimo_bit | vector_ultimo_bit;
// "0000 0000 0000 0111" = 7

cout << "El valor de estilo0 es: " << estilo0 << endl;

cout << endl << endl << endl;


}
18

// IH19.cpp: Bitwise operator XOR, ^. Operador de bits XOR, exclusive OR.


//
// Este es un ejercicio de cómo poner banderas (flags) en los bits individuales
/ de un número entero.
// También usa la clase "numeric_limits<type>::min()" para determinar el valor
// mínimo.
//
#include <iostream>
using namespace std;
int main()
{
system("cls");
system("color FD");
cout << endl << endl << endl;
unsigned short estilo0{ 0U }; // "0000 0000 0000 0000" = 0
unsigned short estilo1{ 65535U }; // "1111 1111 1111 1111" = 65535
short estilo2{ 0 };
short estilo3{ 1024 };
cout << "Límite inferior de estilo2, short, " << numeric_limits<short>::min() << endl;
cout << "Límite superior de estilo2, short, "
<< numeric_limits<short>::max() << endl << endl;
cout << "Límite inferior de estilo0, unsigned short, "
<< numeric_limits<unsigned
short>::min() << endl;
cout << "Límite superior de estilo2, unsigned short, "
<< numeric_limits<unsigned
short>::max() << endl << endl;
unsigned short vector_ultimo_bit{ 0x01U }; // "0000 0000 0000 0001" = 1
unsigned short vector_penultimo_bit{ 0x02U }; // "0000 0000 0000 0010" = 2
unsigned short vector_antepenultimo_bit{ 0x04U }; // "0000 0000 0000 0100" = 4
cout << "El valor de estilo0 es: " << estilo0 << endl;
cout << "El valor de estilo1 es: " << estilo1 << endl << endl;
19

estilo1 = estilo1 ^ vector_ultimo_bit; // "1111 1111 1111 1100" = 65534

cout << "El valor de estilo1 es: " << estilo1 << endl << endl;

estilo1 = 65535U;

estilo1 = estilo1 ^ vector_penultimo_bit ^ vector_ultimo_bit;


// "1111 1111 1111 1000" = 65532
cout << "El valor de estilo1 es: " << estilo1 << endl;

cout << endl << endl << endl;


}

// IH20.cpp: XOR - ^. Exclusive OR. OR exclusivo.


//
#include <iostream>
using namespace std;
int main()
{
system("cls");
system("color FD");
cout << endl << endl << endl;
char primera_letra{ 'A' }; // "0100 0001"
char ultima_letra{ 'Z' }; // "0101 1010"
primera_letra ^= ultima_letra; // "0001 1011"
cout << "La primera letra, A, cambio a " << primera_letra << endl << endl;
ultima_letra ^= primera_letra; // "0100 0001" letra A
cout << "La anterior letra, cambio a " << ultima_letra << endl << endl;
primera_letra ^= ultima_letra; // "0101 1010" letra Z
cout << "La anterior letra, cambio a " << primera_letra << endl << endl;
/* El resultado de estas operaciones es que el contenido de ambas variables char se
Intercambia (swap), sin necesidad de utilizar ninguna posición de memoria.
20

Esto se puede hacer con cualquier variable int.


Se puede usar en criptografía. */
cout << endl << endl << endl;
}

// IH21.cpp: The Bitwise NOT ~. El operador de bits NOT ~.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char letraA{ 'A' }; // "0100 0001", 0x41, 65d
char letra_A = ~letraA; // "1011 1110", 0xBE, 190d
// ~ es Alt + 126
// < es Alt + 60; > es Alt + 62
cout << "La letra A es 0x41, 65d, " << letraA << endl << endl;
cout << "La letra ~A es 0xBE, 190d, " << letra_A << endl << endl; // ╛
cout << endl << endl << endl;
}

// IH22.cpp: Bitwise shift operators. Operadores de desplazamiento de bits.


//
// Desplazamiento hacia la izquierda y hacia la derecha.
// Para realizar el desplazamiento, el compilador agrega bits 0. Si el desplazamiento
// es hacia la izquierda, el compilador agrega la cantidad de bits 0 en el extremo
// derecho.
// Los últimos bits a la izquierda se eliminan y se pierden. Lo mismo ocurre hacia
// la derecha.
// División y multiplicación. Sobrecarga de operadores.
21

//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
unsigned short numero{ 16387U }; // "0100 0000 0000 0011"
numero <<= 2;
/* Este es el operador de desplazamiento hacia la izquierda. El desplazamiento es de dos
posiciones, o dos bits.
El resultado será 12. "0000 0000 0000 1100". Los dos bits 1 de 16387U, que sumaban 1 +
2 =3, ahora son los bits 1 4 + 8 = 12. Los dos bits de la izquierda, 01, se desplazaron hacia
la izquierda y quedaron fuera de memoria; por tanto, se perdieron. */

cout << "El desplazamiento de dos bits hacia la izquierda en 16387U dio " << numero << endl
<< endl;

/* Redefinamos la variable numero hacia su valor original */


numero = 16387U;
numero >>= 2;
/* Este es el operador de desplazamiento hacia la derecha.
El desplazamiento es de dos posiciones, o dos bits.
El resultado será 4,096. "0010 0000 0000 0000".
*/
cout << "El desplazamiento de dos bits hacia la derecha en 16387U dio " << numero << endl
<< endl;
/* El desplazamiento de bits hacia la izquierda es similar a la división entre potencias de 2.
Si se desplaza una posición hacia la izquierda, asumiendo que no haya pérdida de bits en
el extremo izquierdo, el resultado será el doble del valor inicial. Probemos con el número
4, un desplazamiento hacia la izquierda en un bit, nos dará el resultado 8. */
numero = 4U;
numero <<= 1;
cout << "El desplazamiento de un bit hacia la izquierda en 4U dio "
<< numero << endl << endl;
22

cout << "O sea, la cantidad 4 se multiplicó por 2 y dio 8." << endl << endl;
/* Los dos operadores << y >>, que también se usan con cout, actúan aquí como "operadores
sobrecargados".
Es decir, han sido redefinidas sus funciones como objetos, para que en este otro contexto,
donde no están delimitados por cout, sino que por =, actúen como operadores de
desplazamiento. En el otro modo, son objetos para actuar como operadores de inserción
de salida y entrada de caracteres.
El proceso se llama "sobrecarga de operadores".
*/
cout << endl << endl << endl;
}

// IH23.cpp: Lvalues and Rvalues. Valores a la izquierda y valores a la derecha.


//
// Lvalues son valores que pueden ser almacenados en una ubicación permanente de
// la memoria.
// Rvalues son valores que se almacenan en forma transitoria.
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
int a{}, b{ 1 }, c{ 2 }; // Se inicializa tres valores.
a = b + c; // El procesador suma los valores almacenados en memoria
// para las variables b y c. El resultado lo guarda en la
// posición de memoria que quedó asignada a la variable a.
// Temporalmente se mantiene en memoria el valor de b+c.
// Cuando la ejecución termina, se borra de la memoria.
// b+c es un Rvalue (dValor). a es un Lvalue (iValor).
b = ++a; // ++a es un Lvalue, porque se asigna a una posición. Su
// cálculo ocurre después de verificar el valor de a.
23

c = a++; // a++ es un Rvalue, porque primero se verifica el valor


// de a, y luego se procede a incrementarlo. Esto queda
// guardado temporalmente.
cout << "Los valores de a, b y c, son " << a << " " << b << " " << c;
cout << endl << endl << endl;
}

// IH24.cpp: Todas las variables tienen un tiempo de vida finito.


//
// Inician cuando se les declara. Y en algún momento, desaparecen.
// Todas desaparecen finalmente cuando el programa termina.
// Esta propiedad se llama "duración de almacenamiento".
// Hay tres tipos de duración de almacenamiento:
// duración de almacenamiento automática
// duración de almacenamiento estática
// duración de almacenamiento dinámica
// El ámbito o alcance de una variable se llama scope. Dentro de un programa,
// la variable permanece funcionando para un determinado alcance. Este alcance
// puede ser para todo el programa (variables globales) o sólo dentro de una
// función específica.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
int cuenta1{ 10 };
int cuenta3{ 50 };

cout << "Valor externo de la variable automática cuenta1: " << cuenta1 << endl << endl;
24

{ // Desde esta llave empieza un nuevo alcance.


cuenta1 = 60; // Nuevo valor de cuenta1 dentro de un ámbito diferente.
// Aquí, el valor 60 oculta al valor externo 10.
int cuenta2{ 80 }; // cuenta2 es una variable automática interna aquí.
cout << "Valor interno de la variable automática cuenta1: " << cuenta1
<< endl << endl;
cuenta1 += 5; // Se modifica el valor interno y externo de cuenta1.
cout << "Nuevo valor interno de la variable automática cuenta1: " << cuenta1
<< endl << endl;
cout << "La variable automática interna cuenta2 tiene el valor: " << cuenta2
<< endl << endl;
cout << "El valor externo de cuenta1 permanece inalterado, en 10." << endl
<< endl << endl;
cuenta3 += cuenta2; // La variable cuenta3 está dentro de un alcance mayor.
// Y se puede modificar aquí.
}
cout << "Las variables cuenta1 y cuenta3 valen: " << cuenta1 << " " << cuenta3 << endl;

cout << endl << endl << endl;


}

// IH25.cpp: Scope resolution operator. Operador de resolución de alcance. ::


//
#include <iostream>
using namespace std;
int cuenta1{ 100 }; // Variable global cuenta1
int main()
{ // El alcance de la función empieza aquí.
system("color FD");
cout << endl << endl << endl;
int cuenta1{ 10 }; // Variable automática de alcance limitado, interno,
// que aplica sólo dentro de la función. A partir de
25

// aquí tenemos dos variables cuenta1, la global que


// tiene el valor 100, y la de la función main, que
// tiene el valor 10.
int cuenta2{ 80 };
cout << "Valor interno de cuenta1 en la función main: " << cuenta1 << endl << endl;
cout << "Valor externo, de la variable global, afuera de la función main: " << ::cuenta1
<< endl << endl;
cout << "Valor interno de la variable local cuenta2: " << cuenta2;
cout << endl << endl << endl;
}

// IH26.cpp: Static variables. Variables estáticas.


//
// Son variables definidas y usadas localmente dentro de un bloque, o incluso dentro
// de varios bloques. Tienen alcance dentro del bloque, y no fuera del bloque. Y su
// duración difiere en cuanto a que no se borra el contenido de la variable en la
// memoria, a pesar de que su alcance es limitado. Su duración llega hasta el final del
// programa.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
static int cuenta0{ 500 }; // La "variable" cuenta0 tendrá el valor 500 durante
// todo el programa, aún afuera de la función main.
cout << endl << endl << endl;
}
int funcionA{
// static int cuenta0 = 300; // El compilador no permite su uso.
}; // Este punto y coma es muy necesario.
26

// IH27.cpp: Enumerations. Enumeraciones.


//
// Son listas de datos, que guardan un orden entre ellos, que se pueden manejar en
// forma especial.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;

// Viejo estilo de definir enumeraciones.


enum diasDeLaSemana{lunes, martes, miércoles, jueves, viernes, sábado, domingo} hoy;

/* La "variable" diasDeLaSemana, solo puede tomar los valores que se han definido entre
Las dos llaves.
La variable hoy es una instancia de la variable diasDeLaSemana. Puede tomar el valor
"lunes", etc.
Los valores de diasDeLaSemana serán referidos por su posición, como en una matriz.
"lunes" tiene el valor 0.
*/
hoy = lunes; // hoy tendrá el valor entero 0.
cout << "El valor de hoy (lunes) es " << hoy << endl << endl;
hoy = jueves; // ahora tendrá el valor entero 3.
cout << "El valor de hoy (jueves) es " << hoy << endl << endl;
//---------------------------------------------------------------------------
enum meses{enero = 1, febrero, marzo, abril, mayo, junio, julio, agosto, septiembre, octubre,
noviembre, diciembre} mes;
mes = enero; // mes tendrá el valor 1.
cout << "El valor de mes (enero) es " << mes << endl << endl;
enum trimestre{january = 1, february = 1, march = 1, april, may, june} trim;
/* Con esta forma de enumerar, la variable trim queda sin alcance. Todos los valores serán
1. */
27

trim = march; // trim tendrá el valor 1.


cout << "El valor de trim (march) es " << mes << endl << endl;
trim = june; // trim tendrá el valor 2. No funciona, valor 1.
cout << "El valor de trim (june) es " << mes << endl << endl;
cout << endl << endl << endl;
}

// IH28.cpp: Nuevo procedimiento para las enumeraciones.


//
// Este nuevo procedimiento se definió a partir del estándar c++ 11.
// Se utiliza una clase para definir la enumeración. Es muchísimo más seguro que el
// viejo.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
enum class DiasSemana {lunes, martes, miércoles, jueves, viernes, sábado,
domingo} hoy, manana, ayer;
hoy = DiasSemana::viernes;
DiasSemana today{ DiasSemana::domingo };
cout << "El día de hoy es " << static_cast<int>(today) << endl << endl; // Código: 6.
//---------------------------------------------------------
enum class Baraja {flor, diamante, corazón, espada};
enum class Joya {diamante, rubí, perla, ópalo, esmeralda, safiro};
Baraja miCarta{ Baraja::diamante };
int miCartaValor{ static_cast<int>(miCarta) }; // Convierte miCarta al tipo de dato int.
//---------------------------------------------------------
enum class DiasSem : char {lunes, martes, miércoles, jueves, viernes, sábado, domingo};
enum class Colores : char {rojo, azul, verde, amarillo, café, rosado, violeta};
28

Colores azulCeleste{ Colores::azul };


cout << "Código del color azul celeste " << static_cast<unsigned long>(azulCeleste) << endl;
// Código: 1.
cout << endl << endl << endl;
}

// IH29.cpp: Creating your own namespaces. La creación de sus propios nombres de dominios.
//
#include <iostream>
#include <iomanip> // Para poder usar el manipulador de entrada/salida setw(n).
// setw(n) asigna n espacios para caracteres, justificado a
// la derecha. Se puede cambiar para justificarlo a la izq.
namespace miDom // Crear el dominio miDom.
{
long double PI = 3.141592654;
}
using namespace std;
using namespace miDom;
int main()
{
system("color FD");
cout << endl << endl << endl;
cout << "El valor de PI es " << setw(10) << PI << endl << endl;
cout << endl << endl << endl;
}
29

// IH30.cpp: The if statement. La sentencia if.


#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char letraA{ 'A' };
if ('A' == letraA)
cout << "Esta es la primera letra del alfabeto, la letra A.";
cout << endl << endl << endl;
}

// IH31.cpp: The if statement. La sentencia condicional if.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char letra{ 'A' };
if ('A' == letra)
{
cout << "Esta es la primera letra en la mayoría de los alfabetos.";
letra = 'b';
}
cout << endl << endl << "La letra ahora es: " << letra;
cout << endl << endl << endl;
}
30

// IH32.cpp: The nested if. La sentencia anidada if.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char letra{};
cout << "Escriba una letra: ";
cin >> letra;
if (letra >= 'A')
{
if (letra <= 'Z')
{
cout << endl << endl << "Usted escribió una letra mayúscula." << endl << endl;
// Funciona.
}
return 0;
}
if (letra >= 'a')
{
if (letra <= 'z')
{
cout << endl << endl << "Usted escribió una letra minúscula." << endl << endl;
// No funciona.
}
return 0;
}
cout << endl << endl << "Usted no escribió ninguna letra.";
// Funciona bien.
cout << endl << endl << endl;
}
31

// IH33.cpp: Convert to lowercase. Convertir a letra minúscula.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char letraA{ 'A' };
cout << "La letraA es " << letraA << endl << endl;
// Convertirla a letra minúscula
letraA += 'a' - 'A';
cout << "La letraA es ahora " << letraA << endl << endl;
// Convertirla a letra mayúscula
letraA -= 'a' - 'A';
cout << "La letraA se retornó a la letra " << letraA << endl << endl;
cout << endl << endl << endl;
}

// IH34.cpp: The extended if statement. La sentencia if extendida. If, else.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
long numero{};

cout << "Escriba un número que sea menor que 2 mil millones. ";
cin >> numero;
32

if (numero % 2L)
cout << endl << endl << "Su número es impar." << endl << endl;
else
cout << endl << endl << "Su número es par." << endl << endl;

cout << endl << endl << endl;


}

// IH35.cpp: The nested if-else. La sentencia anidada if-else.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char cafe{ 'n' };
char dona{ 'n' };

cout << "Escriba una 'y' si desea que le sirvamos café. 'n' si no. ";
cin >> cafe;

cout << endl << endl << "Escriba una 'y' si también desea que le demos una dona. 'n' si no. ";
cin >> dona;

if ('y' == cafe)
if ('y' == dona)
cout << endl << endl << "Le serviremos café con dona.";
else
cout << endl << endl << "Solo le serviremos café.";
else
if ('y' == dona)
33

cout << endl << endl << "Le serviremos una dona.";
else
cout << endl << endl << "Le serviremos un vaso de agua.";

cout << endl << endl << endl;


}

// IH36.cpp: The logical operators. Los operadores lógicos. AND, OR, NOT.
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char letra{};
cout << "Escriba una letra cualquiera: ";
cin >> letra;
cout << endl << endl;
if ((letra >= 'A') && (letra <= 'Z')) // El operador AND.
cout << "Su letra es una letra mayúscula.";
else
cout << "Su letra no es una letra mayúscula.";
double ingreso{ 0.0 };
double capital{ 0.0 };
cout << endl << endl;
cout << "Introduzca el valor de su ingreso anual: ";
cin >> ingreso;
cout << endl << endl;
cout << "Introduzca el valor de su capital: ";
cin >> capital;
cout << endl << endl;
34

if ((ingreso >= 100000.) || (capital >= 300000.)) // El operador OR.


cout << "La cantidad que le podemos prestar es menor que 200,000.";
else
cout << "Lo sentimos mucho, el banco no le puede hacer un préstamo.";
int num2{ -10 };
cout << endl << endl << "Los múltiplos de num2 son: ";
if ((num2 >= -10) && (num2 != 0))
{
cout << setw(10) << 3 * num2; // -30
num2 += 3;
}
if ((num2 >= -10) && (num2 != 0))
{
cout << setw(10) << 3 * num2; // -21
num2 += 3;
}
if ((num2 >= -10) && (num2 != 0))
{
cout << setw(10) << 3 * num2; // -12
num2 += 3;
}
if ((num2 >= -10) && (num2 != 0))
{
cout << setw(10) << 3 * num2; // -3
num2 += 3;
}
if ((num2 >= -10) && (num2 != 0))
{
cout << setw(10) << 3 * num2; // 6
num2 += 3;
}
cout << endl << endl << endl;
}
35

// IH37.cpp: The conditional operator. El operador condicional.


//
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
int a{ 16 };
int b{ 44 };
int c{ 0 };
/* Al operador condicional se le llama el operador ternario, porque comprende tres
operandos.
Veamos un ejemplo. Supongamos que tenemos dos variables, a y b, y deseamos
Asignar el valor máximo entre el de a y el de b a una tercera variable c. Eso se puede
hacer con una sola instrucción usando el operador condicional.
*/
c = a > b ? a : b; // Asigna el valor que sea mayor, entre a o b. Si a es mayor que b,
// asigna a c el valor de a; de lo contrario, asigna el valor de b.
cout << "Los valores de a, b y c son: " << setw(10) << a << setw(10) << b << setw(10) << c;
c = 0; b = 88; a = 599;
if (a > b)
c = a;
else
c = b;
cout << "\n\n\nLos valores de a, b y c son: " << setw(10) << a << setw(10) << b
<< setw(10) << c;
int nPasteles{ 1 }; // Cuenta del número de pasteles.
cout << endl << endl << endl
<< "Tenemos " << nPasteles << " pastel" << ((nPasteles > 1) ? "es." : ".")
<< endl;
++nPasteles;
cout << endl << endl
<< "Tenemos " << nPasteles << " pastel" << ((nPasteles > 1) ? "es." : ".")
36

<< endl;
nPasteles = 1;
cout << endl << endl
<< "Hay " << ((nPasteles > 1) ? "unos " : "un ") << nPasteles << " pastel"
<< ((nPasteles > 1) ? "es." : ".") << endl;
++nPasteles;
cout << endl << endl
<< "Hay " << ((nPasteles > 1) ? "unos " : "un ") << nPasteles << " pastel"
<< ((nPasteles > 1) ? "es." : ".") << endl;
cout << endl << endl << endl;
}

// IH38.cpp: The switch statement. La instrucción switch. switch-case-break-default.


//
// Las palabras "switch" y "case" son palabras claves del lenguaje c++.
// "case 1" indica al procesador que si la variable que se está comprobando tiene
// el valor "1" (valor entero), entonces se ejecutarán las instrucciones siguientes
// hasta encontrar la palabra clave "break". El valor entero es constante.
// Los valores numéricos enteros no tienen ningún orden preestablecido.
// No se permiten valores enteros repetidos, pues el compilador se vería frente a
// una confusión.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
int seleccion{};
cout << "Le presentamos nuestro menú. Seleccione el que más le guste." << endl << endl;
cout << "<1> Huevos cocidos." << endl << endl;
cout << "<2> Huevos fritos." << endl << endl;
37

cout << "<3> Huevos revueltos." << endl << endl;


cout << "<4> Huevos en torta." << endl << endl;
cin >> seleccion;
cout << endl << endl;
switch (seleccion)
{
case 1:
cout << "Hierva un par de huevos.";
break;
case 2:
cout << "Haga dos huevos fritos.";
break;
case 3:
cout << "Revuelva dos huevos grandes.";
break;
case 4:
cout << "Entorte dos huevos pequeños.";
break;
default:
cout << "Usted seleccionó mal.";
break;
}
cout << endl << endl << endl;
}
38

// IH39.cpp: Sharing the cases. Compartir las sentencias case.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char letra{};
cout << "Escriba una letra vocal. ";
cin >> letra;
cout << endl << endl;
switch( letra * (letra >= 'a' && letra <= 'Z'))
{
case 'a': case 'e': case 'i': case 'o': case 'u':
cout << "Usted ha escrito una letra vocal. Bien.";
break;
case 0:
cout << "Su letra no es una vocal.";
break;
default:
cout << "Usted ha escrito una consonante.";
}
cout << endl << endl << endl;
}
39

// IH40.cpp: Unconditional branching. La sentencia de redireccionamiento incondicional. Goto.


//

#include <iostream>

using namespace std;

int main()
{
system("color FD");

cout << endl << endl << endl;

int miNum{ 0 };

miEtiq: cout << "Esta es mi etiqueta. Aquí se reinicia el procedimiento." << endl << endl;

char miLetra{ 'C' };

if (miNum == 0)
miNum += 5;
else
return 0;

cout << "Este es el numero: " << miNum << endl << endl;

goto miEtiq;

cout << endl << endl << endl;


}
40

// IH41.cpp: Loop with if and goto. Los ciclos con las sentencias if y goto.
//

#include <iostream>

using namespace std;

int main()
{
system("color FD");

cout << endl << endl << endl;

int i{ 1 };
int suma{};

const int MAX{ 10 };

loop:
suma += i;

if (++i <= MAX)


goto loop;

cout << "Suma = " << suma << endl << endl
<< "i = " << i;

cout << endl << endl << endl;

}
41

// IH42.cpp: The for loop. El ciclo for.


//

#include <iostream>

using namespace std;

int main()
{
system("color FD");

cout << endl << endl << endl;

int i{ 1 };
int suma{};

const int MAX{ 10 };

for (i = 1; i <= MAX; i++)


suma += i;

cout << "La suma de los primeros 10 dígitos es: " << suma
<< endl << endl << "i es ahora: " << i;

cout << endl << endl << endl;


}
42

// IH43.cpp: Using multiple counters. El uso de contadores múltiples. For loop. Ciclo for.
//

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
system("color FD");

cout << endl << endl << endl;

const long max{ 10L };

for (long i{}, potencia{ 1L }; i <= max; i++, potencia += potencia)


cout << setw(10) << i << setw(10) << potencia << endl;

cout << endl << endl << endl;

}
43

// IH44.cpp: The indefinite for loop. El ciclo for infinito.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double valor{}; // Cada dato que se sumará y sacará el promedio.
double suma{}; // Suma acumulada de los datos.
int i{}; // Número de datos.
char continuar{ 'n' }; // Selección de continuar.

for (;;) // Loop infinito.


{
cout << endl << "Escriba uno de los datos: ";
cin >> valor;
++i;
suma += valor;
cout << endl << "¿Desea Usted agregar otro dato? <Escriba y o n> ";
cin >> continuar;
if ((continuar == 'n') || (continuar == 'N'))
break; // Salir del loop.
}

cout << endl << endl << "El promedio de los datos es: " << suma / i;

cout << endl << endl << endl;


}
44

// IH45.cpp: The continue statement. La sentencia continue.


//

#include <iostream>

using namespace std;

int main()
{
system("color FD");

cout << endl << endl << endl;

int valor{};
int producto{ 1 };

for (int i{ 1 }; i <= 10; i++)


{
cout << "Escriba un número entero: ";
cin >> valor;

if (0 == valor) // Es mejor usar esta notación.


continue;

producto *= valor;
}

cout << endl << "El producto es " << producto;

cout << endl << endl << endl;


}
45

// IH46.cpp: Using other types in loops. El uso de otros tipos de datos en los ciclos.
//

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
system("color FD");

cout << endl << endl << endl;

for (char mayusc{ 'A' }, minusc{ 'a' }; mayusc <= 'Z'; mayusc++, minusc++)
{
cout << endl
<< "\t" << mayusc // Imprimir el carácter.
<< hex << setw(10) << static_cast<int>(mayusc) // luego en hexadecimal.
<< dec << setw(10) << static_cast<int>(mayusc) // luego en decimal.
<< " " << minusc // Letra minúscula.
<< hex << setw(10) << static_cast<int>(minusc) // luego en hexadecimal.
<< dec << setw(10) << static_cast<int>(minusc); // luego en decimal.
}

cout << endl << endl << endl;


}
46

// IH47.cpp: Floating point loop counters. Contadores de ciclo de punto flotante.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double a{ 0.3 };
double b{ 2.5 };
for (double x{}; x <= 2.0; x += 0.25)
cout << "\n\tx = " << x << "\ta * x + b = " << a * x + b;
cout << endl << endl << endl;
}

// IH48.cpp: The while statement. La cláusula while.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double valor{};
double suma{};
int i{};
char continuar{ 'y' };
while(('y' == continuar) || ('Y' == continuar))
{
cout << endl
<< "Escriba una cantidad: ";
cin >> valor;
47

++i;
suma += valor;
cout << endl
<< "¿Desea introducir otra cantidad más? Escriba <y> o <n>. ";
cin >> continuar;
}
cout << endl << endl
<< "El promedio de las " << i << " cantidades que ha escrito, es: "
<< suma / i;
cout << endl << endl << endl;
}

// IH49.cpp: The while(true) loop. El ciclo while(true).


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double valor{};
double suma{};
int i{};
char continuar{ 's' };
while (true)
{
cout << endl << "Escriba una cantidad. ";
cin >> valor;
suma += valor;
++i;
cout << endl << "¿Desea continuar? Escriba <s> para sí; <n> para no. ";
cin >> continuar;
48

if (('s' == continuar) || ('S' == continuar))


continue;
else
break;
}
cout << endl << "La suma de los " << i << " datos que Usted ha escrito es: " << suma
<< endl << "El promedio de los datos es: " << suma / i;
cout << endl << endl << endl;
}

// IH50.cpp: The do-while loop. El ciclo do-while.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char continuar{ 'y' };
int i{};
double valor{};
double suma{};
do
{
cout << endl
<< "Escriba una cantidad. ";
cin >> valor;
suma += valor;
++i;
cout << "\n¿Quiere escribir otra cantidad más? Escriba <y> para sí; <n> para no. ";
cin >> continuar;
} while (('y' == continuar) || ('Y' == continuar));
49

cout << endl << endl << "La suma de los " << i << " números es: " << suma;
cout << endl << endl << "El promedio de los datos es: " << suma / i;

cout << endl << endl << endl;


}

// IH51.cpp: The range-based for loop. El ciclo for basado en rango.


//
// Esta es sólo una ubicación temporal, con el propósito de recordar al usuario de
// que este tipo de ciclo pertenece también al conjunto de ciclos.
// El ciclo basado en rango requiere el uso de una colección de objetos o elementos,
// ordenados, dentro de los cuales el ciclo estará iterando.
// Estos se verán posteriormente.
//
#include <iostream>
int main()
{
std::cout << "Hola todos!\n";
}

// IH52.cpp: Nested loops. Ciclos anidados.


//
// Se puede ejecutar un ciclo dentro de otro ciclo mayor. A esto se le llama
// "ciclos anidados".
// Se mostrará aquí el uso de los ciclos anidados aplicando los mismos a la
// solución de los números factoriales.
// Un número factorial es una cantidad entera que resulta del producto de
// todos los números enteros inferiores a ella.
// El factorial de 5 es el producto de 5 x 4 x 3 x 2 x 1. El total es 120.
// El último factorial correcto que se puede usar es el del número 12.
// Factoriales mayores que 12 producen error.
50

//
#include <iostream>
using namespace std;

int main()
{
system("color FD");
cout << endl << endl << endl;
char continuar{ 'n' };
long valor{};
long factorial{};
do
{
cout << endl
<< "Escriba un valor numérico entero al cual desea obtener su factorial. ";
cin >> valor;
if (13 <= valor)
{
cout << endl << endl
<< "El número que Usted ha seleccionado producirá error.\n\n";
continue;
}
factorial = 1L;
for (long i{ 2L }; i <= valor; i++)
factorial *= i;
cout << endl
<< "El factorial de " << valor << " es " << factorial;
cout << endl
<< "\n¿Desea obtener el factorial de otro número? ";
cin >> continuar;
} while (('y' == continuar) || ('Y' == continuar));
cout << endl << endl << endl;
}
51

// IH53.cpp: Using nested loops to generate a multiplication table. El uso de los ciclos
// anidados para generar una tabla de multiplicar.
//
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
system("color FD");
const int tam{ 18 };
int i{};
int j{};
cout << endl
<< tam << " por " << tam << " Tabla de Multiplicar\n\n";
cout << endl << " |";
for (i = 1; i <= tam; i++)
cout << setw(3) << i << " ";
cout << endl;
for (i = 0; i <= tam; i++)
cout << "_____";
for (i = 1; i <= tam; i++) // Ciclo exterior para las filas
{
cout << endl
<< setw(3) << i << " |"; // Imprimir la etiqueta de la fila
for (j = 1; j <= tam; j++) // Ciclo interior para el resto de la fila
cout << setw(3) << i * j << " "; // Final del ciclo interior
} // Final del ciclo exterior
cout << endl << endl << endl;
}
52

// IH54.cpp: Arrays. Arreglos o matrices.


//
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
long altura[6]; // Matriz de 6 datos de altura: altura[0], altura[1]...
/*
Un valor de tipo "long" ocupa 4 bytes, por lo que
toda la matriz requiere 24 bytes.
*/
double tam_maquinas[10];
double caballos_de_fuerza[10];
const int maX{ 20 }; // Máximo número de elementos de las matrices.
double gas[ maX ];
double millas[ maX ];
int cuenta{};
char continuar{ 'y' };
while (('y' == continuar) || ('Y' == continuar) && cuenta < maX)
{
cout << endl
<< "Escriba la cantidad de gasolina: ";
cin >> gas[cuenta];
cout << endl
<< "Escriba la lectura del odómetro: ";
cin >> millas[cuenta];
++cuenta;
cout << endl
<< "¿Desea ingresar otro par de datos? <y> para si; <n> para no. ";
cin >> continuar;
53

}
if (cuenta <= 1)
{
cout << endl
<< "Lo siento mucho. Se necesita por lo menos dos datos.";
return 0;
}
// Salida de datos para los últimos datos de 2 en adelante.
for (int i{ 1 }; i < cuenta; i++)
{
cout << endl
<< setw(2) << i << "."
<< " Gasolina adquirida = " << gas[i] << " galones, "
<< "rindió " << (millas[i] - millas[i - 1]) / gas[i] << " millas por galón.";
}
cout << endl << endl << endl;
}

// IH55.cpp: Initializing arrays. La inicialización de las matrices.


//
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
int maquinas_tam[5]{ 100, 200, 300, 400, 500 };
/* Se declara la matriz de datos enteros "maquinas_tam",
la cual tiene 5 elementos: maquinas_tam[0], etc.
Los valores iniciales de la matriz son: maquinas_tam[0]
con el valor 100, maquinas_tam[1] con valor 200, etc.
54

*/
char alfabeto[28];
/* El arreglo o matriz alfabeto[] tiene 28 elementos. Los
valores iniciales no se incluyeron, por lo tanto tendrán
"basura informática", es decir, dada su dirección inicial
los registros sucesivos contendrán los bits que hayan
quedado como residuos de operaciones con la RAM.
*/

char vocales[5]{};
/* La matriz vocales[] tiene 5 elementos. Los elementos se
crean sin valores iniciales. En blanco.
*/

long datos[100]{};
/* Se inicializa la matriz datos[100] con valores 0.
Los elementos de la matriz van desde datos[0] hasta
datos[99]. */
int valor[] { 3, 4, 5 };
/* Matriz valor[], con los valores iniciales 3, 4, 5. La
matriz tendrá 3 elementos, asignados por el compilador
porque no se declaró la cantidad de elementos.
*/
int basura[5]{ 1, 2, 3 };
int basura2[5];
for (int i{}; i < 5; i++)
cout << endl
<< "Valores en basura: " << setw(12) << basura[i];
cout << endl << endl;
for (int i{}; i < 5; i++)
{
cout << endl
<< "Valores en basura2: " << setw(12) << basura2[i];
55

}
cout << endl << endl << endl;
for (int i{}; i <= 5; i++)
cout << endl
<< "Valores en basura: " << setw(12) << basura[i];
cout << endl << endl;
for (int i{}; i <= 5; i++)
{
cout << endl
<< "Valores en basura2: " << setw(12) << basura2[i];
}
cout << endl << endl << endl;
}

// IH56.cpp: Using the range-based for loop. El uso del ciclo for basado en el rango. (1)
//
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double temperaturas[]{ 65.5, 68.0, 75.0, 77.5, 76.4, 73.8, 80.1 };
double suma{};
int cuenta{};
for (double t : temperaturas)
{
suma += t;
++cuenta;
}
double promedio = suma / cuenta;
56

cout << "Los valores de temperatura fueron: \n";


for (double t : temperaturas)
{
cout << setw(12) << t;
}
cout << endl << endl
<< "La suma de las temperaturas es: " << suma
<< endl << "El promedio de las temperaturas es: " << promedio;
cout << endl << endl << endl;
}

// IH57.cpp: Using the range-based for loop. El uso del ciclo for basado en el rango. (2)
// Auto keyword.
// _countof() Microsoft c++ function. Esta última función no está habilitada en
// VS2019.
//
#include <iostream>
#include <iomanip>
// #include <cstdlib> Ya está incluida esta librería en iostream.
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double temperaturas[]{ 65.5, 68.0, 75.0, 77.5, 76.4, 73.8,80.1 };
double suma{};
int cuenta{};
for (auto temp : temperaturas)
{
suma += temp;
++cuenta;
}
57

double promedio = suma / cuenta;


cout << "Los valores de temperatura fueron: \n";
for (auto t : temperaturas)
{
cout << setw(12) << t;
}
cout << endl << endl
<< "La suma de las temperaturas es: " << suma
<< endl << "El promedio de las temperaturas es: " << promedio;
cout << endl << endl << endl;
}

// IH58.cpp: Multidimensional arrays. Matrices multidimensionales.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double frijol[12][10];
frijol[2][4] = 10.7;
double arroz[3][14][7];
arroz[0][0][0] = 9.8;
cout << endl << endl << endl;
}
58

// IH59.cpp: Multidimensional arrays. Matrices multidimensionales. cin.getline()


//
#include <iostream>
#include <string>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char palabra[2][80];
int maX{ 80 };
cout << "Escriba dos oraciones con un máximo de 80 caracteres.\n";
cout << "Para finalizar la frase, use <Enter>.\n\n";
cin.getline(palabra[0], maX, '\n'); // No basta escribir sólo el nombre de
// la matriz. Debe escribirse por lo
// menos la primera dimensión.
cin.getline(palabra[1], maX, '\n'); // No basta escribir sólo el nombre de
// la matriz. Debe escribirse por lo
// menos la primera dimensión.
cout << "\n\n\nLas oraciones que Usted ha escrito son:\n\n";
cout << palabra[0] << endl << palabra[1];
cout << endl << endl << endl;
}

// IH60.cpp: Initializing multidimensional Arrays. La inicialización de las matrices


// multidimensionales.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
59

cout << endl << endl << endl;


char palabra[2][8]{
{'a', 'l', 'c', 'i', 'd', 'e', 's'},
{'i', 'v', 'a', 'n'}
};
int numeros[10][5]{}; // La matriz se inicializa con valores 0.
cout << endl << endl << endl;
}

// IH61.cpp: Using multidimensional matrices. El uso de las matrices multidimensionales.


// setiosflags(ios::fixed). setprecision(2). for (double prom : promedios)
//
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
const int plantas_xhilera{ 6 }; // Cantidad de plantas en una hilera.
double frijoles[][plantas_xhilera]{ // Producción por cada planta.
{12, 15},
{0, 10, 16, 0, 13, 4},
{8, 7, 10, 12, 5},
{9, 8, 11, 4, 16}
};
double promedios[_countof(frijoles)]{}; // Guarda los promedios de producción.
for (int fila{}; fila < _countof(frijoles); ++fila)
{
for (int planta{}; planta < plantas_xhilera; ++planta)
{
promedios[fila] += frijoles[fila][planta];
60

}
promedios[fila] /= plantas_xhilera;
}
cout << "La producción promedio por fila es: "
<< setiosflags(ios::fixed) // Salida con punto fijo.
<< setprecision(2) // Con dos dígitos decimales.
<< endl << endl;
int n{}; // Número de fila.
for (double prom : promedios)
cout << "Fila" << ++n << setw(10) << prom << endl;
cout << endl << endl << endl;
}

// IH62.cpp: Declaring pointers. La declaración de punteros. The address-of operator, &.


// El operador de dirección de, &. The reference operator. El operador de referencia.
// The indirection operator, *. El operador de indirección, *.
// The dereference operator, *. El operador de dereferencia, *.
// De-referencing the pointer. La dereferenciación del puntero.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
long numero{ 0L }; // Declaración de la variable del tipo Long, "numero".
// Se inicializa la variable con el dato 0L.
long* rNumero; // Declaración del puntero del tipo Long, "rNumero".
long* rNumero2, numero2{ 99 }; // Declaración de dos variables tipo Long.
rNumero = &numero; // Al puntero rNumero se le asigna la dirección de
// "numero".
rNumero2 = &numero2; // Al puntero rNumero2 se le asigna la dirección de
61

// "numero2".
int numero3{ 9 }; // Declaro la variable entera numero3, con el valor
// inicial 9.
int* rNumero3{ &numero3 }; // Declaro el puntero tipo int a la variable numero3.
int* rNulo{ nullptr }; // Declaro el puntero tipo int como puntero nulo, sin
// dirección.
int* rNulo2{}; // Igual.
if (rNulo == nullptr)
cout << "El puntero rNulo no está apuntando a ninguna variable, es nullptr." << endl
<< endl;

if (!rNulo2)
cout << "El puntero rNulo2 no está apuntando a ninguna variable, es nullptr." << endl
<< endl;
// rNulo2 es nullptr. nullptr se convierte únicamente en forma
// implícita a bool (true or false).
cout << endl << endl << endl;
}

// IH63.cpp: Pointer operations. Operaciones con punteros.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
long* rNumero{}; // Declara puntero tipo long, nullptr.
long numero1{ 55 }, numero2{ 99 }; // Declara variables tipo long, con datos.

rNumero = &numero1; // Asigna la dirección de la variable numero1 al puntero tipo


// long rNumero.
*rNumero += 11; // Incrementa el contenido de la dirección de memoria del
62

// puntero rNumero en 11. El cambio es en el contenido, y


// no afecta a la dirección de memoria.
cout << "El valor inicial de la variable tipo long, numero1 fue de 55.\n";
cout << "Ahora, el valor de numero1 es: " << numero1 << " y su dirección de memoria "
<< "rNumero o &numero1, es: " << hex << rNumero;
rNumero = &numero2; // Asigna la dirección de la variable numero2 al puntero
// tipo long rNumero.
numero1 = *rNumero * 10; // Multiplica el contenido de la dirección rNumero por 10.
cout << endl << endl
<< " numero1 = " << dec << numero1
<< " rNumero = " << hex << rNumero
<< " *rNumero = " << dec << *rNumero;
cout << endl << endl << endl;
}

// IH64.cpp: Pointers of type const char*. Punteros del tipo const char*.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
const char* proverbio{ "Un desacierto es tan bueno como una milla." };
/* Esto tiene la apariencia de que se está inicializando una matriz de caracteres.
La matriz de caracteres, tipo lenguaje c, tiene un carácter final '\0' para se-
ñalar el final de la matriz. Ese carácter no está incluido dentro de la cadena
de caracteres escrita entre comillas y dentro de las llaves.
La instrucción crea una cadena de caracteres, en forma de una matriz del tipo
const char[]. El compilador agrega el carácter '\0' al final de la cadena.
La cadena alfanumérica tiene una dirección en la memoria, que da la ubicación
del primer carácter. El puntero "proverbio" contiene esa dirección.
63

*/
cout << *proverbio; // Solo imprime la "U".
cout << endl << endl
<< proverbio; // Imprime todo.
cout << endl << endl << endl;
}

// IH65.cpp: Initializing pointers with strings. La inicialización de punteros con cadenas.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
const char* rCad1{ "Sofía Loren" };
const char* rCad2{ "Salma Hayek" };
const char* rCad3{ "Talía" };
const char* rCad4{ "Cameron Díaz" };
const char* rCad5{ "Charlize Theron" };
const char* rCad6{ "Elizabeth Taylor" };
const char* rCad{ "\n\nSu estrella preferida es " };
int cubo{};
cout << endl
<< "Escoja su actriz preferida.\n"
<< "Escriba un número entre 1 y 6: ";
cin >> cubo;
switch (cubo)
{
case 1: cout << rCad << rCad1;
break;
case 2: cout << rCad << rCad2;
64

break;
case 3: cout << rCad << rCad3;
break;
case 4: cout << rCad << rCad4;
break;
case 5: cout << rCad << rCad5;
break;
case 6: cout << rCad << rCad6;
break;
default: cout << "\n\nBueno, Usted no tiene una actriz preferida.";
}
cout << endl << endl << endl;
}

// IH66.cpp: The sizeof operator. El operador sizeof.


//
// Este operador genera una cantidad entera, del tipo "size_t", uno de los
// tipos de datos fundamentales definidos en la librería estándar del c++.
// El operador sizeof devuelve la cantidad de bytes (octetos) que se usan
// para representar el operando en la memoria.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
int dado{};
cout << "El número de bytes de un entero es: " << sizeof dado;
// Saca el valor numérico 4, el número de bytes de un tipo de dato int.

//----------------------------------------------------------------------
65

const char* rCad[]{ "Sofía Loren", // Inicializar un puntero tipo char*.


"Salma Hayek",
"Talía",
"Cameron Díaz",
"Charlize Theron",
"Elizabeth Taylor"
};
const char* rIni{ "Su actriz preferida es " };
cout << "\n\n\nEl tamanho de la matriz rCad es: " << (sizeof rCad) / (sizeof rCad[0]);

//----------------------------------------------------------

double temperaturas[]{ 65.5, 68.0, 75.0, 77.5, 76.4, 73.8, 80.1 };


double suma{};
for (auto t : temperaturas)
suma += t;
double promedio = suma / ((sizeof temperaturas) / (sizeof temperaturas[0]));
cout << "\n\n\nLa media de las temperaturas es: " << promedio;

//----------------------------------------------------------

size_t long_size{ sizeof(long) };

cout << "\n\n\nEl número de bytes de una variable tipo long es: " << long_size;

cout << endl << endl << endl;


}
66

// IH67.cpp: Constant pointers and pointers to constants. Punteros constantes


// y punteros a constantes.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
// Almacenar la dirección de memoria de las cadenas de caracteres, del tipo
// de datos matriz de caracteres constantes, en los punteros constantes.
const char* rCad[]{ "Sofía Loren",
"Salma Hayek",
"Talía",
"Cameron Díaz",
"Charlize Theron",
"Elizabeth Taylor"
};
/* Las cadenas y los punteros son constantes. No se pueden modificar.
Sin embargo, se puede asignar un puntero a otro puntero.
*/
rCad[0] = rCad[1];
cout << "Los primeros dos elementos de la matriz rCad son: " << rCad[0]
<< "\t" << rCad[1];
//------------------------------------------------------------------
// Crear matriz de punteros constantes a cadenas constantes de caracteres.
const char* const rCad2[] = { "Sofía Loren",
"Salma Hayek",
"Talía",
"Cameron Díaz",
"Charlize Theron",
"Elizabeth Taylor"
};
67

/* Con ambos aspectos constantes, no se puede modificar ni la cadena de


caracteres, ni las direcciones de memoria, ni los punteros.
Con respecto a punteros y objetos, podemos tener un puntero a un objeto
constante, un puntero constante a un objeto no constante, y un puntero
constante a un objeto constante.
*/
// rCad2[0] = rCad2[3]; // Esta instrucción indica error.

cout << endl << endl << endl;


}

// IH68.cpp: Pointers and arrays. Punteros y matrices.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double* rDatos{}; // Puntero tipo doble a una matriz.
double* rData{}; // Puntero tipo doble a una matriz.
double datos[5]; // Definición de matriz datos con 5 elementos tipo doble.
rDatos = datos; // Inicializar el puntero con la dirección de memoria de la
// matriz.
rData = &datos[1]; // Inicializar el puntero con la dirección de memoria del
// segundo elemento de la matriz datos.
cout << endl << endl << endl;
}
68

// IH69.cpp: Pointer arithmetic. Aritmética de punteros.


//
// A los punteros sólo le podemos sumar o restar.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double* rDatos{}; // Puntero tipo doble a una matriz.
double* rData{}; // Puntero tipo doble a una matriz.
double* rData2{};
double datos[5]; // Definición de matriz datos con 5 elementos tipo doble.

rDatos = datos; // Inicializar el puntero con la dirección de memoria de la


// matriz.
rData = &datos[1]; // Inicializar el puntero con la dirección de memoria del
// segundo elemento de la matriz datos.
rData = &datos[2];
rData += 1; // Incrementar rData al elemento que sigue, datos[3]. Como
// el tipo dato de datos[] es double, la dirección de memoria
// en rData.
// aumentará en 4 bytes. El 1 se refiere a la cantidad de
// elementos.
// El cambio es n*sizeof(double) bytes.
cout << endl << endl << endl;
}
69

// IH70.cpp: Pointer arithmetic. Aritmética de punteros. Continuación.


//
// A los punteros sólo le podemos sumar o restar.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double* rDatos{}; // Puntero tipo doble a una matriz.
double datos[5]; // Definición de matriz datos con 5 elementos tipo doble.
rDatos = &datos[1]; // Inicializar el puntero con la dirección de memoria del
// segundo elemento de la matriz datos.
rDatos = &datos[2]; // Cambiar el valor del puntero rDatos al tercer elemento.
rDatos += 1; // Incrementar rDatos al elemento que sigue, datos[3]. Como
// el tipo de dato de datos[] es double, la dirección de memoria
// en rDatos aumentará en 4 bytes. El 1 se refiere a la cantidad
// de elementos.
// El cambio es n*sizeof(double) bytes.
rDatos -= 2; // Decrementar el puntero rDatos en dos elementos.
rDatos++; // Incrementar rDatos al siguiente elemento de la matriz. Esta
// instrucción es idéntica a la instrucción anterior += 1.
*(rDatos + 1) = *(rDatos + 2);
/* Aquí se usó derefenciar el puntero rDatos. Se supone que el puntero está ubicado ahora en
datos[2].
Por ello, esta instrucción es equivalente a:
data[3] = data[4];
El contenido del elemento 4 será ahora el contenido del elemento 5.
Los paréntesis son necesarios cuando se quiere dereferenciar un puntero después de
incrementar la dirección del mismo.
El nombre de una matriz se puede usar como si fuera un puntero que provee la
Dirección de memoria de los elementos de la matriz. *(rDatos + 1) equivale a datos[1]
si rDatos está al inicio.
70

*/
cout << endl << endl << endl;
}

// IH71.cpp: Pointers as array names. Punteros como nombres de matrices.


//
// Programa para calcular números primos.
//
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
const int maX{ 1000 }; // Calcular 1000 números primos.
long primos[maX]{ 2, 3, 5 }; // Definir primeros 3 elementos.
long prueba{ 5 }; // Número primo candidato.

int cuenta{ 3 }; // Números primos encontrados.


bool hallado{ false }; // Indicador de si se halló un número primo.
do {
prueba += 2; // Avanza de 2 en 2 (sólo números impares).
hallado = false;
for (int i{}; i < maX; i++) // Criterio de prueba: divisibilidad.
{
hallado = (prueba % *(primos + i)) == 0; // Si es divisible...
if (hallado)
break; // No es un número primo.
}
if (!hallado)
*(primos + cuenta++) = prueba; // Agregar el número primo a la matriz.
71

} while (cuenta < maX);


// Imprimir en pantalla los números primos.
for (int i{}; i < maX; i++)
{
if (i % 5 == 0) // 5 números por línea.
cout << endl;
cout << setw(10) << *(primos + i);
}
cout << endl << endl << endl;
}

// IH72.cpp: Counting string characters using a pointer. Contar los caracteres de una cadena
// usando un puntero.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
const int maX{ 80 }; // Máximo número de caracteres por cadena.
char buffer[maX]; // Buffer de entrada desde el teclado.
char* rBuffer{ buffer }; // Puntero a la primera dirección de memoria de la matriz
// buffer.
cout << endl
<< "Escriba una cadena de menos de 80 caracteres.\n\n";
cin.getline(rBuffer, maX, '\n'); // Leer la cadena hasta '\n'. La cadena se lee hasta que el
// usuario aplica <Enter>. rBuffer tiene el valor del 1-ésimo
// carácter.
while (*rBuffer) // Continuar leyendo hasta '\0'. Se sigue leyendo rBuffer++;
// hasta el n-ésimo carácter de la cadena. Así, rBuffer llega
// al número total de caracteres.
72

// Es posible leer más sencillo así:


// while(*rBuffer++);
// Sólo contiene la condición de prueba. Al finalizar, rBuffer
// contendrá la cantidad de caracteres más 1.
// Porque lee el último carácter '\0' de fin de cadena.
cout << endl
<< "La cadena \"" << buffer << "\" tiene " << rBuffer - buffer << " caracteres.";
/* Sólo aquí se usa la cadena buffer. En lo anterior, se usó el puntero a la cadena. La
expresión buffer++ es ilegal porque modifica el puntero de inicio de la cadena, el
cual debe usarse como un valor constante.
*/
cout << endl << endl << endl;
}

// IH73.cpp: Using pointers with multidimensional arrays. El uso de punteros con matrices
// multidimensionales.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double frijoles[3][4]; // Matriz de dos dimensiones.
double* rFrijol; // Puntero rFrijol de tipo double.
rFrijol = &frijoles[0][0]; // Al puntero se le asigna la dirección del primer
// elemento de la matriz.
/* Asignar punteros por filas y columnas. */
double* rFrjF1; // Puntero de tipo double a la primera fila.
double* rFrjF2; // Puntero de tipo double a la segunda fila.
double* rFrjF3; // Puntero de tipo double a la tercera fila.
double* rFrjC1; // Puntero de tipo double a la primera columna.
73

double* rFrjC2; // Puntero de tipo double a la segunda columna.


double* rFrjC3; // Puntero de tipo double a la tercera columna.
double* rFrjC4; // Puntero de tipo double a la cuarta columna.
rFrjF1 = &frijoles[0][0]; // Al puntero se le asigna la dirección del primer
// elemento de la matriz. Fila 1.
rFrjF2 = &frijoles[1][0]; // Al puntero se le asigna la dirección del primer
// elemento de la matriz. Fila 2.
rFrjF3 = &frijoles[2][0]; // Al puntero se le asigna la dirección del primer
// elemento de la matriz. Fila 3.
/* MMM: rFrijol = &frijoles causará un error. rFrijol es un puntero o dirección del tipo
double, mientras que &frijoles es un puntero o dirección del tipo double [3][4],
doble bidimensional. */
double(*rFrj)[4]; // Este es un puntero doble bidimensional. Los
// paréntesis son esenciales acá.
rFrj = frijoles; // MMM: El compilador se encargará de determinar
// el tipo de dato del puntero y de la matriz.
// Verificará si son compatibles y decidirá.
// Si se agrega "auto", el compilador dará
// error.
// auto rFrj = frijoles;
//----------------------------------------------------------------------------------
// Notación de punteros con matrices multidimensionales.
frijoles[0][0] = 5.673; // Asignar el valor doble 5.673 al primer elemento.
frijoles[0][1] = 1.004; // Asignar el valor doble 1.004 al segundo elemento.
*(*(frijoles + 0) + 2) = 2.809; // Asignar el valor doble 2.809 al tercer elemento.
*(*(frijoles + 0) + 3) = 7.112; // Asignar el valor doble 7.112 al cuarto elemento.
// *(frijoles + 0) se refiere a la dirección de la fila 0.
// *(*(frijoles + 0) + 3) se refiere a la cuarta columna
// de la fila 0.
*(frijoles[1] + 0) = 4.201; // Asignar el valor doble 4.201 al quinto elemento.
// Este elemento es el primer elemento de la
// segunda fila.
// También es el segundo elemento de la primera
74

// columna.
*(frijoles[1] + 1) = 3.992; // Asignar el valor doble 3.992 al sexto elemento.
// Este elemento es el segundo elemento de la
// segunda fila.
// También es el segundo elemento de la segunda
// columna.
(*(frijoles + 1))[2] = 6.337; // Asignar el valor doble 6.337 al séptimo elemento.
// Este elemento es el tercer elemento de la segunda
// fila.
// También es el segundo elemento de la tercera
// columna.
(*(frijoles + 1))[3] = 0.995; // Asignar el valor doble 0.995 al octavo elemento.
// Este elemento es el cuarto elemento de la
// segunda fila.
// También es el segundo elemento de la cuarta
// columna.
cout << endl << endl << endl;
}

// IH74.cpp: Dynamic memory allocation. Asignación dinámica de memoria.


//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double* rValor{}; // Declarar un puntero de tipo double, sin asignar.
rValor = new double; // Solicitar memoria dinámica para el puntero.
*rValor = 9999.99; // Dereferenciar el puntero y asignarle un valor a la variable.
// Si no hay suficiente memoria dinámica, el comando new
// nos dará un mensaje de error mediante una EXCEPCION.
75

// Inicializar la variable creada con new, al momento de crearla.


double* rValor2{ nullptr }; // Declarar un puntero de tipo double, con valor nullptr.
rValor2 = new double{ 555.55 }; // Usar memoria dinámica para el valor double.
// Hacer todo en un solo proceso.
double* rValor3{ new double{6734.22} }; // Crear el puntero tipo double, usar memoria
// dinámica, poner el valor 6734.22
//-------------------------------------------------------------------------
// Todas las variables creadas con new, usando memoria dinámica, deben borrarse
// antes de salir.
delete rValor;
delete rValor2;
delete rValor3;
cout << endl << endl << endl;
}

// IH75.cpp: Allocating memory for an array dynamically. La asignación de memoria para una
// matriz en forma dinámica.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
// Para declarar una matriz de tipo char, ubicada en la memoria RAM disponible,
// con 20 caracteres:
char* rCad{ new char[20] };
// Para borrar la matriz:
delete[] rCad;
/* Note que se usa "delete[]", con un par de corchetes rectos, para hacer la
diferenciación de que se está borrando una matriz y no una variable de
memoria.
76

Para borrar matrices en la memoria dinámica, siempre deberán incluirse los


corchetes, para evitar confusiones en el uso de la memoria por el compilador.
No es necesario especificar la dimensión o dimensiones de la matriz.
*/
// Cuando se usa delete para descartar la memoria que se ha usado para guardar
// una matriz mediante un puntero, al puntero debe resetearse, así:
rCad = nullptr;
/* Esto asegura que la dirección de memoria apuntada por el puntero no estará
disponible.
*/
//----------------------------------------------------------------------------
// Se puede declarar e inicializar una matriz creada mediante memoria dinámica
// (MD).
int* rNum{ new int[10] {2,3,4} };
/* Esta instrucción declara la matriz dinámica apuntada por rNum, de tipo entero,
con una cantidad de elementos de 10, de los cuales se declaran los valores de
los primeros tres, y los restantes siete tendrán el valor 0.
*/
cout << endl << endl << endl;
}

// IH76.cpp: Smart pointers. Punteros inteligentes.


//
// Se incluye este ejercicio en blanco aquí para que quede registrado
// que también existe este tipo de punteros.
// El ejercicio se desarrollará en el capítulo 10.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
77

cout << endl << endl << endl;

cout << endl << endl << endl;


}

// IH77.cpp: Using free store. El uso de la tienda libre (memoria dinámica). Calculating primes
// using dynamic memory allocation. Calcular números primos usando la memoria
// dinámica.
//
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
int maX{}; // maX contiene la cantidad de números que se desea.
cout << endl
<< "Escriba la cantidad de números primos que desea. Q > 3. ";
cin >> maX;
// Asegurarse de que maX sea mayor que 3.
if (maX < 4)
maX = 4;
// Declarar una matriz inicial de números primos. Se ocupan tres valores semilla.
long* rPrimos{ new long[maX] {2L, 3L, 5L} };
long numPrueba{ 5L }; // Candidato a número primo.
int cuenta{ 3 }; // Conteo de números primos hallados.
bool hallado{ false }; // Indica cuando un número primo es encontrado.
do
{
numPrueba += 2L; // Probar sólo números impares. > 5.
hallado = false;
78

for (int i{}; i < cuenta; i++)


{
hallado = (numPrueba % *(rPrimos + i)) == 0;// Será verdadero (true) cuando
// divida sin residuo.
if (hallado) // numPrueba tiene divisores. No es número primo.
break; // Probar el número impar siguiente.
}
if (!hallado) // Si hallado es falso (false)
*(rPrimos + cuenta++) = numPrueba;// Guardarlo en la matriz de números
// primos.
} while (cuenta < maX);
// Imprimir en pantalla 5 números primos a la vez.
for (int i{}; i < maX; i++)
{
if (i % 5 == 0) // Si no se han impreso 5, 10, 15...
cout << endl;
cout << setw(10) << *(rPrimos + i);
}
delete[] rPrimos; // Liberar espacio en la memoria dinámica.
rPrimos = nullptr; // Resetear el puntero.
cout << endl << endl << endl;
}

// IH78.cpp: Dynamic allocation of multidimensional arrays. Asignación dinámica


// de matrices multidimensionales.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
79

// Definir puntero tipo double a una matriz de dos dimensiones.


double(*rFrijoles)[4]{};
// Asignar memoria dinámica a la matriz de dos dimensiones.
rFrijoles = new double[3][4];
//--------------------------------------------------------------------------
// Definir el puntero tipo double, la matriz de dos dimensiones, y asignar
// memoria dinámica.
double(*rArroz)[4]{ new double[3][4] };
//--------------------------------------------------------------------------
// Asignar espacio para una matriz tridimensional solo requiere que se
// expresen las dimensiones.
auto rMatrizGde(new double[6][9][20]); // No se puede inicializar una lista con auto.
// La palabra clave auto hace que el compilador determine
// el tipo de puntero en forma automática cuando se
// establezcan los datos. Al indicar new double, se pone que
// el tipo es double.
// También se puede escribir así:
auto rMatrizMed = new double[4][6][8];
//---------------------------------------------------------------------
// Sin importar la cantidad de dimensiones que tenga la matriz, se destruye
// de la misma forma.
delete [] rMatrizGde;
rMatrizGde = nullptr;
//---------------------------------------------------------------------
// Solo la dimensión más a la izquierda en una matriz multidimensional se puede
// poner como variable.
int maX{ 16 };
auto rMatrizPeq = new double[maX][5][8];
cout << endl << endl << endl;
}
80

// IH79.cpp: Using references. El uso de las referencias.


//
// Hay dos tipos de referencias:
// referencias de iValor y referencias de dValor
// Las referencias de iValor se usan como alias para otra variable. Aparecen a la
// izquierda en una operación de asignación.
// Las referencias de dValor además pueden representar otra referencia de dValor.
// Las referencias no se pueden alterar para representar otras variables, no se
// pueden cambiar.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
long numero{};
// Definir una referencia iValor a la variable long numero, usando long&.
long& fNumero{ numero };
// Ahora se puede usar la referencia en vez de la variable numero.
fNumero += 10L; // Se agregó 10 unidades tipo long a la variable numero.
//------------------------------------------------------------------------------------
/* No es posible escribir la instrucción:
int& fCinco{ 5 };
La expresión 5 no es una variable y la instrucción no se compilará. Así
como está declarada, se toma como variable, pero el compilador la tiene
definida como una constante.
*/
const int& fCinco{ 5 }; // Perfecto.
cout << endl << endl << endl;
}
81

// IH80.cpp: Using references. El uso de las referencias. Punteros y referencias.


//
// Hay dos tipos de referencias:
// referencias de iValor y referencias de dValor
// Las referencias de iValor se usan como alias para otra variable. Aparecen a la
// izquierda en una operación de asignación.
// Las referencias de dValor además pueden representar otra referencia de dValor.
// Las referencias no se pueden alterar para representar otras variables, no se
// pueden cambiar.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
long numero{}; // Definición de la variable long numero.
long* rNum{&numero}; // Definir un puntero long a la dirección de numero.
// El puntero long rNum se puede incrementar, así:
*rNum += 10L;
/* Existe una gran diferencia entre el uso de un puntero y el uso de una referencia.
Se tiene que dereferenciar el puntero para tener acceso a la variable a la que
apunta el puntero.
Con una referencia, no es necesario dereferenciar.
La referencia es algo así como un puntero que ya está dereferenciado. Lo único
que está fijo a su variable.
Una referencia iValor es equivalente a la variable a la que está referenciando.
*/
cout << endl << endl << endl;
}
82

// IH81.cpp: Using references in a range-based for loop. El uso de las referencias en un


// ciclo for basado en rango. Temperaturas.
//
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
double temperaturas[]{ 65.5, 68.0, 75.0, 77.5, 76.4, 73.8,80.1 };
double suma{};
int cuenta{};
for (auto t : temperaturas)
{
suma += t;
++cuenta;
}
double promedio = suma / cuenta;
cout << "Los valores de temperatura fueron: \n";
for (auto t : temperaturas)
{
cout << setw(12) << t;
}
cout << endl << endl
<< "La suma de las temperaturas es: " << suma
<< endl << "El promedio de las temperaturas es: " << promedio;
cout << endl << endl;
//-----------------------------------------------------------------------------
// La variable t no hace referencia a ninguno de los elementos de la matriz.
// No se puede usar para modificar el elemento.
// Sólo es posible utilizando una referencia.
const double FtoC{ 5.0 / 9.0 }; // Convierte Fahrenheit a Centígrado.
83

for (auto& t : temperaturas)


t = (t - 32) * FtoC;
cout << "Conversión de temperaturas desde grados Fahrenheit hasta Centígrados: ";
for (auto& t : temperaturas)
cout << " " << t;
// La variable t ahora es del tipo double&. Ahora hace referencia directa a cada
// elemento de la matriz. El ciclo anterior cambia los valores de la matriz, de
// Fahrenheit a Celsius.
// Usar referencias evita el tener que copiar los valores nuevamente.
//------------------------------------------------------------------------------
// Si Usted desea usar referencias con el ciclo for basado en rangos, por razones
// de eficiencia, pero no quiere tener la capacidad de modificar los valores,
// puede aplicar const auto&, así:
cout << endl << endl << endl << "Valores constantes de temperatura: ";
for (const auto& t : temperaturas)
cout << " " << t;
cout << endl << endl << endl;
}

// IH82.cpp: Creating Rvalue references. Crear las referencias del tipo dValor.
//
// Las referencias del tipo dValor son especialmente importantes con respecto
// a las funciones.
// Cualquier expresión es del tipo iValor o del tipo dValor.
// Una variable es un iValor porque representa una ubicación en la memoria RAM.
// Un dValor es el resultado de evaluar una expresión, comúnmente localizada en
// la parte derecha de una asignación.
// Una referencia iValor es una referencia a una variable que posee un nombre;
// y permite que el contenido de la memoria que la variable representa sea
// accesado a través de la referencia iValor.
// Se especifica una referencia del tipo dValor usando dos ampersand (&&), a
// continuación del tipo de datos.
84

//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
int x{ 5 }; // x es el número entero 5.
int&& dfExpr{ 2 * x + 3 }; // dfValor referencia.
// MMM: Esta forma de uso del dfValor no es
// recomendada
cout << "Valor de referencia del tipo derecho: " << dfExpr << endl;
int& ifX{ x }; // referencia iValor.
cout << "Valor de referencia del tipo izquierdo: " << ifX << endl;
cout << endl << endl << endl;
}

// IH83.cpp: Library functions for strings. Librería de funciones para cadenas de caracteres.
// cstring standard header. strnlen(cdnB, _countof(cdnB)). Archivo de cabecera
// estándar para cadenas tipo c.
//
// El encabezado estándar cstring define funciones que operan sobre cadenas
// terminadas con el carácter nulo.
// Estas funciones están definidas dentro del espacio de nombres std.
// Hay mejores alternativas que no son estándar, y por lo tanto no están en std.
// Estas funciones más seguras tienen nombres terminados en _s.
//
// El encabezado estándar string define las clases string y wstring.
// La clase string representa cadenas de caracteres del tipo char.
// La clase wstring representa cadenas de caracteres del tipo wchar_t.
//
// _countof() es un macro. Actúa sobre argumentos no constantes. (const char* no
85

// está permitido).
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
// La función strlen(). La función wcslen().

/* La función strlen() regresa la longitud de una cadena de caracteres del tipo char*,
la cual actúa como argumento de la función.
strlen( char* );
El tipo de dato del retorno es size_t.
*/

/* La función wcslen() regresa la longitud de una cadena de caracteres del tipo wchar_t*,
la cual actúa como argumento de la función.
strlen( wchar_t* );
El tipo de dato del retorno es size_t.
*/

const char* cdnA{ "Una mula es tan buena como una muela." };
cout << "cdnA: \n\n" << cdnA << endl << endl;
cout << "La cadena de caracteres char* cdnA tiene " << strlen(cdnA) << " caracteres.";

/* La longitud de la cadena devuelta por la función strlen() no incluye el carácter


nulo final.
Es muy importante no olvidar esto cuando se copian cadenas de caracteres de este
tipo, pues hay que copiar el carácter nulo final.
Las dos funciones hallan la longitud de la cadena buscando el carácter nulo final.
Si hay un error y este carácter nulo final no existe, la función continuará
buscando hasta encontrarlo.
Ello implica un gran riesgo de seguridad informática, pues la longitud no será
86

verdadera.
Es preferible usar strnlen() y wcsnlen().
*/
char cdnB[40]{ "Es tan buena una mujer, como una amiga." };
cout << "\n\n\nLa cadena cdnB es: " << cdnB << endl << endl;
std::cout << "La cadena cdnB contiene " << strnlen(cdnB, _countof(cdnB))
<< " characters.";
cout << endl << endl << endl;
}

// IH84.cpp: Joining null-terminated strings. Unir las cadenas terminadas en carácter nulo.
// Las funciones strncat_s() y wstrncat_s().
//
// La función strcat(), que se usa para concatenar dos cadenas de caracteres, tiene
// limitaciones debido a que es insegura.
// La alternativa segura es la función strcat_s().
// Las funciones strncat_s() y wstrncat_s().
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
const size_t cuenta{ 60 }; // La variable cuenta es del tipo size_t.
char cad1[cuenta]{ "Muchas manos" }; // cad1 no es constante.
const char* cad2{ " hacen fácil el trabajo." }; // cad2 sí es constante.
errno_t error{ strcat_s(cad1, cad2) }; /* La función strcat_s() tiene dos argumentos.
El primer argumento es la cadena1, la que
recibe el agregado de caracteres de la ca-
dena2. La cadena2 puede ser constante; la
cadena1 tiene que ser variable, para incre-
87

mentar su cantidad de caracteres.


*/
if (error == 0)
cout << "Las cadenas se fusionaron satisfactoriamente.\n"
<< cad1 << endl << endl;
else
if (error == EINVAL)
{
cout << "Error! La dirección de la cadena fuente o de la cadena destino es";
cout << " un puntero nulo."
<< endl << endl;
}
else
if (error == ERANGE)
cout << "Error! La cadena de destino es muy pequeña.";
//----------------------------------------------------------------------------
/* Al ejecutar, sale el error de la cadena del buffer.
"L buffer es demasiado pequeño."
La variable cuenta debe ser más grande. Mayor que 30.
60 funcionó perfectamente.
*/
//----------------------------------------------------------------------------
/* Con la función strncat_s() se puede adicionar (append) una parte de una
cadena de caracteres terminada con un carácter nulo final ('\0') a otra
cadena similar. El tercer argumento es la cantidad de caracteres a agregar.
El carácter nulo final se agrega por defecto.
*/
const size_t cuenta2{ 60 };
char cad3[cuenta2]{ "Muchas amigas" }; // cad3 no es constante.
const char* cad4{ " nos alegran la vida." }; // cad4 sí es constante.
errno_t error2{ strncat_s(cad3, cad4, 12) };
if (error2 == 0)
cout << "Las cadenas se fusionaron satisfactoriamente.\n"
88

<< cad3 << endl << endl;


else
if (error2 == EINVAL)
{
cout << "Error! La dirección de la cadena fuente o de la cadena";
cout << " destino es un puntero nulo." << endl << endl;
}
else
if (error2 == ERANGE)
cout << "Error! La cadena de destino es muy pequeña.";
cout << endl << endl << endl;
}

// IH85.cpp: Copying null-terminated strings. Copiar las cadenas de caracteres terminadas


// en un carácter nulo \0.
//
// Los dos puntos del encabezado de este archivo (.cpp:) no deben estar separados
// con un espacio en blanco porque provocan error en "using namespace...".
// strcpy(), strcpy_s(), wcscpy(), wcscpy_s().
//
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
/* La función de la librería estándar strcpy() copia una cadena de caracteres desde
una ubicación fuente hasta una ubicación de destino.
La función strcpy_s() es una versión más segura de strcpy().
El primer argumento es un puntero a la ubicación de destino.
El segundo es un puntero a la dirección de la cadena fuente.
89

El primer argumento es del tipo char*.


El segundo es del tipo const char*.
strcpy_s() verifica que la fuente y el destino no son punteros nulos (nullptr).
Y que el destino tiene suficiente espacio para acomodar la cadena fuente.
Si hay error, se tiene la opción de usar la programación corriente de error.
wcscpy_s() es la versión análoga para caracteres extendidos. */
const size_t cuenta{ 80 };
char cad1[cuenta]{'a', 'b', 'c'}; // cad1 no es constante.
const char cad2[3]{ '1', '2', '3' };
char cad3[cuenta]{};
char cad4[8] = "Rodrigo";
char cad5[8];
//--------------------------------------------------------------------------
const char* rCad2{ cad2 }; // cad2 es constante y el puntero es
// del tipo const char*.
char* rCad1{ cad1 }; // Definir puntero tipo char*
//--------------------------------------------------------------------------
// strncpy(cad5, cad4, 8); // Esta función strncpy() es insegura.
strncpy_s(cad5, cad4, 8); // Usar strncpy_s().
cout << "La cadena que se ha copiado a cad5 es:\n\n" << cad5;
errno_t error2{ strncpy_s(cad5, cad4, 8) };
if (error2 == 0)
cout << "\n\nLas cadenas se fusionaron satisfactoriamente.\n"
<< cad5 << endl << endl;
else
if (error2 == EINVAL)
cout << "\n\nError! La dirección de la cadena fuente o de la"
<< " cadena destino es un puntero nulo." << endl << endl;
else
if (error2 == ERANGE)
cout << "\n\nError! La cadena de destino es muy pequeña.";
cout << endl << endl << endl;
}
90

// IH86.cpp: Comparing null-terminated Strings. La comparación de cadenas de caracteres


// terminadas en un carácter nulo \0.
//
// La función strcmp() compara dos cadenas de caracteres terminadas en un carácter
// nulo \0.
// Las dos cadenas son argumentos de tipo const char*.
// La función retorna un valor entero, tipo int, que puede ser menor que cero,
// cero y mayor que cero.
// Dependiendo de si la cadena del primer argumento es menor, igual o mayor que
// la cadena del segundo argumento.
// wcscmp() es la función equivalente a strcmp(), que compara cadenas de tipo
// wstring.
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
const char* str1{ "Jesús" };
const char* str2{ "Jaime" };
int result{ strcmp(str1, str2) };
if (result < 0)
cout << str1 << " es menor que " << str2 << '.' << endl;
else if (0 == result)
cout << str1 << " es igual que " << str2 << '.' << endl;
else
cout << str1 << " es mayor que " << str2 << '.' << endl;
/* Jesús es mayor que Jaime. "Je" es mayor que "Ja".
"Mayor que" no se refiere a que tenga más cantidad de caracteres.
Se refiere a que el valor ordinal de los caracteres iniciales es mayor.
*/
cout << endl << endl << endl;
}
91

// IH87.cpp: Searching null-terminated strings. Buscar dentro de cadenas de caracteres


// terminadas en un carácter nulo \0.
//
// La función strspn() busca dentro de una cadena de caracteres, por el primero
// de los caracteres que no está dentro de un conjunto dado. La función devuelve
// el índice del elemento de ese carácter.
// La función tiene dos argumentos. El primero es un puntero a la cadena en la
// que se buscará.
// El segundo es un puntero a la cadena que contiene el conjunto de caracteres.
// wcsspn() es la función equivalente para caracteres de tipo wchar_t.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
// Buscar dentro de una cadena, el primer carácter que no es una vocal.
char cad[]{ "Aileen es muy bonita." };
const char* vocales{ "aeiouAEIOU " };
size_t index{ strspn(cad, vocales) };
cout << "El primer caracter que no es una vocal es '" << cad[index]
<< "' en la posición " << index << endl;
/* Otra manera de interpretar el resultado es que la subcadena que sí pasó
la prueba tiene una longitud dada por el índice (index). En este caso es
2: "Ai". El primer carácter no vocal es 'l', cad[2].
*/
cout << endl << endl << endl;
}
92

// IH88.cpp: Searching null-terminated strings. Buscar dentro de cadenas de caracteres


// terminadas en un carácter nulo \0. strstr().
//
// La función strstr() devuelve un puntero a la posición dentro de la cadena
// referida por el primer argumento, de una subcadena de búsqueda especificada
// por el segundo argumento.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char cad[]{ "Me acuerdo de la cuerda." }; // Cadena cad, en la que se buscará la
// subcadena.
const char* substring{ "cuer" }; // Subcadena substring, fragmento que se
// buscará.
char* rSubstr{ strstr(cad, substring) }; // La función strstr() devuelve un puntero de
// tipo carácter (char*).
if (!rSubstr)
cout << "\"" << substring << "\" no se halla en\"" << cad << "\"" <<
endl;
else
cout << "La primera ocurrencia de \"" << substring
<< "\" en \"" << cad << "\" es en la posición "
<< rSubstr - cad << endl;
cout << endl << endl << endl;
}
93

// IH89.cpp: Searching null-terminated strings. Buscar dentro de cadenas de caracteres


// terminadas en un carácter nulo \0.
//
// La búsqueda dentro de una cadena para hallar la cantidad de ocurrencias
// de una subcadena.
//
#include <iostream>
#include <cstring> // Librería para cadenas \0.
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char cad[]{ "La tenia no tenía tantas tentaciones." };
const char* palb{ "ten" };
cout << "La cadena en la que se buscará es:\n\n" << endl << cad << endl;
int cuenta{}; // Número de ocurrencias de la palabra en cad.
char* rCad{ cad }; // Puntero de tipo char* para buscar dentro de cad
// la posición inicial de la subcadena.
char* rHallado{}; // Puntero tipo char* para validar la ocurrencia.
// Al no hallar, devuelve nullptr.
const size_t largoPal{ strlen(palb) };// Longitud de la palabra palb. Tipo de dato: size_t.
while (true) // Ciclo while, mientras sea verdadero.
{
rHallado = strstr(rCad, palb);// rHallado será el resultado de la búsqueda con
// strstr().
if (!rHallado) // Si no se halló... El puntero tiene nullptr.
break; // Reiniciar el ciclo de búsqueda. Ir a while...
++cuenta; // Se halló. Incrementar el contador de hallados.
rCad = rHallado + largoPal; // Continuar la búsqueda a partir de la posición
// inicial de la última palabra que se encontró,
// más 1 posición.
}
94

cout << endl << endl;


cout << "La subcadena " << "\"" << palb << "\" se halló "
<< cuenta << " veces en la cadena." << endl;
cout << endl << endl << endl;
}

// IH90.cpp: How to declare and write your own c++ functions. Cómo declarar y escribir
// sus propias funciones en c++.
//
// Funciones: suma_enteros(int i, int j); potencia(double x, int n).
// La función debe declararse primero, antes de definir la función main. En
// esta declaratoria, la función tiene indicado el tipo de dato que devuelve.
// Si no devuelve nada, el tipo es void.
// La función suma_enteros() devuelve un valor entero; la función potencia()
// devuelve un valor doble.
// La función suma_enteros() tiene dos argumentos enteros; la función potencia(),
// uno doble y un int.
//
#include <iostream>
using namespace std;
int suma_enteros(int, int); // Declaratoria de la función, con el tipo de argumento.
double potencia(double, int); // Declaratoria de la función, con el tipo de argumento.
int main()
{
system("color FD");
cout << endl << endl << endl;
cout << suma_enteros(4, 8); // Sumar y escribir 4 y 8.
cout << endl << endl << endl;
cout << potencia(2.5, 5); // Elevar 2.5 a la quinta y escribirlo.
cout << endl << endl << endl;
}
int suma_enteros(int i, int j) // Encabezado de la función. Function header.
95

{
return i + j;
}
double potencia(double x, int n) // Encabezado de la función. Function header.
{
double resultado{ 1.0 };
for (int i{ 1 }; i <= n; i++)
resultado *= x;
return resultado;
}

// IH91.cpp: The function header. El encabezado de la función. double potencia(double, int);


// double potencia(double x, int n){}.
//
#include <iostream>
using namespace std;
// Declaración de las funciones.
double potencia(double, int); // Aquí sí se ocupa ;
int main()
{
system("color FD");
cout << endl << endl << endl;
cout << potencia(7.8, 2); // 7.8 al cuadrado se imprime en la pantalla.
cout << endl << endl << endl;
}
double potencia(double x, int n) // Function header. Encabezado. No se ocupa ; acá.
{
double resultado{ 1.0 }; // Resultado guardado aquí.
for (int i{ 1 }; i <= n; i++)
resultado *= x;
return resultado;
} // Tampoco se ocupa ; acá.
96

/* El encabezado está compuesto de varios elementos. El primero es el tipo


de valor de devolución.
En este encabezado es double, doble.
El segundo es el nombre de la función. Aquí es power.
El tercero es la lista de argumentos. Puede ser cero, uno, dos o más.
Los argumentos contienen varios datos: el tipo de dato del argumento,
el nombre interno para la función de la variable que identificará a
cada argumento.
El orden de los argumentos es esencial.

Una función se puede usar de diversas maneras.


La más simple es declarar y definir la función dentro del cuerpo de la
función main. Así, main la buscará internamente y no habrá mayor dificultad.
Sin embargo, cuando son numerosas funciones, hacerlo así se vuelve demasiado
complejo.
Entonces se acostumbra declarar la función antes de main. En main solo
se llama a la función para utilizarla. Y después de main se define la
función en sus detalles.
*/

// IH92.cpp: The function header. El encabezado de la función. Void mi_funcion(void).


//

#include <iostream>
using namespace std;
void mi_funcion(void); // Esta es la declaración de la función.
void mi_funcion2(); // Esta es la declaración de la función.
int main()
{
system("color FD");
cout << endl << endl << endl;
mi_funcion();
97

mi_funcion2();
/* Estas dos funciones no devuelven ningún tipo de valor o dato. Por lo tanto,
debe tenerse cuidado de no mezclarla dentro de una expresión compuesta, donde
estaría representando algún tipo de dato. El compilador señalaría este error.
*/
cout << endl << endl << endl;
}
// Definición de la función mi_funcion().
// Esta función no devuelve ningún dato. Ni ocupa ningún dato como argumento.
// Su argumento está vacío.
void mi_funcion(void)
{
cout << "Esta es mi función.\n\n\n";
}
// Definición de la función mi_funcion2().
// Esta función no devuelve ningún dato. Ni ocupa ningún dato como argumento.
// Su argumento está vacío.
void mi_funcion2()
{
cout << "Esta es mi segunda función.\n\n\n";
}

// IH93.cpp: The function header. El encabezado de la función. Alternative function syntax.


// Sintaxis alternativa de la función.
//
// En esta sintaxis alternativa, el tipo de dato que devuelve la función está
// pendiente de determinarse de acuerdo a la ejecución de la función. Se llama
// "trailing return type".
// Esto será muy útil cuando se usen plantillas de funciones. Function templates.
//
#include <iostream>
using namespace std;
98

auto potencia(double, int) -> double; // Declaración de la función potencia().


int main()
{
system("color FD");
cout << endl << endl << endl;
cout << "El resultado de 7 a la quinta es: " << potencia(7.0, 5);
cout << endl << endl << endl;
}
// Definición de la función potencia.
auto potencia(double x, int n) -> double
{
double resultado{ 1.0 };
for (int i{ 1 }; i <= n; i++)
resultado *= x;
return resultado;
}

// IH94.cpp: Function prototypes. Prototipos de funciones.


//
// El prototipo de una función provee la información básica que el compilador
// necesita para determinar que se está usando la función apropiadamente.
// En el prototipo existen 4 elementos. El tipo de dato que devuelve una función,
// void cuando no devuelve ninguno. El nombre de la función. Los argumentos, con
// el tipo de dato del argumento, que es suficiente para el compilador, siguiendo
// un orden estricto, separados por comas. Finalmente, un punto y coma.
// El encabezado de la función deberá coincidir esencialmente con el prototipo.
// En el encabezado sí es obligatorio nombrar las variables de los argumentos.
// Estos nombres serán usados dentro del cuerpo de la función.
// Los prototipos son obligatorios antes de hacer cualquier llamado con el nombre
// de la función.
// MMM: Es aconsejable usar nombres claros de las variables junto a su tipo de
// datos en el prototipo, con la finalidad de saber de inmediato de que
99

// variable se trata y evitar confusiones.


//
#include <iostream>
using namespace std;
// Declaración de la función potencia().
auto potencia(double x_alapotencia, int n_potencia) -> double;
int main()
{
system("color FD");
cout << endl << endl << endl;
// Pasando dos valores constantes como argumentos a la función potencia.
cout << "El resultado de 7 a la quinta es: " << potencia(7.0, 5);
double x{ 1.1 };
double y{};
x = potencia(x, potencia(2.0, 2.0));// El segundo argumento es la misma función
// potencia con dos argumentos double. El
// segundo argumento será convertido a entero
// por el compilador. Dos veces. La conversión
// será auto conversión.
// MMM: Será mejor aquí usar así:
// x = potencia(x, static_cast<int>potencia(2.0,2.0));
// La pérdida de datos decimales es igual, pero queda
// indicado que estoy haciendo una conversión.
cout << endl << "\n\n\nEl valor de x es: " << x;
y = potencia(5.0, 3);
cout << endl << endl
<< "El valor de y con 5 elevado al cubo es: " << y;
cout << endl << endl << endl;
}
// Definición de la función potencia.
auto potencia(double x, int n) -> double
{
double resultado{ 1.0 };
100

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


resultado *= x;
return resultado;
}

// IH95.cpp: Passing arguments to a function. El paso de argumentos a la función. Paso por


// valor.
//
// El primer mecanismo para pasar argumentos a una función es el paso por valor.
// Los argumentos o parámetros se establecen en la definición de la función como
// variables comunes. El paso de información es directo. No se usan referencias.
// Es un método directo de transferencia de datos.
//
// Con este mecanismo, las variables, las constantes o los valores de expresión, que
// se ponen como argumentos no son transferidos de ninguna manera a la función.
// En vez de eso, la computadora hace una copia de los parámetros. Y estas copias
// son los valores o datos que se transfieren a la función.
// Los parámetros originales no son accesibles dentro de la función. Sólo las
// copias son accesibles.
// Cada vez que se llama a la función, el compilador crea las copias correspon-
// dientes. Y opera solo sobre estas copias, dejando intactos los valores originales.
// Es inútil tratar de modificar los valores originales de los parámetros de llamada.
#include <iostream>
using namespace std;
int incremento_en10(int numero); // Prototipo de la función para incrementar en 10.
int main()
{
system("color FD");
cout << endl << endl << endl;
int numero{ 3 };
cout << "La función incremento_en10(int numero) es igual a: " << incremento_en10(numero)
<< endl << endl << "El número es: " << numero;
101

/* Observe que la función incrementa el número 3 hasta 13.


Pero la variable numero, que se define inicialmente con el valor 3, no cambia de valor
a pesar de que la función la usa. La usa para crear una copia, la cual si cambia de
valor.
*/
cout << endl << endl << endl;
}
// Definición de la función incremento_en10().
int incremento_en10(int numero)
{
numero += 10;
return numero;
}

// IH96.cpp: Pointers as arguments to a function. Punteros como argumentos de una función.


// Paso por punteros.
//
// El primer mecanismo para pasar argumentos a una función es el paso por valor.
// Los argumentos o parámetros se establecen en la definición de la función como
// variables comunes. El paso de información es directo. No se usan referencias.
// Es un método directo de transferencia de datos.
//
// Con este mecanismo, las variables, las constantes o los valores de expresión,
// que se ponen como argumentos no son transferidos de ninguna manera a la
// función.
// En vez de eso, la computadora hace una copia de los parámetros. Y estas copias
// son los valores o datos que se transfieren a la función.
// Los parámetros originales no son accesibles dentro de la función. Sólo las copias
// son accesibles.
// Cada vez que se llama a la función, el compilador crea las copias correspondientes.
// Y opera solo sobre estas copias, dejando intactos los valores originales.
// Es inútil tratar de modificar los valores originales de los parámetros de llamada.
102

//
// Cuando se usa un puntero como argumento, el mecanismo de paso por valor
// continúa vigente; sin embargo, un puntero es una dirección de memoria que
// apunta a una variable, y si el compilador hace una copia de esta dirección de
// memoria, esta copia siempre continúa apuntando a la misma variable.
// De esta manera, especificando un puntero como parámetro nos permite en la
// función el tener acceso a un argumento manipulador de variables.
//
#include <iostream>
using namespace std;
int incremento_en10(int* numero); // Prototipo de la función.
int main()
{
system("color FD");
cout << endl << endl << endl;
int numero{ 3 };
int* rNumero{ &numero }; // Puntero a la variable numero.
cout << "Dirección de memoria que se ha pasado a la función: "
<< rNumero << endl << endl;
int resultado{ incremento_en10(rNumero) };
cout << "\n\n\nEl incremento que se ha hecho es: " << resultado
<< endl << endl;
cout << "El numero es: " << numero;
cout << endl << endl << endl;
}
// Definición de la función incremento_en10() con un puntero como argumento.
int incremento_en10(int* numero)
{
cout << "Dirección de memoria que se ha recibido: " << numero;
*numero += 10; // El contenido de la dirección de memoria numero se incrementa
// en la cantidad 10.
return *numero;
}
103

// IH97.cpp: Passing arrays to a function. El paso de matrices a una función.


//
// Si se pasa en forma directa una matriz a una función, el método de paso
// por valor aún persiste, pero la matriz no es copiada por el compilador.
// Lo que el compilador ejecuta es que el nombre de la matriz es transformado
// en un puntero a la matriz. Una copia del puntero al inicio de la matriz
// se pasa por valor a la función.
// Sin importar el tamaño de la matriz, ésta no se copia.
// Así, los elementos de la matriz sí pueden ser modificados por la función.
// Las matrices o arreglos de elementos no se pasan por valor.
#include <iostream>
using namespace std;
double promedio(double matriz[], int cuenta); // Prototipo de la función.
int main()
{
system("color FD");
cout << endl << endl << endl;
double valores[]{ 11.6, 9.4, 5.8, 2.9, 1.7, 4.8, 6.3, 3.2, 8.5, 10.2 };
cout << "El promedio de los valores es: " << promedio(valores, _countof(valores));
valores[0] = 0.6;
cout << "\n\n\nEl promedio de los valores ahora es: "
<< promedio(valores, _countof(valores));
cout << endl << endl << endl;
}
// Definición de la función para calcular el promedio.
double promedio(double matriz[], int cuenta)
{
double suma{};
for (int i{}; i < cuenta; i++)
suma += matriz[i];
return suma / cuenta;
}
104

// IH98.cpp: Using pointer notation when passing arrays. El uso de notación de punteros
// cuando se pasa una matriz.
//
// Manipulando una matriz dentro de una función, como un puntero.
//
#include <iostream>
using namespace std;
double promedio(double* matriz, int cuenta); // Prototipo de la función promedio().
int main()
{
system("color FD");
cout << endl << endl << endl;
double valores[]{ 2.3, 1.9, 6.2, 9.5, 3.8, 5.1, 8.7, 4.5, 3.4, 10.8 };
cout << "El promedio de los valores de la matriz es: "
<< promedio(valores, _countof(valores));
cout << "\n\n\nEl número de elementos de la matriz es: " << _countof(valores);
cout << endl << endl << endl;
}
// Definición de la función promedio().
double promedio(double* matriz, int cuenta)
{
double suma{};
for (int i{}; i < cuenta; i++) // Sumar los elementos de la matriz[].
suma += *matriz++;
return suma / cuenta; // Devolver el valor promedio.
}
105

// IH99.cpp: Passing multidimensional arrays to a function. El paso de matrices


// multidimensionales a una función.
//
// Para pasar una matriz multidimensional a una función, se procede directamente.
// No es posible pasar un elemento simple de una matriz como parámetro en el
// encabezado de una función,
// o en el prototipo de una función. Para pasarlo, es necesario usar una variable
// y su tipo de dato.
// Cuando se llama a la función, sí es posible pasar un elemento simple de una matriz.
// En el prototipo y en el encabezado de la definición de la función, el compilador
// siempre asume que si aparece una sintaxis de matriz, se está tratando de una
// matriz entera y no de un elemento en particular.
// Cuando se define una matriz multidimensional como parámetro, es posible omitir
// el valor de la primera dimensión.
//
#include <iostream>
using namespace std;
double rendimiento(double frijoles[3][4]); // Prototipo de la función.
double rendimiento2(double arroz[][5], int indice); // Prototipo sin la primera dimensión.
// El valor del índice puede servir para
// indicar la primera dimensión.
int main()
{
system("color FD");
cout << endl << endl << endl;
double frijoles[3][4]{
{6.2, 7.8, 1.9, 3.3},
{8.2, 4.2, 9.3, 4.5},
{6.1, 8.0, 5.5, 9.3} };
double arroz[3][5]{
{8.9, 12.0, 23.1, 17.9, 18.3},
{7.5, 19.5, 14.3, 15.2, 9.9},
{16.6, 11.1, 10.4, 9.2, 8.6} };
106

cout << "El rendimiento total de los frijoles es: " << rendimiento(frijoles);
cout << "\n\n\nEl rendimiento total del arroz es: " << rendimiento2(arroz, 3);
cout << endl << endl << endl;
}
// Definición de la función rendimiento(). Aquí hago un ejercicio de calcular
// el tamaño total de la matriz, y dividirlo entre el tamaño en bytes del primer
// elemento de la matriz. Esto da la cantidad total de los elementos que tiene
// la matriz. El tipo de dato es size_t, el cual debe convertirse a int mediante
// static_cast.
/* double rendimiento(double frijoles[3][4])
{
double suma{};
size_t cuenta{ static_cast<int>(sizeof(frijoles[3][4]))
/ static_cast<int>(sizeof(frijoles[0])) };
// Los paréntesis son indispensables.
for (int i{}; i < ((static_cast<int>(cuenta)) / 4) - 1; i++) // 3 filas.
for (int j{}; j < 4; j++) // 4 columnas.
suma += frijoles[i][j];
return suma;
} */
// Definición de la función rendimiento().
double rendimiento(double frijoles[3][4])
{
double suma{};
for (int i{}; i < 3; i++) // 3 filas.
for (int j{}; j < 4; j++) // 4 columnas.
suma += frijoles[i][j];
return suma;
}
// Definición de la función rendimiento2().
double rendimiento2(double arroz[][5], int indice)
{
double suma{};
107

for (int i{}; i < indice; i++) // 3 filas.


for (int j{}; j < 5; j++) // 5 columnas.
suma += arroz[i][j];
return suma;
}

// IH100.cpp: References as arguments to a function. Las referencias como argumentos de una


// función.
//
// Using an lvalue reference to modify caller arguments. Usando un iValor como
// referencia para modificar argumentos de llamada.
// El segundo mecanismo para pasar argumentos a una función es pasar por
// referencia.
// El parámetro actúa como un alias para el argumento que se pasa.
// El argumento ya no se copia. La función tiene acceso al argumento directamente.
// La dereferenciación, que es requerida cuando se pasa y se usa un puntero a un
// valor, es innecesaria.
// Los parámetros por referencia son muy útiles cuando se pasan objeto de tipo
// clase.
// Estos objetos pueden ser muy voluminosos y también muy complejos. El
// compilador tardaría mucho tiempo en copiarlos. Al usar referencias a estos
// objetos, el proceso es muchísimo más rápido.
//
// El parámetro de la función es inicializado con la dirección de memoria del
// argumento cada vez que es llamada la función. La referencia es creada e
// inicializada cada vez que se llama a la función. Y cada vez, también, es destruida
// cuando la función termina.
// Esto hace que se use una referencia completamente nueva cada vez que se use la
// función.
// Una referencia es un alias para otra variable. Es una manera alternativa de
// referirse a esa otra variable. Es equivalente a utilizar el nombre original de la
// variable.
108

//
// MMM: Si se trata de usar un valor numérico, como 30, en la llamada a la función,
// el compilador señalará un error.
// Tampoco se puede usar una expresión de variables, como argumento
// correspondiente a un iValor, a menos que la expresión sea un iValor.
//
#include <iostream>
using namespace std;
int incremento_en10(int& numero);// Prototipo de una función con un argumento de
// llamada en referencia a la variable entera numero.
int main()
{
system("color FD");
cout << endl << endl << endl;
int numero{ 3 };
int valor{ 6 };
int resultado1{ incremento_en10(numero) };
cout << "Se ha aplicado un incremento en 10 a la variable numero, que estaba en 3.”
<< “ Nuevo dato: " << resultado1 << endl << endl;
cout << "Variable numero: " << numero;
cout << endl << endl << endl;
resultado1 = incremento_en10(valor);
cout << "Se ha aplicado un incremento en 10 a la variable valor, que estaba en 6.”
<< “ Nuevo dato: " << resultado1 << endl << endl;
cout << "Variable valor: " << valor;
cout << endl << endl << endl;
}
// Definición de una función para incrementar en 10 unidades a una variable entera.
// Esta función usa una llamada por referencia con un iValor.
int incremento_en10(int& numero)
{
cout << "Valor recibido: " << numero << endl << endl;
numero += 10; // Se incrementa la variable numero en 10.
109

// La función recibe el valor de numero por


// referencia. El aumento no es interno de
// la función, sino que ocurre en la memoria
// en la que está registrada la variable.
return numero; // Devuelve el nuevo valor.
}

// IH101.cpp: Constant references as arguments to a function. Las referencias constantes como


// argumentos de una función.
//
// El modificador const permite decirle al compilador que no se tiene la intención
// de modificar un argumento de una función, dentro de esa misma función.
// Esto hace que el compilador verifique dentro del código de esa función que en
// efecto no se intenta modificar el argumento, y de esa manera el compilador
// aprueba el uso del modificador const aplicado a un argumento, y no señala
// ningún error.
//
#include <iostream>
using namespace std;
int incremento_en10(const int& numero);// Prototipo de una función con un argumento
// constante.
int main()
{
system("color FD");
cout << endl << endl << endl;
const int numero{ 3 }; // Dentro del ámbito de la función se crea
// temporalmente la variable numero con un valor
// constante de 3.
int valor{ 6 };
int resultado1{ incremento_en10(numero) };
cout << "\n\nSe ha aplicado un incremento en 10 a la variable numero, que estaba en 3.”
<< " Nuevo dato: " << resultado1 << endl << endl;
110

cout << "Variable numero: " << numero;


cout << endl << endl << endl;
resultado1 = incremento_en10(valor);
cout << "\n\nSe ha aplicado un incremento en 10 a la variable valor, que estaba en 6.”
<< " Nuevo dato: " << resultado1 << endl << endl;
cout << "Variable valor: " << valor;
cout << endl << endl << endl;
}
// Definición de una función para incrementar el valor de una variable en 10 unidades.
int incremento_en10(const int& numero)
{
cout << "Valor recibido: " << numero;
// numero += 10; // Esta instrucción ahora sería ilegal.
return numero + 10; // Se incrementa el valor de retorno. El valor
// de la variable numero no se modifica.
}

// IH102.cpp: Using rvalue reference parameters. El uso de un dValor como parámetro de


// referencia.
//
// Un iValor se puede convertir en un dValor. En el capítulo 8 se verá cómo se puede
// usar la función std::move() de la librería standard (std) para convertir un iValor
// a un dValor.
//
#include <iostream>
using namespace std;
int incremento_en10(int&& numero); // Prototipo de función con argumento dValor.
int main()
{
system("color FD");
cout << endl << endl << endl;
int numero{ 3 };
111

int valor{ 6 };
int resultado{};
/*
resultado = incremento_en10(numero); // Incrementar numero
cout << endl << "incremento_en10(numero) = " << resultado
<< endl << "numero = " << numero;
resultado = incremento_en10(valor); // Incrementar valor
cout << endl << "incremento_en10(valor) = " << resultado
<< endl << "valor = " << valor; */
resultado = incremento_en10(valor + numero); // El argumento es una expresión, un
// dValor.
cout << "\n\nincremento_en10(valor + numero) = " << resultado << endl << endl
<< "numero = " << numero << endl << endl
<< "valor = " << valor << endl << endl << endl;
resultado = incremento_en10(5);
cout << "\n\nincremento_en10(5) = " << resultado << endl << endl
<< "5 = " << 5;
cout << endl << endl << endl;
}
// Definición de una función para incrementar en 10 unidades a una variable numero.
int incremento_en10(int&& numero) // Prototipo de función con argumento dValor.
{
cout << "Valor recibido en la variable numero: " << numero;
numero += 10; // Incremento a la variable.
return numero; // Devuelve el valor que se aumentó.
}
112

// IH103.cpp: Arguments to main(). Argumentos pasados a la función main().


// Receiving command-line arguments. Recibiendo argumentos en la línea de
// comandos.
//
// Se puede definir la función main() sin ningún parámetro.
// También se puede definir una lista de parámetros, la cual permite que la función
// main() obtenga valores de la línea de comandos contenida en el llamado de la
// función, para ejecutar esos comandos en el programa.
// Para proveer de datos en la línea de comandos de la función main(), defínala así:
// int main( int argc, char* argv[] )
// {
// código de la función (cuerpo de la función)
// }
// El primer parámetro, int argc, es la cuenta del número de cadenas de caracteres
// que se escriben en la línea de comandos de la función main(), incluyendo el
// nombre del programa ejecutable (*.exe).
// El segundo parámetro es una matriz que contiene punteros a estas cadenas de
// caracteres, más un elemento adicional que es nulo (nullptr).
// argc es siempre por lo menos 1, el nombre del programa.
// El número de argumentos recibido depende de lo que se escriba en la línea de
// comandos al momento de ejecutar el programa. Esta sintaxis es muy
// generalizada en Linux.
//
// Vamos a suponer que se ejecuta el programa: Ixz.exe
// En la línea de comandos sólo se escribió el nombre del programa ejecutable.
// argc = 1.
// argv, la matriz, contiene dos elementos. argv[0] apunta a la cadena "Ixz.exe";
// argv[1] contiene "nullptr".
//
// Suponga ahora que se escribe en la línea de comandos: Ixz.exe -a="medio"
// -b="exacta" 99.9
// argc = 3.
// argv[0]="Ixz.exe", argv[1]="-a="medio"", argv[2]="-b="exacta"",
113

// argv[3]="99.9", argv[4]="nullptr".
//
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
system("color FD");
cout << endl << endl << endl;
cout << "argc = " << argc << endl << endl;
cout << "Los argumentos que se recibieron en la línea de comandos son: " << endl;
for (int i{}; i < argc; i++)
cout << "Argumento " << (i + 1) << ": " << argv[i] << endl;
//------------------------------------------------------------------------------------
/* MMM: También se puede programar evitando el último argumento de la matriz argv[],
dado que siempre su contenido será "nullptr".
int{-1};
while(argv[++i])
cout << "El argumento " << (i+1) << " es: " << argv[i] << endl;
*/
cout << endl << endl << endl;
}

// IH104.cpp: Accepting a variable number of function arguments. Recibir una cantidad


// variable de argumentos en la función.
//
// Se puede definir una cantidad variable de argumentos pasados a una función.
// Para indicarlo, se usa una elipsis, tres puntos seguidos, al final de la lista de
// argumentos.
// Debe existir en la lista, al menos un parámetro ordinario. Y la elipse siempre
// debe aparecer al final.
// Como es obvio, no aparece información sobre la cantidad ni el tipo de
// argumentos.
114

// La librería estándar de c++ resuelve esto mediante la librería de encabezados


// cstdarg.
// Este encabezado contiene las funciones/macros va_start(), va_arg(), va_end().
//
#include <iostream>
#include <cstdarg>
using namespace std;
int suma(int cuenta, ...); // Prototipo de función con cantidad indeterminada de
// argumentos.
int main(int argc, char* argv[])
{
system("color FD");
cout << endl << endl << endl;
/* MMM: El primer argumento debe expresar la cantidad de argumentos en la lista,
pues es la cuenta.
*/
cout << suma(2, 2, 3) << endl << endl; // La suma es 5
cout << suma(6, 2, 3, 5, 7, 8, 11) << endl << endl; // La suma es 36
cout << suma(9, 11, 23, 36, 12, 50, 16, 27, 55, 47) << endl;
// La suma es 277
cout << endl;
cout << suma(3, 2, 3) << endl << endl; // La suma es 8000098
cout << endl << endl << endl;
}
// Definición de la función suma con cantidad indeterminada de argumentos.
int suma(int cuenta, ...)
{
if (cuenta <= 0)
return 0;
va_list rArg_ptr; // Puntero a la lista de argumentos
va_start(rArg_ptr, cuenta); // Asigna el puntero rArg_ptr a la dirección del
// primer argumento opcional.
int suma{};
115

for (int i{}; i < cuenta; i++)


suma += va_arg(rArg_ptr, int);
// Incrementa el valor de suma con la variable apuntada
// por rArg_ptr.
va_end(rArg_ptr); // Redefine el puntero a nulo (nullptr).
return suma;
}

// IH105.cpp: A function that returns a pointer. Una función que devuelve un puntero.
// El puntero termina vacío.
//
// Una función puede usarse para retornar un puntero, fácilmente.
// La instrucción return, al final de la función, debe devolver por referencia
// la dirección de una variable. Así:
// return &variable;
// El tipo de retorno de dato de la función debe indicarse apropiadamente en
// el prototipo de la función y en la definición de la función (encabezado).
// MMM: La función puede de manera correcta devolver una dirección de memoria
// en forma de un puntero a una variable. El asunto es que una variable
// local automática, la función la crea cuando inicia la función y la
// destruye al finalizar la función. Por lo tanto, la dirección de memoria
// del puntero continúa existiendo, pero los datos han sido borrados por
// el compilador.
//
// Regla de oro para la devolución de direcciones de memoria:
//
// ¡Nunca, pero nunca, devuelva la dirección de memoria de una variable
// local automática en una función!
//
#include <iostream>
using namespace std;
double* rfTriplicar(double datos); // Prototipo de una función que devuelve un puntero.
116

int main()
{
system("color FD");
cout << endl << endl << endl;
double numero{ 7.1 };
double* rValRet{}; // Puntero nullptr tipo double.
rValRet = rfTriplicar(numero);
cout << "El valor triplicado de la variable numero es: " << 3.0 * numero << endl << endl;
cout << "La variable numero es: " << numero << endl << endl << endl;
cout << "El valor devuelto por la función rfTriplicar es: " << *rValRet;
/* MMM: El resultado que se obtiene es garbage, basura digital en la memoria de la RAM.
Sin embargo, la función cumple en devolver un puntero a una posición de la
memoria RAM, que apuntaba a una variable que ahora no existe.
*/
cout << endl << endl << endl;
}
// Definición de la función que devuelve un puntero. La función ejecuta correctamente las
// instrucciones, y devuelve correctamente un puntero a una variable local automática.
// Sin embargo, el valor acumulado en la variable es borrado por el compilador al finalizar
// la función.
double* rfTriplicar(double datos)
{
double resultado{};
resultado = 3 * datos;
return &resultado; // Devuelve la dirección de memoria de la variable.
}
117

// IH106.cpp: A function that returns a pointer. Una función que devuelve un puntero.
// El puntero ahora sí es eficiente.
//
// Una función puede usarse para retornar un puntero, fácilmente.
// La instrucción return, al final de la función, debe devolver por referencia
// la dirección de una variable. Así:
// return &variable;
// El tipo de retorno de dato de la función debe indicarse apropiadamente en
// el prototipo de la función y en la definición de la función (encabezado).
// MMM: La función puede de manera correcta devolver una dirección de memoria
// en forma de un puntero a una variable. El asunto es que una variable
// local automática, la función la crea cuando inicia la función y la
// destruye al finalizar la función. Por lo tanto, la dirección de memoria
// del puntero continúa existiendo, pero los datos han sido borrados por
// el compilador.
//
// Regla de oro para la devolución de direcciones de memoria:
//
// ¡Nunca, pero nunca, devuelva la dirección de memoria de una variable
// local automática en una función!
//
#include <iostream>
using namespace std;
double* rfTriplicar(double datos); // Prototipo de una función que devuelve un puntero.
int main()
{
system("color FD");
cout << endl << endl << endl;
double numero{ 7.1 };
double* rValRet{}; // Puntero nullptr tipo double.
rValRet = rfTriplicar(numero);
cout << "El valor triplicado de la variable numero es: " << 3.0 * numero << endl << endl;
cout << "La variable numero es: " << numero << endl << endl << endl;
118

cout << "El valor devuelto por la función rfTriplicar es: " << *rValRet;
/* MMM: El resultado que se obtiene ya no es garbage, basura digital en la memoria de
la RAM.
La función ahora sí cumple en devolver un puntero a una posición de la
memoria RAM, que apunta a una variable que ahora sí existe.
Es necesario eliminar de la memoria disponible esta ocupación mediante un
puntero que continúa declarado. Una manera de evitar esto es usar punteros
inteligentes, los cuales se estudiarán en el capítulo 10.
Siempre que se usa new se debe eliminar el puntero con delete. Para que el
puntero no quede "loco", se le asigna una dirección nula.
*/
delete rValRet;
rValRet = nullptr;
cout << endl << endl << endl;
}
// Definición de la función que devuelve un puntero. La función ejecuta correctamente las
// instrucciones.
// Para que devuelva en forma eficiente la información que se requiere de los datos, es
// necesario usar la instrucción new y crear la variable en la memoria complementaria.
// En la memoria adicional, la variable sólo se puede destruir usando delete.
double* rfTriplicar(double datos)
{
double* resultado{ new double{} };// Creación de resultado en la free store (memoria
// libre).
*resultado = 3 * datos; // Modificación del contenido de resultado.
return resultado; // Devuelve el puntero resultado.
}
119

// IH107.cpp: Returning a reference. Devolver una referencia. Referencia a un iValor.


//
// Se puede devolver una referencia a un iValor desde una función.
// Esta acción es tan frágil como devolver un puntero.
// Debido a que una referencia a un iValor no tiene existencia propia, sino que está
// siempre determinada como un alias a un objeto, debe estarse seguro de que el
// dicho objeto continúe existiendo aún cuando la función haya terminado.
// Es muy fácil pasar por alto lo anterior cuando se están usando referencias dentro
// de una función, porque dan la apariencia de ser como variables.
//
// Por otro lado, las referencias como tipos de retorno son muy valiosas en el
// contexto de la programación orientada a objetos.
// Ellas permiten realizar cosas que serían imposibles de otra forma. Por ejemplo,
// en la sobrecarga de operadores.
// Al retornar una referencia iValor desde una función se puede usar el resultado
// de la función en la parte izquierda de una instrucción de asignación.
//
// Regla de oro sobre el retorno de una referencia desde una función:
//
// ¡Nunca, pero nunca, devuelva una referencia desde una función a una variable
// local!
//
#include <iostream>
#include <iomanip>
using namespace std;
double& minimo(double valores[], int longitud); // Prototipo de una función.
int main()
{
system("color FD");
cout << endl << endl << endl;
double datos[]{ 6.9, 7.5, 2.8, 5.9, 3.1, 13.4, 12.3, 11.2, 8.4, 10.4, 6.8, 17.6 };
int elementos{ _countof(datos) };
for (auto valor : datos)
120

cout << setw(6) << valor;


minimo(datos, elementos) = 7.2; // Cambiar el elemento mínimo a 7.2
minimo(datos, elementos) = 2.7; // Cambiar el elemento mínimo a 2.7
/* El dato más bajo al principio es 2.8, el cual es sustituido por 7.2 al usar la función.
Luego, el dato más bajo es 3.1, el cual es sustituido por 2.7 en el segundo uso de la
función.
*/
cout << endl << endl << endl;
for (auto valor : datos)
cout << setw(6) << valor;
cout << endl << endl << endl;
}
// Definición de la función minimo, la cual retorna una referencia.
double& minimo(double matA[], int elementos)
{
int j{}; // Marcador del dato mínimo.

for (int i{ 1 }; i < elementos; i++) // Chequear todos los elementos.


if (matA[j] > matA[i]) // Comparar los datos. Si matA[i] es menor...
j = i; // Marcar el dato menor.
return matA[j]; // Devolver la referencia al dato mínimo. Aquí
// en apariencia se devuelve una variable, un
// dato. Pero el compilador tiene indicado que
// el valor devuelto por la función minimo es
// una referencia (&) a un dato o a una variable,
// por lo que aplica la referencia al dato.
}
121

// IH108.cpp: Static variables in a function. Variables estáticas en una función.


//
// Hay algunas cosas que no se pueden hacer con una variable automática en una
// función.
// Por ejemplo, no se puede contar cuántas veces es llamada la función, pues no se
// puede acumular valores de una llamada a otra.
// Se podría obviar esta situación usando una variable contadora dentro del
// programa que llama a la función. Sería problemático, sin embargo, si la función
// es llamada desde varios programas. Se usaría un parámetro de referencia para
// pasar la información desde la función al programa llamador.
// Se pudiera usar una variable global para acumular las llamadas desde dentro de
// la función. Con cuidado, porque las variables globales son arriesgadas. Se pueden
// llamar desde cualquier parte y modificar inadvertidamente.
//
// Para crear una variable cuyos valores persistan de una llamada a la siguiente, se
// puede declarar la variable como estática dentro de la función.
// Por ejemplo, para declarar la variable cuenta como estática, se usaría:
//
// static int cuenta{};
//
// La inicialización ocurre la primera vez que la función es llamada. La variable se
// crea y se inicializa. Luego continúa existiendo mientras se ejecute el programa.
// Cuando la función termina, el valor de la variable no desaparece y tampoco la
// variable.
// Ambos están activos cuando la función es llamada de nuevo. La declaración que
// crea la variable estática ya no tiene validez cuando la variable ya está creada.
//
#include <iostream>
using namespace std;
void registrar(); // Prototipo de función; no se usa argumentos ni valor de retorno.
int main()
{
system("color FD");
122

cout << endl << endl << endl;


registrar();
for (int i{}; i <= 3; i++)
registrar();
cout << endl << endl << endl;
}
// Declaración de la función registrar(). Esta función registra la cantidad de veces que
// se ha llamado a la función.
void registrar()
{
static int cuenta{ };
cout << endl << "Esta es la " << ++cuenta
<< " vez que se ha llamado a la función registrar().";
}

// IH109.cpp: A recursive function call. Una llamada a una función recursiva.


//
// Una función recursiva es una función que se llama a sí misma. Esta es una función
// recursiva directa. Si la función A llama a la función B, y dentro de ésta se llama
// a la función A, entonces decimos que la función A es recursiva indirecta.
// Toda función recursiva debe disponer de un mecanismo que le permita detener
// el ciclo de llamadas.
// El ejemplo clásico de función recursiva es la función de los números factoriales.
//
#include <iostream>
using namespace std;
double potencia(double base, int exponente); // Prototipo de la función potencia.
int main()
{
system("color FD");
cout << endl << endl << endl;
double x{ 2.0 };
123

double resultado{};
// Calcular la base x elevada a la potencia -3 hasta +3.
for (int indice{ -3 }; indice <= 3; indice++)
cout << x << " elevado a la potencia " << indice << " es "
<< potencia(x, indice) << endl;
cout << endl << endl << endl;
}
// Declaración de la función potencia, usando recursividad.
double potencia(double base, int exponente)
{
if (exponente < 0)
{
base = 1.0 / exponente; // 1.0 es tipo double.
exponente = -exponente;
}
if (exponente > 0)
return base * potencia(base, exponente - 1);
else
return 1.0;
}

// IH110.cpp: Pointers to functions. Punteros a funciones. Sintaxis.


//
// Un puntero guarda la dirección de memoria de una variable, de una matriz y
// también de una función.
// Mediante un puntero a una función es posible llamar a la función.
// El puntero a una función debe contener la dirección de la función que se desea
// llamar.
// Además, debe contener información sobre la lista de parámetros de la función y
// el tipo de dato de retorno de la función.
// El tipo de dato de un puntero a una función debe incorporar los tipos de
// parámetros y el tipo de dato de retorno de la función a la que apunta.
124

// Si se ha declarado un puntero a una función que tiene un parámetro de tipo int, y


// que devuelve un dato de tipo double, ese puntero sólo podrá apuntar a funciones
// con esas características.
//
#include <iostream>
using namespace std;
long suma(long numero1, long numero2); // Prototipo de la función suma().
double cartas(char* mazo, int juego); // Prototipo de la función cartas().
int main()
{
system("color FD");
cout << endl << endl << endl;
// Declarando punteros a funciones.
/* Para declarar un puntero a una función que recibe dos argumentos, de tipo char*
e int, y que devuelve un valor de tipo double, se hace:
*/
double (*rFunc1)(char*, int); // Declaración de puntero a una función.
rFunc1 = cartas; // Asignación del puntero a la función cartas().
/* A primera vista, los paréntesis alrededor del nombre del puntero y el asterisco
se muestran bastante extraños. Estos paréntesis son esenciales. Sin ellos, la
instrucción estaría declarando una función y no un puntero.
La forma general de declarar un puntero a una función es la siguiente:
tipo_de_retorno (*nombre_del_puntero)(lista de parámetros);
El puntero declarado sólo puede apuntar a funciones que coinciden con las
indicaciones de tipo de retorno y lista de parámetros (ordenados).
*/
//-----------------------------------------------------------------------------------------------
/* Se puede inicializar un puntero con el nombre de la función a la que está
apuntando.
Esto se hace en la misma declaración del puntero.
*/
// long suma(long numero1, long numero2);
// Ver prototipo de la función suma().
125

long (*rSuma)(long, long) { suma };


cout << endl << endl << endl;
}
// Declaración de la función suma().
long suma(long numero1, long numero2)
{
return (numero1 + numero2);
}
// Declaración de la función cartas().
double cartas(char* mazo, int juego)
{
return 3.3;
}

// IH111.cpp: Pointers to functions. Punteros a funciones. Ejercicios.


//
// Se puede inicializar un puntero en su declaración utilizando la palabra clave
// "auto".
// auto rPuntero = funcion;
// El prototipo de la función "funcion" debe estar declarado previamente.
//
#include <iostream>
using namespace std;
long suma(long numero1, long numero2); // Prototipo de la función suma().
long resta(long numero1, long numero2); // Prototipo de la función resta().
long producto(long numero1, long numero2); // Prototipo de la función producto().
int main()
{
system("color FD");
cout << endl << endl << endl;
/* El siguiente puntero es de carácter general. No está asociado específicamente
a ninguna función.
126

Lo que vale es el tipo de puntero a función. La función tiene que devolver un


número de tipo long, y utilizar dos argumentos también de tipo long.
*/
long (*rPuntero)(long, long);
rPuntero = suma; // Asignación del puntero a suma().
cout << "La suma de 17 y 34 es " << rPuntero(17, 34) << endl << endl;
rPuntero = resta; // Asignación del puntero a resta().
cout << "La resta de 67 y 34 es " << rPuntero(67, 34) << endl << endl;
rPuntero = producto; // Asignación del puntero a producto().
cout << "El producto de 11 y 33 es " << rPuntero(11, 33) << endl << endl;
rPuntero = suma; // Asignación del puntero a suma().
cout << "La expresión 7 * ( 75 - 16 ) + 43 es "
<< rPuntero(producto(7, resta(75, 16)), 43);
cout << endl << endl << endl;
}
// Definición de la función suma().
long suma(long numero1, long numero2)
{
return (numero1 + numero2);
}
// Definición de la función resta().
long resta(long numero1, long numero2)
{
return (numero1 - numero2);
}
// Definición de la función producto().
long producto(long numero1, long numero2)
{
return (numero1 * numero2);
}
127

// IH112.cpp: A Pointer to a function as an argument. Un puntero a una función como un


// argumento.
//
// Un puntero a una función es un tipo de datos absolutamente razonable. El
// compilador lo reconoce perfectamente.
// Por esa razón, un puntero a una función puede ser un parámetro válido en la lista
// de argumentos de una función.
// La función así tiene la capacidad de llamar a la otra función que está en su lista
// de argumentos.
// De este modo, un solo puntero es capaz de usarse para llamar distintas funciones,
// dado que todas ellas tengan las mismas características de llamada.
// Incluso, es factible pasar explícitamente el nombre de la función como el
// argumento para un parámetro que es del tipo puntero a una función.
//
#include <iostream>
using namespace std;
double cuadrado(double base); // Prototipo de la función al cuadrado.
double cubo(double base); // Prototipo de la función al cubo.
double sumaMatriz(const double datos[], size_t largo, double (*rSumaM) (double));
int main()
{
system("color FD");
cout << endl << endl << endl;
double datos[]{ 3.2, 9.7, 4.8, 3.3, 5.0, 6.1, 9.3, 2.2 };
size_t largo{ _countof(datos) };
cout << "La suma de los cuadrados de la matriz es "
<< sumaMatriz(datos, largo, cuadrado)
<< endl << endl << endl;
cout << "La suma de los cubos de la matriz es " << sumaMatriz(datos, largo, cubo);
cout << endl << endl << endl;
}
// Definición de la función cuadrado().
double cuadrado(double base)
128

{
return base * base;
}
// Definición de la función cubo().
double cubo(double base)
{
return base * base * base;
}
// Definición de la función sumaMatriz().
double sumaMatriz(const double datos[], size_t largo, double (*rSumaM) (double))
{
double total{}; // Variable acumulativa.
for (size_t i{}; i < largo; i++)
total += rSumaM(datos[i]);
return total;
}

// IH113.cpp: Arrays of pointers to functions. Matrices de punteros a funciones.


//
// Así como se puede crear una matriz de punteros comunes, también se puede
// hacer una matriz de punteros a funciones.
// De la misma manera, las matrices se pueden inicializar en las declaraciones.
//
/* double suma(const double, const double); // Prototipo de función
double producto(const double, const double);// Prototipo de función
double resta(const double, const double); // Prototipo de función
double (*rPuntero[])(const double, const double)
{
suma, producto, resta
}; // Matriz de punteros a funciones.
*/
// Cada elemento de la matriz de punteros a funciones es inicializado por la
129

// correspondiente dirección de memoria de la función en la lista de inicialización.


// El tamaño de la matriz es determinado por el número de valores en la lista. Aquí
// no se puede usar la palabra clave auto para deducir el tipo de matriz. El tipo
// debe hacerse visible aquí.
// Para llamar a la función producto(), usando el segundo elemento de la matriz de
// punteros, se escribe así:
// rPuntero[1] (2.3, 5.6)
//
#include <iostream>
using namespace std;
void pubMensaje(const char mensaje[] = "Algo está mal.");
// Prototipo con valor inicial por defecto.
int main()
{
system("color FD");
cout << endl << endl << endl;
/* Inicializando parámetros de las funciones.
Con todas las funciones que hemos usado, hemos tenido que tener el cuidado de
proveer un argumento correspondiente a cada parámetro de una llamada de función.
Sería muy favorable el poder omitir uno o más argumentos en una llamada de función,
y tener algunos valores por defecto proveídos automáticamente para los argumentos
dejados pendientes.
Esto se puede arreglar proveyendo valores iniciales para los parámetros en los
prototipos de las funciones.
*/
const char miMsj[]{ "El fin del mundo se acerca." };
pubMensaje("Este es mi nuevo mensaje.");
pubMensaje("Ahora lo estoy diciendo así.");
pubMensaje("Algo está realmente mal.");
pubMensaje();
pubMensaje(miMsj);
cout << endl << endl << endl;
}
130

// Declaración de la función pubMensaje(). Si al llamar la función pubMensaje(), se omite


// el argumento, es decir, no se especifica ningún mensaje, la función publicará el mensaje
// declarado en el prototipo de la función.
void pubMensaje(const char mensaje[])
{
cout << endl << endl
<< mensaje;
}

// IH114.cpp: Omitir argumentos en las llamadas de funciones.


//
// Cuando tenemos una función con varios argumentos, podemos proveer valores
// iniciales a tantos argumentos como queramos. Si deseamos omitir más de un
// argumento, para aprovechar el uso de los valores por defecto que se ponen en los
// prototipos de las funciones, todos los argumentos hacia la derecha del argumento
// que deseamos omitir deben también ser dejados pendientes.
// Si deseamos omitir únicamente un argumento de la lista, éste debe ser el último.
// Si queremos omitir en medio o al principio de la lista, tendremos que omitir todos
// los argumentos restantes.
// Por lo tanto, si vamos a omitir uno o dos argumentos, o unos pocos, deben dejarse
// para la parte final de la lista. En el prototipo, serán los que tengan definidos
// sus valores por defecto.
//
#include <iostream>
using namespace std;
int matematician(long arg1 = 10, long arg2 = 20, long arg3 = 30, long arg4 = 40);
int main()
{
system("color FD");
cout << endl << endl << endl;
cout << "La primera suma es " << matematician() << endl << endl;
cout << "La segunda suma es " << matematician(33) << endl << endl;
131

cout << "La tercera suma es " << matematician(33, 44) << endl << endl;
cout << "La cuarta suma es " << matematician(33, 44, 55) << endl << endl;
cout << "La quinta suma es " << matematician(33, 44, 55, 66) << endl << endl;
cout << endl << endl << endl;
}
// Declaración de la función matematician().
int matematician(long arg1, long arg2, long arg3, long arg4)
{
int suma = arg1 + arg2 + arg3 + arg4;
return suma;
}

// IH115.cpp: Exceptions. Excepciones.


//
// Las excepciones son mecanismos que nos permiten darnos cuenta de errores y
// fallas en los procedimientos, unas esperadas y otras inesperadas, que es preciso
// corregir dentro del programa mismo, si ello es posible, y si no, pues dar por
// terminado el procedimiento sin daño o con el mínimo daño.
// Por ejemplo, el operador new lanza una excepción si la memoria RAM disponible
// no es suficiente para cubrir el requerimiento del operador.
// De manera particular, se ha usado la palabra clave if para probar una condición
// que pueda significar un error dentro de los cálculos.
// Una manera general de señalar errores consiste en acceder al sistema
// excepcional que tiene programado el c++ cuando ocurren errores fatales en la
// ejecución. Este sistema no es un sustituto de los chequeos ordinarios de errores
// que se deben instalar dentro de los programas, usando el código del usuario.
// Las excepciones son recursos extras, entendidos como modos extraordinarios de
// captar fallas de naturaleza no prevista, y que no es posible prevenir en el código
// normal.
// Una excepción puede ocurrir por una falla física en la lectura de datos dentro
// de un archivo en un disco duro. O en una lectura dentro de una usb.
// El mecanismo de excepción usa tres palabras claves: try - throw - catch.
132

//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
int cuentas[]{ 34, 54, 0, 27, 10, 0 };
double tiempo{ 60 };
int hora{};
for (auto cuenta : cuentas)
{
try
{
cout << "\n\nLa hora es " << ++hora;
if (0 == cuenta)
throw("Cuenta cero, el cálculo no es posible.");
cout << " minuto(s) por ítem: " << tiempo / cuenta;
}
catch (const char mensaje[])
{
cout << endl << endl << mensaje << endl << endl;
}
}
/* Dentro de un bloque "try", se puede lanzar una excepción mediante la palabra clave
"throw".
El operando de la instrucción <throw> determina el tipo de excepción. En este caso,
el tipo de operando es un literal o cadena de caracteres, por lo tanto, es del tipo "const
char[]". El operando que sigue a la instrucción throw puede ser cualquier expresión,
y el tipo del resultado de la expresión determina el tipo de excepción que se está
lanzando. Debe existir un bloque "catch" para coger el tipo de excepción lanzado.
Las excepciones también pueden ser lanzadas en las funciones que son llamadas
desde adentro de un bloque try, y pueden ser cogidas por un bloque catch puesto a
133

continuación del bloque try, si no son capturadas dentro de la función.


void probarThrow()
{
throw " Cuenta cero - el cálculo no es posible.";
}
Y en vez del throw anterior, en el ejemplo, se llama a la función probarThrow():
if(0 == cuenta)
probarThrow();// Llamada a una función que lanza una excepción.
*/
cout << endl << endl << endl;
}

// IH116.cpp: Exceptions. Throwing and catching exceptions. Excepciones. Lanzar y


// capturar excepciones. (2). probarThrow().
//
// Las excepciones son mecanismos que nos permiten darnos cuenta de errores y
// fallas en los procedimientos, unas esperadas y otras inesperadas, que es preciso
// corregir dentro del programa mismo, si ello es posible, y si no, pues dar por
// terminado el procedimiento sin daño o con el mínimo daño.
// Por ejemplo, el operador new lanza una excepción si la memoria RAM disponible
// no es suficiente para cubrir el requerimiento del operador.
// De manera particular, se ha usado la palabra clave if para probar una condición
// que pueda significar un error dentro de los cálculos.
// Una manera general de señalar errores consiste en acceder al sistema
// excepcional que tiene programado el c++ cuando ocurren errores fatales en la
// ejecución. Este sistema no es un sustituto de los chequeos ordinarios de errores
// que se deben instalar dentro de los programas, usando el código del usuario.
// Las excepciones son recursos extras, entendidos como modos extraordinarios de
// captar fallas de naturaleza no prevista, y que no es posible prevenir en el código
// normal.
// Una excepción puede ocurrir por una falla física en la lectura de datos dentro
// de un archivo en un disco duro. O en una lectura dentro de una usb.
134

// El mecanismo de excepción usa tres palabras claves: try - throw - catch.


//
#include <iostream>
using namespace std;
void probarThrow(); // Prototipo de la función probarThrow().
int main()
{
system("color FD");
cout << endl << endl << endl;
int cuentas[]{ 34, 54, 0, 27, 10, 0 };
double tiempo{ 60 };
int hora{};
for (auto cuenta : cuentas)
{
try
{
cout << "\n\nLa hora es " << ++hora;
if (0 == cuenta)
probarThrow();
cout << " minuto(s) por ítem: " << tiempo / cuenta;
}
catch (const char mensaje[])
{
cout << endl << endl << mensaje << endl << endl;
}
}
/* Dentro de un bloque "try", se puede lanzar una excepción mediante la palabra clave
"throw".
El operando de la instrucción <throw> determina el tipo de excepción. En este caso,
el tipo de operando es un literal o cadena de caracteres, por lo tanto, es del tipo "const
char[]".
El operando que sigue a la instrucción throw puede ser cualquier expresión, y el tipo
del resultado de la expresión determina el tipo de excepción que se está lanzando.
135

Debe existir un bloque "catch" para coger el tipo de excepción lanzado.


Las excepciones también pueden ser lanzadas en las funciones que son llamadas
desde adentro de un bloque try, y pueden ser cogidas por un bloque catch puesto a
continuación del bloque try, si no son capturadas dentro de la función.
void probarThrow()
{
throw " Cuenta cero - el cálculo no es posible.";
}
Y en vez del throw anterior, en el ejemplo, se llama a la función probarThrow():
if(0 == cuenta)
probarThrow(); // Llamada a una función que lanza una excepción.
*/
cout << endl << endl << endl;
}
// Definición de la función probarThrow().
void probarThrow()
{
throw " Conteo cero - el cálculo no es posible.";
}

// IH117.cpp: Exceptions. Throwing and catching exceptions. Excepciones. Lanzar y


// capturar excepciones. (3). Catching exceptions. Nested try blocks. Capturar
// excepciones. Bloques try anidados.
//
// Se debe suplir por lo menos un bloque <catch> inmediatamente después de un
// bloque <try>.
// Un bloque catch captura todas las excepciones del tipo especificado en la
// sentencia throw.
// La captura incluye todas las ocurrencias de errores, fallas y excepciones del
// sistema, considerando todos los casos no captados por ninguna función llamada
// directamente o indirectamente en el bloque try.
// Si se desea especificar que un bloque catch capture cualquier tipo de excepción
136

// lanzada en un bloque try, debe escribirse una elipsis (...) entre paréntesis dentro
// de la declaración de la excepción, así:
// catch (...)
// {
// código para administrar cualquier excepción.
// }
// Este bloque catch capta excepciones de cualquier tipo. Debe aparecer de último
// si se tiene otros bloques catch definidos para el mismo bloque try.
// try
// {
// …
// }
// catch (const tipo1& exam)
// {
// …
// }
// catch (tipo2 exam)
// {
// …
// }
// Estos bloques catch son probados en secuencia, hasta que una de las excepciones
// coincida.
// Esta será ejecutada inmediatamente.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
int altura{};
const int alturaMin{ 9 };
const int alturaMax{ 100 };
137

const double Pulg_a_Mt{ 0.0254 };


char ch{ 'y' };
try
{
while ('y' == ch || 'Y' == ch)
{
cout << "\nEscriba una altura en pulgadas: ";
cin >> altura; // Leer el dato de altura que se va a convertir.
// El siguiente bloque try-catch-throw permite definir, capturar y
// lanzar una excepción en la ejecución del código.
try
{
if (altura > alturaMax)
// Excepción que se lanza.
throw "\nLa altura excede el máximo.";
if (altura < alturaMin)
// Excepción que se lanza.
throw altura;
cout << endl << static_cast<double>(altura)* Pulg_a_Mt
<< " metros" << endl;
}
catch (const char aMensaje[]) // Inicio del bloque de captura (catch).
{
cout << aMensaje << endl; // const char[].
}
cout << "\n¿Desea continuar(y o n)? ";
cin >> ch;
}
}
catch (int alturaMala)
{
cout << endl << alturaMala << " pulgadas está debajo del mínimo." << endl;
}
138

cout << endl << endl << endl;


}

// IH118.cpp: Exceptions. Throwing and catching exceptions. Excepciones. Lanzar y


// capturar excepciones. (4). Rethrowing exceptions. Relanzar excepciones.
//
// Es factible recibir una excepción en un bloque catch y pasarla al programa
// llamador para una acción adicional. Es decir, se divide el procesamiento en dos
// partes. Para hacerlo, se relanza la excepción para procesamiento posterior.
// Ejemplo:
// try
// {
// …
// }
// catch (const tipo1& error)
// {
// …
// }
// catch (tipo2 error)
// {
// Procesar la excepción
// throw; // Relanzar la excepción para que la procese
// } // la función llamadora.
//
// Al usar throw sin ninguna expresión como operando se provoca que se relance
// la excepción que se está manejando.
// Sólo se puede usar throw sin operando dentro de un bloque catch.
// Al relanzar la excepción, se permite que otro bloque de nivel superior, anidado,
// pueda captar esta excepción. Y también se permite que el programa o función
// llamadora pueda captarla dentro de un bloque try que contenga la llamada.
//
#include <iostream>
139

using namespace std;


// Definición de tres estructuras para procesar tres tipos de errores
// relacionados. E, E1 y E2.
struct E {
const char* mensaje;
E() : mensaje("Error tipo clase E") { }
};
struct E1 : E {
const char* mensaje1;
E1() : mensaje1("Error tipo clase E1") { }
};
struct E2 : E {
const char* mensaje2;
E2() : mensaje2("Error tipo clase E2") { }
};
// Definición de la función A.
void funcionA() {
try
{
cout << "Dentro del bloque try de la funcionA():" << endl << endl;
cout << "Lanzando excepción del tipo E1" << endl << endl;
E1 miExcepcion;
throw miExcepcion;
}
catch (E2& error) {
cout << "Dentro del proceso de la funcionA(), catch (E2& error)" << endl << endl;
cout << "Excepción: " << error.mensaje << endl << endl;
throw; // Relanzando la excepción.
}
catch (E1& error) {
cout << "Dentro del proceso de la funcionA(), catch (E1& error)" << endl << endl;
cout << "Excepción: " << error.mensaje << endl << endl;
throw; // Relanzando la excepción.
140

}
catch (E& error) {
cout << "Dentro del proceso de la funcionA(), catch (E& error)" << endl << endl;
cout << "Excepción: " << error.mensaje << endl << endl;
throw; // Relanzando la excepción.
}
}
int main()
{
system("color FD");
cout << endl << endl << endl;
// Bloque try principal.
try
{
cout << "Dentro del bloque try principal de la función main():" << endl << endl;
funcionA(); // Se ejecuta la funciónA(), la cual verifica los errores.
}
catch (E2& error) {
cout << "Dentro del proceso de la función main(), catch (E2& error)" << endl << endl;
cout << "Excepción: " << error.mensaje << endl << endl;
}
catch (...) {
cout << "Dentro del proceso de la función main(), catch (...)" << endl << endl;
}
cout << endl << endl << endl;
}
141

// IH119.cpp: Exception handling in the MFC (Microsoft Foundation Classes). Manejo de


// excepciones dentro de MFC.
//
// El c++ tiene su propio sistema try - throw - catch. Lo mismo tiene el MFC, aunque
// su sistema es ya obsoleto. Ambos sistemas son independientes.
// En el MFC, estos son macros elaborados con el propio lenguaje de Microsoft.
// A pesar de su obsolescencia, estos macros aún permanecen en el MFC por dos
// razones.
// Una es que se mantienen para asegurar que los códigos viejos puedan funcionar,
// si los incluyen.
// La otra es que en las partes del MFC que se manejan excepciones, lo hacen con
// estos macros.
// Una anomalía de este sistema es que el tipo de excepción que usa es de tipo clase.
// Y aunque el tipo es clase, se necesita captar la excepción como un puntero y no
// como una clase. Y si no se relanza la excepción, es preciso borrar la excepción
// usando Delete().
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
/* try
{
... // Ejecute algún código que podría lanzar una excepción de MFC.
}
catch (CException* errorX)
{
... // Procese la excepción aquí.
errorX->Delete(); // Borrar el objeto excepción errorX.
} */
/* No debería usarse Delete para borrar el objeto de excepción, porque el objeto
142

podría no estar alojado en el heap.


CException es la clase base de todas las excepciones en MFC, de modo que el
bloque catch captará excepciones MFC de cualquier clase. */
cout << endl << endl << endl;
}

// IH120.cpp: Handling memory allocation errors. Manipular errores de asignación de


// memoria.
//
// Hasta ahora, se ha usado el operador new asumiendo que no hay problema en el
// acto de asignación de memoria para las nuevas variables, matrices, etc., que
// aloja el operador new.
// El que se genere un problema depende de la cantidad de memoria disponible, y
// de la que los programas que estén en ejecución demanden del sistema operativo.
// Cuando la memoria no es suficiente, el procesador emite una excepción, la
// cual por lo común hace que se termine la ejecución del programa.
// En general, es muy poco lo que se puede hacer cuando ya no hay memoria a la
// que se pueda acceder. Por otro lado, a veces es posible tener alguna alterna-
// tiva, o por lo menos se pueda recurrir a reportar la situación de manera par-
// ticular.
//
#include <iostream>
#include <new> // Para el tipo de excepción bad_alloc.
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
char* rDatos{}; // Puntero del tipo char*.
size_t cuenta{ ~static_cast<size_t>(0) / 2 };
/* La variable cuenta es del tipo size_t. Esta variable se usará para especificar
el tamaño de la matriz del tipo char[] apuntada por la dirección de memoria
143

contenida en el puntero rDatos, del tipo char*. El tamaño de una matriz es


siempre del tipo size_t.
El valor para la variable cuenta se genera mediante una expresión complicada.
El valor (0) es de tipo entero, int. Se convierte a un valor 0 del tipo size_t
haciendo uso de static_cast<size_t>(0).
Se aplica el operador ~ para cambiar todos los bits 0 a 1. Este cambio nos da
el valor más alto que se puede obtener con un tipo de datos size_t, porque
size_t es un valor sin signo.
Este valor excede la cantidad máxima de memoria que se puede asignar de una
vez. Por eso, se divide la cantidad entre dos para hacer más factible la obten-
ción de memoria dinámica.
Aún así, el valor es muy elevado, y sólo con máquinas muy excepcionales dota-
das de mucha memoria se puede lograr la asignación. Lo más probable es que
falle.
*/
try
{
rDatos = new char[cuenta]; // Uso de memoria dinámica adicional.
cout << "Memoria dinámica asignada." << endl << endl;
}
catch (bad_alloc& errorX)
{
cout << "La asignación de memoria dinámica falló." << endl << endl
<< "La información sobre el objeto excepción es: "
<< errorX.what() << endl << endl;
}
delete[] rDatos;
cout << endl << endl << endl;
}
144

// IH121.cpp: Function overloading. Sobrecarga de funciones.


//
// La sobrecarga de funciones permite utilizar varias funciones con el mismo
// nombre siempre que cada una de ellas tenga una diferente lista de parámetros.
// Cuando una de las funciones es llamada, el compilador escogerá la función
// correcta basándose en la lista de parámetros.
// La lista de cada función debe ser unívoca, es decir, sin posibilidad de confusión
// para el compilador.
// La lista de parámetros puede variar en la cantidad de parámetros. Uno de ellos,
// al menos, debe coincidir con las demás listas.
// El tipo de dato que devuelve la función no es un criterio de selección para el
// compilador.
// MMM: En todo programa, cada función tiene una signatura, o sea, una firma de
// función.
// La signatura está determinada por el nombre de la función y su lista de
// parámetros.
// Todas las funciones deben tener signaturas unívocas para que el compila-
// dor las acepte.
#include <iostream>
using namespace std;
// Prototipo de función max(); datos int.
int max(const int datos[], const size_t longitud);
// Prototipo de función max(); datos long.
long max(const long datos[], const size_t longitud);
// Prototipo de función max(); datos double.
double max(const double datos[], const size_t longitud);
int main()
{
system("color FD");
cout << endl << endl << endl;
int matrizPeq[]{ 1, 24, 34, 22 };
long matrizMed[]{ 23, 245, 123, 1, 234, 2345 };
double matrizLarga[]{ 23.0, 1.4, 2.456, 345.5, 12.0, 21.0 };
145

const size_t longPeq{ _countof(matrizPeq) };


const size_t longMed{ _countof(matrizMed) };
const size_t longLarga{ _countof(matrizLarga) };
cout << endl << "El valor máximo en la matriz pequeña (int) es: "
<< max(matrizPeq, longPeq);
cout << endl << endl << "El valor máximo en la matriz mediana (long) es: "
<< max(matrizMed, longMed);
cout << endl << endl << "El valor máximo en la matriz larga (double) es: "
<< max(matrizLarga, longLarga);
cout << endl << endl << endl;
}
// Definición de la función max(), valor entero.
int max(const int datos[], const size_t longitud)
{
int maximo{ datos[0] };
for (size_t i{ 1 }; i < longitud; i++)
if (maximo < datos[i])
maximo = datos[i];
return maximo;
}
// Definición de la función max(), valor long.
long max(const long datos[], const size_t longitud)
{
long maximo{ datos[0] };
for (size_t i{ 1 }; i < longitud; i++)
if (maximo < datos[i])
maximo = datos[i];
return maximo;
}
// Definición de la función max(), valor double.
double max(const double datos[], const size_t longitud)
{
double maximo{ datos[0] };
146

for (size_t i{ 1 }; i < longitud; i++)


if (maximo < datos[i])
maximo = datos[i];
return maximo;
}

// IH122.cpp: Reference types and overload selection. Tipos de parámetros de referencia y


// selección de funciones por sobrecarga.
//
// En la sobrecarga de funciones, si usamos parámetros por referencia, debemos
// tener el cuidado de asegurar que el compilador tenga la posibilidad de selec-
// cionar el tipo de sobrecarga que sea apropiado.
// Suponer que se definen dos funciones con los siguientes prototipos:
// void funcionA(int n);
// void funcionB(int& fN);
// Las dos funciones tienen distintos tipos de parámetros. Sin embargo, el
// programa no va a compilar. Al llamar la función, el compilador no podrá dis-
// tinguir entre los dos tipos de parámetros, pues ambos son iValor, y ambos
// son igualmente válidos.
// MMM: En general, no se puede sobrecargar entre un tipo de dato y una refe-
// rencia iValor a ese tipo de dato. Ejemplo: int, int&.
//
// Por otro lado, el compilador sí es capaz de distinguir entre lo siguiente:
// void funcionA(int& arg); // Parámetro de referencia, iValor.
// void funcionB(int&& arg); // Parámetro de referencia, dValor.
//
// En ciertos casos, el compilador puede aplicar cualquier función, y tiene la
// preferencia por una de ellas. La función void funcionA(int& arg) siempre
// tendrá la preferencia cuando el argumento es un iValor.
/* int num{5};
f(num); // Llama a f(int&).
f(2 * num); // Llama a f(int&&)
147

f(25); // Llama a f(int&&)


f(num++); // Llama a f(int&&)
f(++num); // Llama a f(int&)
*/
// Sólo las instrucciones al principio y al final llaman a la sobrecarga con
// un parámetro de referencia que constituye un iValor. Los demás son dValor.
// MMM: El parámetro de referencia del tipo dValor está diseñado para resolver
// problemas específicos, que se verán cuando estudiemos las clases.
//
#include <iostream>
using namespace std;
int f(int numero); // Prototipo de función con sobrecarga.
long f(long numero); // Prototipo de función con sobrecarga.
double f(double numero); // Prototipo de función con sobrecarga.
int main()
{
system("color FD");

cout << endl << endl << endl;


int numero{ 5 };
f(numero); // 5
f(4 * numero); // 20
f(250); // 250
f(numero++); // 5
cout << endl << "El número ahora es: " << numero; // 6
f(++numero); // 7
cout << endl << "El número ahora es: " << numero; // 7
/* MMM: Todas las procesó el compilador satisfactoriamente. */
cout << endl << endl << endl;
}
// Definición de la función f(int).
int f(int numero)
{
148

cout << endl << "El número es: " << numero;
return numero;
}
// Definición de la función f(long).
long f(long numero)
{
cout << endl << "El número es: " << numero;
return numero;
}
// Definición de la función f(double).
double f(double numero)
{
cout << endl << "El número es: " << numero;
return numero;
}

// IH123.cpp: Using a function template. Usar una plantilla de funciones.


//
// En el programa IH122.cpp tuvimos que repetir el mismo código, cambiando
// únicamente el tipo de datos, varias veces. Con un programa mucho más largo eso
// se vuelve tedioso y trabajoso.
// Las plantillas de funciones son una técnica para evitar esa carga pesada.
// Se puede crear una única plantilla de una función, para que el compilador se
// encargue de generar la misma función para distintos tipos de parámetros.
// El código que permite al compilador generar un grupo de funciones similares se
// llama plantilla de funciones.
// Una plantilla de funciones tiene uno o varios parámetros tipo. Luego, podemos
// generar una función en particular aportando un parámetro tipo en particular. Así,
// las funciones que genera el compilador a partir de una plantilla, todas tienen el
// mismo código básico, y se adapta el parámetro tipo que se aporta.
//
#include <iostream>
149

using namespace std;


// Prototipo de la clase template max().
template<class T> T max(const T datos[], const size_t longitud);
/* La palabra clave "template" identifica ésta como una definición de plantilla. Los signos
"menor que" y "mayor que" (<>) que acompañan a la palabra clave, encierran los parámetros
tipo que el compilador usa para crear una instancia de la función, separados por comas.
En este caso particular, sólo tenemos un parámetro tipo: T.
La palabra clave "class" que se escribe antes de T, indica que la T es el parámetro tipo
de esta plantilla. "class" es el término genérico para "tipo", o sea, T. Una clase es un
tipo especial de dato del usuario, definido por el usuario.
Además de los datos fundamentales del c++, tales como "int", "char", están los datos
definidos por el usuario.
En vez de la palabra clave "class", es posible también usar la palabra clave "typename".
Así: template<typename T> T max(const T datos[], const size_t longitud);
Dondequiera que T aparece en una definición de plantilla de función, ésta es reemplazada
por el parámetro tipo específico aportado por el usuario.
La creación de una instancia específica de función se denomina instanciación.
Cada vez que se use max() en el programa, el compilador chequeará si existe una función
correspondiente al tipo de argumentos que se proveen en la llamada a la función. Si no
existe así, el compilador creará una sustituyendo el argumento tipo que el usuario ha
usado en la llamada a la función, en lugar del parámetro T dentro de la definición de la
plantilla de función.

MMM: El compilador sólo procesará una definición de plantilla si es usada en el


código. Si no se usa, los errores en la plantilla no serán identificados.
*/
int main()
{
system("color FD");
cout << endl << endl << endl;
int matrizPeq[]{ 1, 24, 34, 22 };
long matrizMed[]{ 23, 245, 123, 1, 234, 2345 };
double matrizLarga[]{ 23.0, 1.4, 2.456, 345.5, 12.0, 21.0 };
150

const size_t longPeq{ _countof(matrizPeq) };


const size_t longMed{ _countof(matrizMed) };
const size_t longLarga{ _countof(matrizLarga) };
cout << endl << "El valor máximo en la matriz pequeña (int) es: "
<< max(matrizPeq, longPeq);
cout << endl << endl << "El valor máximo en la matriz mediana (long) es: "
<< max(matrizMed, longMed);
cout << endl << endl << "El valor máximo en la matriz larga (double) es: "
<< max(matrizLarga, longLarga);
cout << endl << endl << endl;
}
// Definición de la plantilla (template) de la función max().
template<class T> T max(const T datos[], const size_t longitud)
{
T maximum{ datos[0] };
for (size_t i{ 1 }; i < longitud; i++)
if (maximum < datos[i])
maximum = datos[i];
return maximum;
}

// IH124.cpp: Using the decltype operator. Usar el operador decltype(expression).


//
// El operador decltype(expression) sirve para averiguar el tipo de dato de una
// expresión. Ejemplo:
// double x {100.0};
// int n{ 5 };
// decltype(x*n) resultado(x*n);
// La última instrucción hace que el tipo de dato de "resultado" sea el tipo de
// dato de la expresión x*n, cuyo tipo es double.
// El uso primario para el operador decltype es en la construcción de plantillas
// de las funciones.
151

// En algunas ocasiones, el tipo de retorno de una plantilla de función depende


// de la multiplicidad de tipos de parámetros que pueda usar, y de los tipos que
// el usuario utilice para instanciar una función.
//
#include <iostream>
using namespace std;
template<typename T1, typename T2>
auto f(T1 v1[], T2 v2[], const size_t cuenta) -> decltype(v1[0] * v2[0]);
template<typename T1, typename T2>
auto producto(T1 v1[], T2 v2[], const size_t cuenta) -> decltype(v1[0] * v2[0]);
int main()
{
system("color FD");
cout << endl << endl << endl;
double x[]{ 100.5, 99.5, 88.7, 77.8 };
short y[]{ 3, 4, 5, 6 };
long z[]{ 11L, 12L, 13L, 14L };
size_t n{ _countof(x) };
cout << "El tipo de dato del resultado es " << typeid(producto(x, y, n)).name()
<< endl << endl;
cout << "El resultado es " << producto(x, y, n) << endl << endl;
auto resultado = producto(z, y, n);
cout << "El tipo de dato del resultado es " << typeid(resultado).name()
<< endl << endl;
cout << "El resultado es " << resultado << endl;
cout << endl << endl << endl;
}
/*
La siguiente definición no funcionará. "return_type" necesita ser el tipo del resultado
de multiplicar los correspondientes elementos de los argumentos de las matrices. El
operador decltype podría ayudar, pero no pasará la prueba de la compilación debido a
que v1 y v2 no estarán previamente definidas.
// Definición de la plantilla de return_type().
152

template<typename T1, typename T2>


return_type f(T1 v1[], T2 v2[], const size_t cuenta)
{
decltype(v1[0] * v2[0]) suma{};
for (size_t i{}; i < cuenta; i++) suma += v1[i] * v2[i];
return suma;
}
template<typename T1, typename T2>
decltype(v1[0]*v2[0]) f(T1 v1[], T2 v2[], const size_t cuenta) // Esto no va a compilar.
{
decltype(v1[0]*v2[0]) suma {};
for(size_t i {}; i<cuenta; i++) suma += v1[i]*v2[i];
return suma;
}
Se requiere una sintaxis diferente para lograr el resultado.
template<typename T1, typename T2>
auto f(T1 v1[], T2 v2[], const size_t cuenta) -> decltype(v1[0]*v2[0])
{
decltype(v1[0]*v2[0]) suma {};
for(size_t i {}; i<cuenta; i++) suma += v1[i]*v2[i];
return suma;
}
Lo anterior es definido como "trailing return type", tipo de retorno diferido.
*/
template<typename T1, typename T2>
auto f(T1 v1[], T2 v2[], const size_t cuenta) -> decltype(v1[0] * v2[0])
{
decltype(v1[0] * v2[0]) suma{};
for (size_t i{}; i < cuenta; i++) suma += v1[i] * v2[i];
return suma;
}
template<typename T1, typename T2>
auto producto(T1 v1[], T2 v2[], const size_t cuenta) -> decltype(v1[0] * v2[0])
153

{
decltype(v1[0] * v2[0]) suma{};
for (size_t i{}; i < cuenta; i++) suma += v1[i] * v2[i];
return suma;
}
/* Los tipos de datos retornados por las funciones dependen de la expresión:
decltype(v1[0] * v2[0])
Cuando las matrices son del tipo double y short, el compilador convierte el tipo
a double.
El tipo de dato final retornado es del tipo double.
Cuando los argumentos son dos matrices del tipo long y short, el compilador
convierte los tipos de retorno a long.
El tipo de dato de retorno es valorado por la clave "auto", pues no se puede
predecir el tipo que estará saliendo de la función.
El tipo del resultado será el mismo que el tipo del valor retornado por la
función producto(), el cual es determinado por la palabra clave "auto".
template<typename T> auto max(const T x[], const size_t longitud) -> T
{
T maximum {x[0]};
for (size_t i {1}; i < longitud; i++)
if(maximum < x[i])
maximum = x[i];
return maximum;
}
*/
154

// IH125.cpp: Eliminating blanks from a string. Eliminar los espacios en blanco de una
// cadena de caracteres.
//
// Para eliminar espacios en blanco dentro de una cadena de caracteres, un
// procedimiento simple será copiar la cadena entera en otra matriz, y luego
// proceder a explorar un carácter a la vez.
// Se usan dos índices, i, j, para manejar el procedimiento.
// Si el carácter A[i] es un carácter válido, el valor i se hace igual al
// valor j.
// Si el carácter referido es un espacio en blanco, el valor i no se copia al
// valor j. Es decir, el elemento B[i] será el elemento A[i+1], eliminado así
// el espacio en blanco.
//
#include <iostream>
using namespace std;
// Prototipo de la función para eliminar espacios.
void spacius(char* cadena);
int main()
{
system("color FD");
cout << endl << endl << endl;
const int maX{ 80 };
char chainA[maX]{ "Esta cadena es una muy buena prueba de este programa." };
spacius(chainA); // Primero hacer la conversión. La función spacius()
// no devuelve ningún dato. Es del tipo "void".
cout << chainA;
cout << endl << endl << endl;
}
// Definición de la función para eliminar espacios en blanco en una cadena.
void spacius(char* cadena)
{
size_t i{}; // Indice guía para copiar o no copiar.
size_t j{}; // Indice receptor de copia o no copia.
155

while ((*(cadena + i) = *(cadena + j++)) != '\0')


// Repita hasta llegar a \0.
if (*(cadena + i) != ' ') // Incremente i si no es un espacio en blanco.
i++;
return;
}

// IH126.cpp: The struct in C++. La estructura en c++.


//
// Una estructura es un tipo de datos definido por el usuario. Para hacerlo, se usa
// la palabra clave "struct".
// La estructura se originó en el lenguaje C. El lenguaje c++ la ha hecho más
// avanzada.
// Una estructura en c++ es similar a una clase.
// La clase ofrece algunas ventajas adicionales a la estructura, de modo que es
// preferible aprender a usar una clase en vez de una estructura, siempre que ello
// sea posible.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
struct Libro
{
char titulo[80]; // Miembro "titulo"
char autor[80]; // Campo "autor"
char editorial[80];
int fecha;
}; // Es obligatorio escribir este ;
/* En la estructura se puede incluir cualquier tipo de datos, excepto el tipo de
156

datos Libro que se está definiendo. Es posible, eso sí, incluir un puntero al
tipo de datos Libro: Libro*.
*/
Libro miNovela // Instanciar el objeto "miNovela"
{
"Mis cien años de soledad", // Valor inicial para el campo "titulo".
"A. I. Meza", // Los campos están separados por ,
"Imprenta Maxima",
2009 // Sin ; al final.
};
// Cambiar el año al 2019.
miNovela.fecha = 2019;
cout << "La fecha es: " << miNovela.fecha << endl << endl;
// Bajarle dos años a la fecha.
miNovela.fecha -= 2;
cout << "La fecha es: " << miNovela.fecha << endl << endl;
cout << endl << endl << endl;
}

// IH127.cpp: The RECT windows structure. La estructura RECT en Windows.


//
// Los rectángulos se usan mucho en los programas Windows. Con ellos se hacen
// las ventanas.
// Hay una estructura RECT predefinida en el archivo de encabezado windef.h,
// que está incluido en windows.h.
// Su estructura es la siguiente:
/* struct RECT
{
LONG izq; // Coordenadas del punto superior izquierdo
LONG sup; // Coordenadas de la línea superior
LONG der; // Coordenadas del punto inferior derecho
LONG inf; // Coordenadas de la línea inferior
157

};
*/
// El tipo de dato LONG es un tipo de Windows, similar al tipo long en c++.
// windows.h también posee prototipos para una cantidad de funciones que
// permiten manipular y modificar rectángulos.
// InflateRect() aumenta el tamaño de un rectángulo.
// EqualRect() compara dos rectángulos.
// MFC tiene una clase CRect, la cual es equivalente a una estructura RECT.
// Esta clase es mucho más funcional que una estructura RECT.
#include <iostream>
#include <windows.h> // Tiene definida la estructura RECT.
using namespace std;
// Definición de la estructura RECTANG. Si se escribe RECT, el compilador lo toma
// como redefinición, pues ya está en el archivo de encabezado windows.h.
typedef struct _RECT
{
LONG izq; // Coordenadas del punto superior izquierdo
LONG sup; // Coordenadas de la línea superior
LONG der; // Coordenadas del punto inferior derecho
LONG inf; // Coordenadas de la línea inferior
} RECTANG, *PRECTANG;
int main()
{
system("color FD");
cout << endl << endl << endl;
// Crear el objeto miRect, inicializado por defecto.
RECTANG miRect{};
miRect.izq = 90; // En píxeles.
miRect.sup = 60;
miRect.der = 660;
miRect.inf = 330;
cout << endl << endl << endl;
}
158

// IH128.cpp: Using pointers with a struct. Usar punteros con una estructura.
//
// Es factible crear un puntero a un objeto del tipo estructura.
// Muchas de las funciones definidas en windows.h requieren punteros a un objeto
// RECT como argumentos. Esto evita que se tenga que copiar la estructura cuando
// se hace el llamado a la función.
//
#include <iostream>
#include "windows.h"
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
RECT miRect; // Crear un objeto RECT.
RECT* rRect; // Crear un puntero del tipo RECT.
rRect = &miRect; // Asignar el puntero rRect a miRect.
// Definición de una estructura.
struct ListaElementos
{
RECT miRect;
ListaElementos* rSiguiente; // Puntero del tipo estructura ListaElementos.
};
/* El primer miembro de la estructura ListaElementos es del tipo RECT. Y el segundo
miembro es un puntero a una estructura del tipo ListaElementos, el tipo que se ha
definido.
Esto permite a los objetos del tipo ListaElementos permanecer encadenados, con su
dirección de memoria conectada a la dirección de memoria del siguiente objeto
ListaElementos, en una cadena, en la que al final, el último objeto tiene la
dirección "nullptr". Esta dirección indica que ya no hay más objetos.
Este es el diseño de una "lista encadenada", linked list.
La lista encadenada tiene la ventaja de que una vez conocida la dirección de
memoria del primer elemento en la lista, se puede conocer la dirección de todos
159

los demás elementos. Y es muy útil cuando la lista se crea en la memoria dinámica.
Cada nuevo objeto que se crea, es posible añadirlo al final de la lista, guardando
su dirección en el miembro rSiguiente (puntero) del último objeto en la cadena.
*/
cout << endl << endl << endl;
}

// IH129.cpp: Accessing structure members through a pointer. El acceso a miembros de una


// estructura mediante un puntero.
//
// Consideremos un objeto del tipo RECT. Este tipo de estructura está definido en
// windows.h.
// Para tener acceso al objeto, por medio de un puntero, como es obvio primero
// debemos definir un puntero a ese objeto.
// RECT aRect {0, 0, 100, 100};
// RECT* rRect {&aRect};
// El objeto RECT tiene solo dos coordenadas. Así que el objeto aRect tiene dos
// instancias inicializadas. El primer par de miembros está inicializado a (0, 0); y el
// segundo par está inicializado a (100, 100).
// rRect es un puntero del tipo RECT, inicializado a la dirección de memoria del
// objeto aRect.
// Se puede tener acceso a los miembros del objeto aRect por medio del puntero
// con una instrucción así:
// (*rRect).top += 10; // Aumentar la coordenada del miembro
// // superior en 10 unidades.
// Los paréntesis alrededor del puntero dereferenciado son esenciales, pues sin
// ellos tendrá prioridad el operador de acceso al miembro .top, y el compilador
// estará considerando al puntero como una estructura. Lo cual no es funcional. Por
// ello, no será compilado.
//
#include <iostream>
#include "windows.h"
160

using namespace std;


int main()
{
system("color FD");
cout << endl << endl << endl;
RECT aRect{ 0, 0, 100, 100 };
RECT* rRect{ &aRect };
(*rRect).top += 10;
cout << endl << endl << endl;
}

// IH130.cpp: Accessing structure members through a pointer. El acceso a miembros de una


// estructura mediante un puntero. The indirect member selection operator. El
// operador indirecto de selección de miembros de estructuras.
//
// Consideremos un objeto del tipo RECT. Este tipo de estructura está definido en
// windows.h
// Para tener acceso al objeto, por medio de un puntero, como es obvio primero
// debemos definir un puntero a ese objeto.
// RECT aRect {0, 0, 100, 100};
// RECT* rRect {&aRect};
// El objeto RECT tiene solo dos coordenadas. Así que el objeto aRect tiene dos
// instancias inicializadas. El primer par de miembros está inicializado a (0, 0); y el
// segundo par está inicializado a (100, 100).
// rRect es un puntero del tipo RECT, inicializado a la dirección de memoria del
// objeto aRect.
// Se puede tener acceso a los miembros del objeto aRect por medio del puntero
// con una instrucción así:
// (*rRect).top += 10; // Aumentar la coordenada del miembro
// // superior en 10 unidades.
// Los paréntesis alrededor del puntero dereferenciado son esenciales, pues sin
// ellos tendrá prioridad el operador de acceso al miembro .top, y el compilador
161

// estará considerando al puntero como una estructura. Lo cual no es funcional. Por


// ello, no será compilado.
//
// El operador indirecto de selección de miembros de estructuras y clases (->), está
// elaborado específicamente para tener acceso a miembros por medio de un
// puntero.
// rRect->top += 10;
//
#include <iostream>
#include "windows.h"
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
RECT aRect{ 0, 0, 100, 100 };
RECT* rRect{ &aRect };
(*rRect).top += 10; // Puntero rRect dereferenciado para entrar
// a la estructura RECT.
rRect->top += 15; // Operador indirecto de acceso a miembros.
cout << endl << endl << endl;
}

// IH131.cpp: Types, objects, classes, and instances. Tipos de datos, objetos, clases, e
// instancias.
//
// En c++, todas las variables están relacionadas con cierto tipo de datos. Si no
// es posible adelantar el tipo de dato de una variable, entonces se usa la palabra
// clave auto. Es factible también usar decltype.
// Los tipos de datos que hemos visto son: int, short, long, double, float, bool,
// etc. Estos se llaman tipos de datos fundamentales.
// El usuario también puede crear sus propios tipos de datos. Por ejemplo, puede
162

// crear una estructura y ponerle los miembros que desee. Luego, con esta
// estructura tiene la posibilidad de instanciar objetos.
// Los tipos de datos fundamentales son muy limitados en cuanto a la representati-
// vidad que nos pueden proporcionar sobre el mundo real.
// La Programación Orientada a Objetos está diseñada para representar más
// fielmente el mundo real. Está basada en los conceptos de encapsulamiento,
// polimorfismo, herencia.
// Una clase es un tipo de dato creado por el usuario. Muy parecido a una estructura.
// Las clases son tipos de datos que el usuario puede utilizar como los datos nativos
// del c++.
// Las clases difieren de la estructura en el control del acceso a los miembros de
// la clase.
// Cualquier cosa que se puede hacer con una estructura, se puede hacer con una
// clase. Y mucho más.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
class CRegistro
{
public:
char m_palabra[81];
int m_uso{ 0 };
int m_defini{ 0 };
};
/* La palabra clave "class" (clase) nos indica que lo que sigue es un tipo de dato
definido por el usuario. La clase tiene una estructura, tal como el tipo de dato
"struct".
La palabra clave "public" señala que las variables que siguen son de acceso sin
ninguna restricción. Por defecto, todas las variables o datos de la clase son de
163

acceso privado.
Los miembros de una estructura son de acceso público por defecto. Y no se pueden
convertir a acceso privado (private).
La privación del acceso a los miembros de un objeto instanciado de una clase es
una propiedad de las clases que se denomina "encapsulamiento". Los datos están
como en una cápsula.
MFC adopta el criterio de preceder todo nombre de una clase con la letra C.
Además, tiene el criterio de preceder los nombres de los miembros de la clase
con la letra m_.
Para instanciar una clase, se escribe como tipo fundamental el nombre de la
clase, y a continuación el nombre con el que se reconocerá el objeto instanciado
de la clase.
Ejemplo:
CRegistro miRegistro;
*/
CRegistro miRegistro;
cout << endl << endl << endl;
}

// IH132.cpp: Operations on classes. Operaciones con las clases. Understanding classes.


// Entender las clases.
//
// Una clase es un tipo de dato específico que es definido por el usuario.
// La clase contiene elementos de datos del tipo de datos fundamentales o datos
// de un tipo definido por el usuario.
// Los elementos de datos pueden ser palabras aisladas, números aislados, varia-
// bles, matrices, punteros, matrices de punteros de cualquier tipo, objetos de
// otros tipos de clases, etc.
// Además, pueden contener funciones que operan sobre los demás elementos de
// la clase.
// Los datos y funciones de una clase se denominan miembros de la clase. A los
// datos se les llama datos miembros, y a las funciones se les llama funciones
164

// miembros. A las funciones también se les llama métodos y procedimientos.


// La definición de una clase actúa como bosquejo de los objetos que se pueden
// derivar de ella. Se pueden derivar también otras clases, llamadas clases hijas.
// La instrucción que define a una clase empieza con la palabra clave "class",
// seguida del nombre que se le está asignando a la clase; luego, entre corchetes
// o llaves curvas, se definen los datos miembros y las funciones miembros; y
// finaliza con un punto y coma.
// Todos los nombres que se usen son locales en la clase.
// Hay tres atributos de acceso a los datos de una clase: public, private y
// protected.
// Los datos y funciones que son públicos pueden tener acceso desde afuera de
// la clase.
// La clase debe servir únicamente como un tipo de dato creado por el usuario.
// Para hacer operaciones reales, es necesario usar el tipo de la clase para
// crear un objeto derivado de la clase. Luego, se tiene acceso a los miembros
// del objeto.
//
#include <iostream>
using namespace std;
int main()
{
system("color FD");
cout << endl << endl << endl;
class CCaja
{
public:
double m_Longitud; // Longitud de la caja en cm.
double m_Ancho; // Ancho en cm.
double m_Alto; // Alto en cm.
};
CCaja cajaA;
CCaja cajaB;
CCaja cajaC;
165

/* Hemos creado tres objetos derivados de la clase CCaja.


Aún no hemos definido los atributos específicos de cada objeto.
*/
cajaA.m_Alto = 30.0;
cajaA.m_Ancho = 45.6;
cajaA.m_Longitud = 60.4;
cout << "El volumen de la caja A es "
<< cajaA.m_Alto * cajaA.m_Ancho * cajaA.m_Longitud
<< " cm cúbicos.";
// Los tres elementos de cajaB se pueden asignar al mismo tiempo.
cajaB.m_Alto = cajaB.m_Ancho = cajaB.m_Longitud = 38.6;
// Una caja entera se puede copiar en otra caja.
cajaC = cajaA;
cajaA.m_Alto += 15.0; // El alto de cajaA se incrementa en 15.0 cm.
cajaA.m_Ancho -= 12.5; // Se reduce el ancho de cajaA en 12.5 cm.
cout << "\n\nEl volumen de la caja A es "
<< cajaA.m_Alto * cajaA.m_Ancho * cajaA.m_Longitud << " cm cúbicos.";
cout << endl << endl << endl
<< "El objeto cajaC ocupa " << sizeof cajaC << " bytes en la memoria.";
cout << endl << endl << endl
<< "El objeto cajaB ocupa " << sizeof(cajaB) << " bytes en la memoria.";
cout << endl << endl << endl;
}

// IH133.cpp: Memberwise initialization of an object. La inicialización de un objeto miembro


// a miembro.
//
// Cuando los datos miembros de un objeto creado a partir de una clase, son
// públicos, se puede especificar sus valores, en el orden correspondiente,
// al momento de crear el objeto, usando una lista de inicialización.
//
// MMM: No se puede usar una lista de inicialización cuando los datos
166

// miembros son de carácter privado o private.


// Tampoco se puede usar si la clase incluye la definición de un
// constructor de la clase.
//
#include <iostream>
using namespace std;
class CCaja
{
public:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja cajaA{ 34.5, 26.8, 72.3 }; // Crear objeto con lista de inicialización.
CCaja cajaB{ 26.8, 90.0 }; // El alto es cero.
CCaja cajaC{}; // Todas las dimensiones son cero.
cout << "Las dimensiones de la caja A son:\nLongitud " << cajaA.m_longitud
<< "\nAncho " << cajaA.m_ancho << "\nAlto " << cajaA.m_alto;
cout << endl << endl << endl;
}

// IH134.cpp: Initializing class members. La inicialización de los miembros de la clase.


//
// En la definición del tipo de la clase, es decir, cuando se listan los miembros
// de la clase, es posible inicializar cada uno de los miembros de la clase.
// No es necesario inicializar cada dato miembro. Los datos miembros que no se
// inicialicen contendrán valores basura digital (junk values).
// Si se especifican valores iniciales en la definición de la clase, ya no es
167

// posible darle una lista de inicialización cuando se crean los objetos de la


// clase. Es decir, todos los objetos creados a partir de la clase tendrán estos
// valores iniciales.
// Si se desea tener disponible la opción de crear objetos de la clase y al mismo
// tiempo darle un valor inicial distinto a los datos miembro de la clase, es
// necesario utilizar un constructor de la clase.
//
// Funciones miembro de la clase.
// Una función cuyo prototipo o su definición están contenidos dentro del código
// de definición de la clase, se considera como función miembro de la clase.
// La función opera sobre cualquier objeto de la clase. De ese objeto, puede tener
// acceso a todos los miembros del objeto, independientemente de su declaración
// de acceso.
// Los miembros de los objetos de la clase que se usan en el cuerpo de una función
// automáticamente se refieren a los miembros del objeto en donde se llamó a la
// función.
// La función sólo puede llamarse para un objeto particular creado con esa misma
// clase.
// La función es válida sólo para el objeto en donde ha sido creada. Sin la
// mención del objeto, el compilador no reconocerá el nombre de la función.
// La función será llamada así:
// [objeto creado a partir de la clase].funcion(parámetroA, parámetroB, etc.);
// La clase CCaja, y todos sus objetos creados, tienen 24 bytes de extensión en
// la memoria.
// Para encontrar este valor, se usa sizeof().
// La memoria usada para la función miembro está siempre aparte de la clase.
// La función tiene una sola copia en la memoria. sizeof() no considera la
// memoria de las funciones miembros. Excepto cuando la función miembro es del
// tipo virtual.
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, con ámbito global.
168

class CCaja
{
public:
double m_longitud{ 95.0 }; // Inicialización del miembro de la clase m_longitud.
double m_ancho{ 66.8 };
double m_alto{ 45.4 };
// Definición de la función volumen(), función miembro de la clase CCaja.
double volumen()
{
return m_longitud * m_ancho * m_alto;
// No es necesario calificar la procedencia del miembro
// de la clase m_alto, etc., cuando la función miembro
// está definida dentro del cuerpo de código de la clase.
}
};
int main()
{
system("color FD");
cout << endl << endl << endl;
// Crear varios objetos de la clase CCaja.
CCaja cajaA; // Las dimensiones de cajaA ya están en la clase.
CCaja cajaB;
CCaja cajaC;
double volCajas{ cajaA.volumen() };
cout << "El volumen inicial de las cajas es " << volCajas << " cm cúbicos.";
cajaA.m_longitud = 85.0;
cajaA.m_ancho = 60.0;
cajaA.m_alto = 40.0;
volCajas = cajaA.volumen();
cout << "\n\n\nEl volumen actual de la caja A es " << volCajas << " cm cúbicos.";
cout << "\n\n\nEl volumen actual de la caja B es " << cajaB.volumen() << " cm cúbicos.";
cout << "\n\n\nEl volumen actual de la caja C es " << cajaC.volumen() << " cm cúbicos.";
cout << "\n\n\nUn objeto CCaja ocupa " << sizeof(CCaja) << " bytes de memoria.";
169

cout << endl << endl << endl;


}

// IH135.cpp: Defining a member function outside a class. Definir una función miembro
// afuera de la clase.
//
// Se puede definir una función miembro por afuera de la definición de una clase.
// Para hacerlo, escriba el prototipo de la función dentro de la definición de la
// clase.
// En el ejemplo que se escribe más abajo, la función miembro volumen(), de la
// clase CCaja, se define dentro del cuerpo de la función mediante su prototipo.
// Luego, se procederá por afuera de la clase a definir en detalle el cuerpo del
// código de la función.
// Habrá que determinar una manera de indicarle al compilador que la función
// pertenece a la clase CCaja. El modo de hacerlo es usar un prefijo con el nombre
// de la clase, seguido inmediatamente por dos puntos dobles (::), los cuales se
// conocen como el "operador de resolución del ámbito"; también le podemos
// llamar "operador de resolución del alcance".
//
#include <iostream>
using namespace std;
class CCaja
{
public:
double m_longitud{ 90.8 };
double m_ancho{ 60.6 };
double m_alto{ 40.4 };
double volumen(); // Prototipo de la función miembro volumen().
};
int main()
{
system("color FD");
170

cout << endl << endl << endl;


CCaja miCaja{};
double miVolumen = miCaja.volumen();
cout << "El volumen de mi caja es " << miVolumen << " cm cúbicos.";
miCaja.m_longitud -= 10.0;
miCaja.m_ancho -= 10.0;
miCaja.m_alto -= 10.0;
cout << "\n\n\nEl volumen de mi caja ahora es "
<< miCaja.volumen() << " cm cúbicos.";
cout << endl << endl << endl;
}
// Definición de la función miembro volumen(), de la clase CCaja
double CCaja::volumen()
{
return m_longitud * m_ancho * m_alto;
}

// IH136.cpp: Inline functions. Las funciones miembros en línea.


//
// Cuando una función miembro de una clase se define completamente dentro del
// cuerpo de código de la clase, se considera como una función en línea (inline
// function). Este es un tratamiento implícito que el compilador hace del código de
// la función; no es que la función ha sido declarada explícitamente como una inline
// function. Cuando a una función se le declara explícitamente como una función
// inline, el compilador trata de expandir el código del cuerpo de la función en el
// lugar en el que se llama a la función. La expansión se hace con todos los ajustes
// por los demás miembros de la clase, y los alcances de los mismos, de modo que
// se eviten los conflictos en el código. El resultado es que el código se ejecuta
// mucho más rápido.
// Al especificar a una función como de tipo inline, sin embargo, no siempre está
// garantizado que la función estará en línea. A veces, el compilador no tiene la
// capacidad de insertar el código en línea; por ejemplo, con funciones recursivas
171

// o con funciones para las que se ha obtenido mediante una dirección de memoria.
// Es mejor utilizar esta técnica para funciones simples y cortas.
// El compilador inserta el código real de la función en cada una de las llamadas
// dentro del programa, así que el tamaño del código ejecutable se incrementa,
// aunque el código sea más veloz.
// Cuando el compilador no logra convertir la función a código en línea, siempre
// logra compilarla y ejecutarla.
// Si una función se define afuera del cuerpo de código de la clase, aún es
// factible declararla como función en línea, mediante la palabra clave inline.
//
#include <iostream>
using namespace std;
class CCaja
{
public:
double m_longitud{ 90.8 };
double m_ancho{ 60.6 };
double m_alto{ 40.4 };
double volumen(); // Prototipo de la función miembro volumen().
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja miCaja{};
double miVolumen = miCaja.volumen();
cout << "El volumen de mi caja es " << miVolumen << " cm cúbicos.";
miCaja.m_longitud -= 10.0;
miCaja.m_ancho -= 10.0;
miCaja.m_alto -= 10.0;
cout << "\n\n\nEl volumen de mi caja ahora es " << miCaja.volumen()
<< " cm cúbicos.";
cout << endl << endl << endl;
172

}
// Definición de la función miembro volumen(), de la clase CCaja, con carácter
// en línea.
inline double CCaja::volumen()
{
return m_longitud * m_ancho * m_alto;
}

// IH137.cpp: Class constructors. Los constructores de las clases.


//
// Las clases se definen con la posibilidad de inicializar los valores de los
// datos miembros con lo que todos los objetos creados a partir de ellas quedan
// con esos valores desde el inicio de su creación. Luego, dentro del programa,
// se pueden modificar esos valores.
// Una dificultad en cuanto a ese procedimiento consiste en que si los atributos
// de acceso de los datos miembros son de tipo private o protected, no hay acceso
// a los datos desde afuera de la clase y ese procedimiento no funciona.
// Un constructor es una función especial en la clase que actúa sobre la creación
// de nuevos objetos a partir de la clase.
// El constructor permite al usuario declarar los objetos que está creando de una
// manera que los objetos tengan los datos que el usuario desee.
// El constructor es una función miembro de la clase. Por ello, tiene el atributo
// de acceso libre a todos los datos miembros de la clase, sin importar el tipo
// de acceso que se les haya definido.
// MMM: Una clase puede tener varios constructores, permitiendo así la creación
// de objetos en varias maneras.
// En el constructor se puede agregar código que valide los argumentos
// pasados a los objetos, asegurando que llenan los criterios de aceptación
// del usuario.
//
// Cuando se llama al constructor, no se admite que existan menos valores que la
// cantidad de datos miembros del objeto que se está creando. Si la definición
173

// de la clase indica que son 3 datos miembros, el constructor debe tener 3


// parámetros, en el mismo orden.
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, que usa una función miembro constructor.
class CCaja
{
public:
double m_longitud{ 1.0 };
double m_ancho{ 1.0 };
double m_alto{ 1.0 };
// Definición de un constructor de la clase CCaja.
CCaja(double lng, double anc, double alt)
{
cout << "\nSe ha llamado al constructor de la clase.\n";
m_longitud = lng;
m_ancho = anc;
m_alto = alt;
}
// Definición de la función volumen().
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
};
int main()
{
system("color FD");
cout << endl << endl << endl;
// Creación de los objetos de la clase CCaja, usando notación de lista.
CCaja cajaA{ 96.6, 60.8, 44.2 };
CCaja cajaB{ 78.4, 36.8, 22.9 };
174

CCaja cajaC{ 49.1, 28.3, 12.7 };


/* Creación de los objetos de la clase CCaja, usando notación de funciones,
notación antigua.
CCaja cajaA( 96.6, 60.8, 44.2 );
CCaja cajaB( 78.4, 36.8, 22.9 );
CCaja cajaC( 49.1, 28.3, 12.7 );
*/
cout << "\n\n\nEl volumen de la caja A es " << cajaA.volumen();
cout << "\n\nEl volumen de la caja B es " << cajaB.volumen();
cout << "\n\nEl volumen de la caja C es " << cajaC.volumen();
cout << endl << endl << endl;
}

// IH138.cpp: The default constructor. El constructor por defecto.


//
// Un constructor por defecto es un constructor que el compilador usa cuando al
// crear un objeto no se proporciona una lista de argumentos.
// La sintaxis más sencilla para crear un constructor por defecto es:
// CCaja()
// {}
// Para que esta sintaxis funciones sin problemas, se debe escribir dentro de
// la definición de la clase. Si hay otros constructores definidos, el
// constructor por defecto se escribe al final de los demás.
// Un constructor por defecto puede tener una lista vacía, como en el ejemplo
// anterior, o puede contener la lista de los valores que el usuario desea que
// aparezcan inicializando los datos miembros del objeto creado en base a la
// clase.
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, que usa una función miembro constructor.
class CCaja
175

{
public:
double m_longitud{ 1.0 };
double m_ancho{ 1.0 };
double m_alto{ 1.0 };
// Definición de un constructor de la clase CCaja.
CCaja(double lng, double anc, double alt)
{
cout << "\nSe ha llamado al constructor de la clase.\n";
m_longitud = lng;
m_ancho = anc;
m_alto = alt;
}
// Definición del constructor por defecto (default constructor) de la clase CCaja.
CCaja() = default;
// Definición de la función volumen().
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
};
int main()
{
system("color FD");
cout << endl << endl << endl;
// Creación de los objetos de la clase CCaja, usando notación de lista.
CCaja cajaA{ 96.6, 60.8, 44.2 };
CCaja cajaB{ 78.4, 36.8, 22.9 };
CCaja cajaC{ 49.1, 28.3, 12.7 };
/* Creación de los objetos de la clase CCaja, usando notación de funciones,
notación antigua.
CCaja cajaA( 96.6, 60.8, 44.2 );
CCaja cajaB( 78.4, 36.8, 22.9 );
176

CCaja cajaC( 49.1, 28.3, 12.7 );


*/
/* En la siguiente instrucción, el compilador busca los datos miembros del objeto
que se está creando, de acuerdo con la definición de la clase. Al no encontrar-
los, busca un constructor por defecto, y no lo encuentra, señalando que existe
un error. Por tanto, hay que crear un constructor por defecto.
CCaja cajaVacia;
*/
CCaja cajaVacia;
cout << "\n\n\nEl volumen de la caja A es " << cajaA.volumen();
cout << "\n\nEl volumen de la caja B es " << cajaB.volumen();
cout << "\n\nEl volumen de la caja C es " << cajaC.volumen();
cout << "\n\nEl volumen de la caja Vacía es " << cajaVacia.volumen();
cout << endl << endl << endl;
}

// IH139.cpp: Supplying a default constructor. Proporcionar un constructor por defecto.


//
// Un constructor por defecto es un constructor que el compilador usa cuando
// al crear un objeto no se proporciona una lista de argumentos.
// La sintaxis más sencilla para crear un constructor por defecto es:
// CCaja()
// {}
// Para que esta sintaxis funciones sin problemas, se debe escribir dentro de
// la definición de la clase. Si hay otros constructores definidos, el
// constructor por defecto se escribe al final de los demás. A esto le
// llamaríamos "sobrecarga de constructores".
// Un constructor por defecto puede tener una lista vacía, como en el ejemplo
// anterior, o puede contener la lista de los valores que el usuario desea
// que aparezcan inicializando los datos miembros del objeto creado en base a
// la clase.
//
177

#include <iostream>
using namespace std;
// Definición de la clase CCaja, que usa una función miembro constructor.
class CCaja
{
public:
double m_longitud{ 1.0 };
double m_ancho{ 1.0 };
double m_alto{ 1.0 };
// Definición de un constructor de la clase CCaja.
CCaja(double lng, double anc, double alt)
{
cout << "\nSe ha llamado al constructor de la clase CCaja.\n";
m_longitud = lng;
m_ancho = anc;
m_alto = alt;
}
// Definición del constructor por defecto (default constructor) de la clase CCaja.
CCaja()
{
cout << "\n\nEl constructor por defecto de la clase CCaja ha sido llamado.\n\n";
}
// Definición de la función volumen().
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
};
int main()
{
system("color FD");
cout << endl << endl << endl;
// Creación de los objetos de la clase CCaja, usando notación de lista.
178

CCaja cajaA{ 96.6, 60.8, 44.2 };


CCaja cajaB{ 78.4, 36.8, 22.9 };
CCaja cajaC{ 49.1, 28.3, 12.7 };
/* Creación de los objetos de la clase CCaja, usando notación de funciones,
notación antigua.
CCaja cajaA( 96.6, 60.8, 44.2 );
CCaja cajaB( 78.4, 36.8, 22.9 );
CCaja cajaC( 49.1, 28.3, 12.7 );
*/
/* En la siguiente instrucción, el compilador busca los datos miembros del objeto
que se está creando, de acuerdo con la definición de la clase. Al no encontrarlos,
busca un constructor por defecto, y no lo encuentra, señalando que existe un
error. Por tanto, hay que crear un constructor por defecto.
CCaja cajaVacia;
*/
CCaja cajaVacia;
CCaja cajaVacia2;
cajaVacia2.m_longitud += 20.0; // Incrementar 20.0 cm a la longitud.
cajaVacia2.m_ancho += 15.0;
cajaVacia2.m_alto += 8.0;
cout << "\n\n\nEl volumen de la caja A es " << cajaA.volumen();
cout << "\n\nEl volumen de la caja B es " << cajaB.volumen();
cout << "\n\nEl volumen de la caja C es " << cajaC.volumen();
cout << "\n\nEl volumen de la caja Vacía es " << cajaVacia.volumen();
cout << "\n\nEl volumen de la caja Vacía 2 es " << cajaVacia2.volumen();
cout << endl << endl << endl;
}
179

// IH140.cpp: Default parameter values in constructors. Valores por defecto en los parámetros
// de los constructores. Supplying default values for constructor arguments.
// Proporcionar valores por defecto a los argumentos del constructor.
//
// Hemos visto cómo podemos especificar valores por defecto para los parámetros
// de una función en el prototipo de la función. Se puede usar la misma sintaxis para
// las funciones miembros de las clases, incluyendo los constructores.
// Si se pone la definición de la función miembro dentro de la clase, se puede
// además poner los valores por defecto de los parámetros en el encabezado de la
// función.
// Si se incluye sólo el prototipo de una función en la definición de la clase,
// los valores por defecto de los parámetros deberán ir en el prototipo, no en la
// definición de la función.
// Se puede usar esta técnica como una alternativa para especificar valores
// iniciales de los datos miembros de una clase.
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, que usa una función miembro constructor.
class CCaja
{
public:
double m_longitud;
double m_ancho;
double m_alto;
// Definición de un constructor de la clase; ahora este es el constructor
// por defecto.
CCaja(double lng = 1.0, double anc = 1.0, double alt = 1.0)
{
cout << "\nSe ha llamado al constructor por defecto de la clase.\n";
m_longitud = lng;
180

m_ancho = anc;
m_alto = alt;
}
/* Definición del constructor por defecto (default constructor).
CCaja()
{
cout << "\n\nEl constructor por defecto ha sido llamado.\n\n";
}
*/
// Definición de la función volumen().
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
};
int main()
{
system("color FD");
cout << endl << endl << endl;
// Creación de los objetos de la clase CCaja, usando notación de lista.
CCaja cajaA{ 96.6, 60.8, 44.2 };
CCaja cajaB{ 78.4, 36.8, 22.9 };
CCaja cajaC{ 49.1, 28.3, 12.7 };
/* Creación de los objetos de la clase CCaja, usando notación de funciones,
notación antigua.
CCaja cajaA( 96.6, 60.8, 44.2 );
CCaja cajaB( 78.4, 36.8, 22.9 );
CCaja cajaC( 49.1, 28.3, 12.7 );
*/
/* En la siguiente instrucción, el compilador busca los datos miembros del
objeto que se está creando, de acuerdo con la definición de la clase.
Al no encontrarlos, busca un constructor por defecto, y no lo encuentra,
señalando que existe un error. Por tanto, hay que crear un constructor
181

por defecto.
CCaja cajaVacia;
*/
CCaja cajaVacia;
CCaja cajaVacia2;
cajaVacia2.m_longitud += 20.0; // Incrementar 20.0 cm a la longitud.
cajaVacia2.m_ancho += 15.0;
cajaVacia2.m_alto += 8.0;
cout << "\n\n\nEl volumen de la caja A es " << cajaA.volumen();
cout << "\n\nEl volumen de la caja B es " << cajaB.volumen();
cout << "\n\nEl volumen de la caja C es " << cajaC.volumen();
cout << "\n\nEl volumen de la caja Vacía es " << cajaVacia.volumen();
cout << "\n\nEl volumen de la caja Vacía 2 es " << cajaVacia2.volumen();
cout << endl << endl << endl;
}

// IH141.cpp: Using a constructor initialization list. Usar una lista de inicialización del
// constructor.
//
// Es factible inicializar los datos miembros de una clase, usando una lista de
// inicialización en el encabezado de la definición de un constructor.
// Esto es distinto de usar una lista de inicialización cuando se llama al
// constructor.
// En el encabezado, la lista de inicialización se pasa directamente al
// constructor.
// La definición del constructor tiene que estar dentro de la definición de la
// clase.
// La lista de inicialización del constructor se escribe después de la lista de
// parámetros, separada de ella por dos puntos (:), y los inicializadores de los
// datos miembros de la clase se separan con comas. Se puede usar dos formas de
// sintaxis: la inicialización moderna con {}, o la funcional con (). Es mejor
// usar la moderna.
182

// Es preferible usar la inicialización de datos miembros en la lista después de


// la lista de parámetros. Es más eficiente. El constructor procede a inicializar
// desde el encabezado, usando el orden en que los datos miembros están escritos
// en la definición de la clase.
// Como ya tienen valores, el usuario puede usar los datos miembros dentro del
// cuerpo de código del constructor.
// MMM: Los miembros de la clase que son constantes (const), o las referencias,
// no tienen otra salida que ser inicializados en la lista del encabezado
// del constructor.
// No es posible inicializarlos mediante asignaciones dentro del cuerpo de
// código del constructor. Esto se explica porque son constantes.
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, que usa una función miembro constructor.
class CCaja
{
public:
double m_longitud;
double m_ancho;
double m_alto;

// Definición de un constructor de la clase; ahora este es el constructor por defecto.


CCaja(double lng = 1.0, double anc = 1.0, double alt = 1.0) : m_longitud{lng},
m_ancho{anc}, m_alto{alt}
{
cout << "\nSe ha llamado al constructor por defecto de la clase.\n";
// m_longitud = lng;
// m_ancho = anc;
// m_alto = alt;
}
/* Definición del constructor por defecto (default constructor).
CCaja()
183

{
cout << "\n\nEl constructor por defecto ha sido llamado.\n\n";
}
*/
// Definición de la función volumen().
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
};
int main()
{
system("color FD");
cout << endl << endl << endl;
// Creación de los objetos de la clase CCaja, usando notación de lista.
CCaja cajaA{ 96.6, 60.8, 44.2 };
CCaja cajaB{ 78.4, 36.8, 22.9 };
CCaja cajaC{ 49.1, 28.3, 12.7 };
/* Creación de los objetos de la clase CCaja, usando notación de funciones,
notación antigua.
CCaja cajaA( 96.6, 60.8, 44.2 );
CCaja cajaB( 78.4, 36.8, 22.9 );
CCaja cajaC( 49.1, 28.3, 12.7 );
*/
/* En la siguiente instrucción, el compilador busca los datos miembros del objeto
que se está creando, de acuerdo con la definición de la clase. Al no encontrar-
los, busca un constructor por defecto, y no lo encuentra, señalando que existe
un error. Por tanto, hay que crear un constructor por defecto.
CCaja cajaVacia;
*/
CCaja cajaVacia;
CCaja cajaVacia2;
cajaVacia2.m_longitud += 20.0; // Incrementar 20.0 cm a la longitud.
184

cajaVacia2.m_ancho += 15.0;
cajaVacia2.m_alto += 8.0;
cout << "\n\n\nEl volumen de la caja A es " << cajaA.volumen();
cout << "\n\nEl volumen de la caja B es " << cajaB.volumen();
cout << "\n\nEl volumen de la caja C es " << cajaC.volumen();
cout << "\n\nEl volumen de la caja Vacía es " << cajaVacia.volumen();
cout << "\n\nEl volumen de la caja Vacía 2 es " << cajaVacia2.volumen();
cout << endl << endl << endl;
}

// IH142.cpp: Making a constructor explicit; implicit conversion. Hacer explícito a un


// constructor; conversión implícita.
//
// Supongamos que existe un constructor CCubo, el cual podría estar construido
// con 3 parámetros.
// Este constructor no tiene valores por defecto especificados. Este constructor
// podría funcionar como un constructor con un único argumento, y como un
// constructor con 2 argumentos; los parámetros no especificados serían tomados
// por el compilador de acuerdo a sus valores por defecto.
// Si el usuario define un constructor con un único argumento, el compilador
// usará este constructor para conversiones implícitas.
//
#include <iostream>
using namespace std;
class CCubo
{
public:
/* CCubo(double lng{ 1.0 }, double anc{ 1.0 }, double alt{ 1.0 })// No funciona.
CCubo(double lng = 1.0, double anc = 1.0, double alt = 1.0 ) // Sí funciona.
{
cout << "\n\nSe ha llamado al constructor.\n\n";
m_longitud = lng;
185

m_ancho = anc;
m_alto = alt;
}
*/
// Definición de un constructor con un solo argumento, para conversión implícita.
// MMM: Note el cuerpo de código vacío del constructor, al final de la instrucción.
CCubo(double lado) : m_longitud{ lado }, m_ancho{ lado }, m_alto{ lado } {}
// Definición del constructor por defecto.
CCubo()
{
cout << "\n\nEl constructor por defecto ha sido llamado.\n\n";
}
// Definición de la función volumen()
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCubo cubo1; // Crear el objeto cubo1, llamando al constructor por defecto.
/* La siguiente instrucción supone una conversión implícita de parte del compilador.
El objeto cubo1 debería ser convertido al tipo de dato CCubo(lado), implícitamen-
te por el compilador. El compilador rechaza esto. No acepta convertir del tipo de
dato double al tipo de dato CCubo, (class).
Había error en la definición de la clase. Yo había definido un constructor
inicializando los datos miembros. Y en realidad no tenía constructor por defecto.
186

Cuando sí hice la creación del constructor por defecto, el compilador aceptó la


instrucción. Ver más adelante.
cubo1 = 15.0; */
cout << "El volumen del cubo 1 es " << cubo1.volumen() << " cm cúbicos.";
cubo1 = 15.0;
cout << "\n\nEl volumen del cubo 1 es " << cubo1.volumen() << " cm cúbicos.";
//-------------------------------------------------------------------------------
CCubo cubo2( 25.0 );
cout << "\n\nEl volumen del cubo 2 es " << cubo2.volumen() << " cm cúbicos.";
cout << endl << endl << endl;
}

// IH143.cpp: Making a constructor explicit; implicit conversion. Hacer explícito a un


// constructor.
//
// Supongamos que existe un constructor CCubo, el cual podría estar construido
// con 3 parámetros.
// Este constructor no tiene valores por defecto especificados. Este constructor
// podría funcionar como un constructor con un único argumento, y como un
// constructor con 2 argumentos; los parámetros no especificados serían tomados
// por el compilador de acuerdo a sus valores por defecto.
// Si el usuario define un constructor con un único argumento, el compilador
// usará este constructor para conversiones implícitas.
// Para prevenir accidentales conversiones implícitas, no deseadas, se usa la
// palabra clave explicit aplicada a la declaración del constructor.
//
#include <iostream>
using namespace std;
class CCubo
{
public:
/* CCubo(double lng{ 1.0 }, double anc{ 1.0 }, double alt{ 1.0 }) // No funciona.
187

CCubo(double lng = 1.0, double anc = 1.0, double alt = 1.0 ) // Sí funciona.
{
cout << "\n\nSe ha llamado al constructor.\n\n";
m_longitud = lng;
m_ancho = anc;
m_alto = alt;
}
*/
// Definición de un constructor con un solo argumento.
// MMM: Note el cuerpo de código vacío del constructor, al final de la instrucción.
explicit CCubo(double lado) : m_longitud{ lado }, m_ancho{ lado }, m_alto{ lado } {}
// Definición del constructor por defecto.
CCubo()
{
cout << "\n\nEl constructor por defecto ha sido llamado.\n\n";
}
// Definición de la función volumen()
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCubo cubo1; // Crear el objeto cubo1, llamando al constructor por defecto.
/* La siguiente instrucción supone una conversión implícita de parte del compilador.
El objeto cubo1 debería ser convertido al tipo de dato CCubo(lado), implícitamen-
188

te por el compilador. El compilador rechaza esto. No acepta convertir del tipo de


dato double al tipo de dato CCubo, (class).
Había error en la definición de la clase. Yo había definido un constructor inicia-
lizando los datos miembros. Y en realidad no tenía constructor por defecto. Cuando
si hice la creación del constructor por defecto, el compilador aceptó la instrucción.
Ver más adelante.
Al declarar explicit la creación del constructor, la instrucción es rechazada por
el compilador.
cubo1 = 15.0;
*/
cout << "El volumen del cubo 1 es " << cubo1.volumen() << " cm cúbicos.";
/* cubo1 = 15.0; // Conversión implícita rechazada por el compilador. Error.
cout << "\n\nEl volumen del cubo 1 es " << cubo1.volumen() << " cm cúbicos.";
*/
//---------------------------------------------------------------------------------------
CCubo cubo2(25.0); // Llamada explícita del constructor. Notación antigua de función.
cout << "\n\nEl volumen del cubo 2 es " << cubo2.volumen() << " cm cúbicos.";
//---------------------------------------------------------------------------------------
CCubo cubo3{ 25.0 }; // Llamada explícita del constructor. Notación moderna.
cout << "\n\nEl volumen del cubo 3 es " << cubo3.volumen() << " cm cúbicos.";
cout << endl << endl << endl;
}

// IH144.cpp: Delegating constructors. Delegar constructores.


//
// Si una clase tiene varios constructores definidos, uno de ellos puede llamar
// a otro para ayudar en la creación de un objeto. Ello se hace únicamente en la
// lista de inicialización del encabezado del constructor, en cuyo caso no debe
// existir ninguna otra inicialización dentro de la lista.
// El segundo constructor del ejemplo de la definición de la clase, llama al
// primer constructor. Lo hace directamente en el encabezado del constructor.
// Usa un solo argumento. Este argumento es el único parámetro posible en la
189

// lista de inicialización que continúa.


// Si se llama a otro constructor dentro del cuerpo de código del constructor,
// ya no se aplica la delegación de constructores. En cuyo caso, lo que se
// aplica es que se está creando un objeto distinto.
#include <iostream>
using namespace std;
class CCubo
{
public:
// Constructor
explicit CCubo(double lng, double anc, double alt) : m_longitud{lng}, m_ancho{anc}, m_alto{alt} {}
// Otro constructor
explicit CCubo(double lado) : CCubo{ lado, lado, lado } {}
// Definición de la función volumen()
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCubo miCubo{ 98.0, 86.3, 24.7 };
CCubo tuCubo{ 77.0 };
cout << "\n\nEl volumen de miCubo es " << miCubo.volumen() << " cm cúbicos.\n\n";
cout << "\n\nEl volumen de tuCubo es " << tuCubo.volumen() << " cm cúbicos.\n\n";
cout << endl << endl << endl;
}
190

// IH145.cpp: Private members of a class. Miembros privados de una clase.


//
// Un constructor declara los datos miembros de una clase y permite el acceso
// a esos datos. Lo anterior trae a la mesa de debate el tema de la protección
// de los datos que están internamente en una clase.
// La seguridad de los datos miembros de una clase se obtiene mediante la
// palabra clave "private:".
// Los datos miembros y las funciones miembros de la clase que se han declara-
// do como private sólo pueden ser utilizados por funciones de la clase. Existe
// una excepción con respecto a esto.
// Una función ordinaria del programa, que no pertenece a la clase, no tiene
// medios de poder tener acceso a los datos y funciones privados de la clase.
//
// MMM: Esta posibilidad de separar la interfaz de la clase de su implementa-
// ción interna se basa en esa declaración de miembros como privados.
//
// La interfaz con la que se tiene acceso a una clase está compuesta de sus
// datos y funciones públicos.
// Las funciones públicas de la clase proveen un método indirecto de interve-
// nir en los datos privados internos de la clase.
// No todos los datos y funciones requieren ser privados. Cuando sea posible,
// deje datos y funciones de carácter público.
//
// MMM: Todas las funciones miembros de la clase (públicas y privadas) tienen
// acceso a los datos miembros de la clase, sean también públicos o pri-
// vados.
// Las funciones privadas de la clase sólo pueden ser llamadas desde
// las funciones miembros de las clases.
// Las funciones públicas pueden ser usadas desde cualquier parte,
// desde dentro de la clase y desde fuera de la clase.
// El usuario debe asegurarse de poner el código en alguna función que
// le permita realizar todas las operaciones que desea con los datos
// privados.
191

//
#include <iostream>
using namespace std;
class CCubo
{
public:
// Definición del constructor de la clase CCubo.
explicit CCubo(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{lng}, m_ancho{anc}, m_alto{alt}
{
cout << "\n\nSe ha llamado al constructor.\n\n";
}
// Función para calcular el volumen()
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCubo miCubo{ 88.4, 66.5, 16.9 }; // Objeto creado con lista de valores iniciales.
CCubo suCubo; // Objeto creado sin valores iniciales.
cout << "El volumen de miCubo es " << miCubo.volumen() << " cm cúbicos.\n\n";
cout << "El volumen de suCubo es " << suCubo.volumen() << " cm cúbicos.\n\n";
/* Estas instrucciones tratan inútilmente de tener acceso directo a los datos
privados. Cópielas más adelante y el compilador se encargará de señalarlas
como errores de acceso.
192

miCubo.m_longitud += 5.0;
suCubo.m_alto += 7.6;
*/
// miCubo.m_longitud += 5.0;
// suCubo.m_alto += 7.6;
cout << endl << endl << endl;
}

// IH146.cpp: Accessing private class members. Tener acceso a los miembros privados
// de la clase.
//
// Es algo extremo el declarar los datos miembros y funciones miembros de la
// clase como privados.
// Los datos miembros privados no necesariamente tienen que permanecer 100
// por ciento ocultos.
// El acceso se puede programar explícitamente en funciones públicas, cuyo
// ámbito de acción es estrictamente limitado.
//
#include <iostream>
using namespace std;
class CCubo
{
public:
// Constructor de la clase CCubo.
explicit CCubo(double lng = 3.0, double anc = 2.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "\n\nSe ha llamado al constructor.\n\n";
}
// Función para calcular el volumen()
double volumen()
{
193

return m_longitud * m_ancho * m_alto;


}
// Función pública para tener acceso a la variable miembro longitud. Estas
// funciones, por estar definidas dentro del código de la clase, son inline
// (en línea) por defecto. En el programa IH147 se declaran inline
// explícitamente.
double accLongitud()
{
return m_longitud; // Da acceso de lectura al valor de m_longitud.
}
double accAncho()
{
return m_ancho; // Da acceso de lectura al valor de m_ancho.
}
double accAlto()
{
return m_alto; // Da acceso de lectura al valor de m_alto.
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCubo miCubo; // Crear el objeto miCubo, con los valores por defecto.
cout << "Los valores por defecto que pone el constructor de la clase"
<< " al objeto miCubo son:\n\n";
cout << "La longitud = " << miCubo.accLongitud() << " cm.\n\n";
cout << "El ancho = " << miCubo.accAncho() << " cm.\n\n";
cout << "El alto = " << miCubo.accAlto() << " cm.\n\n";
194

cout << "El volumen por defecto del objeto miCubo es "
<< miCubo.volumen() << " cm cúbicos.\n\n";
cout << endl << endl << endl;
}

// IH147.cpp: Accessing private class members. Tener acceso a los miembros privados
// de la clase. (2) Inline functions. Funciones en línea.
//
// Es algo extremo el declarar los datos miembros y funciones miembros de la
// clase como privados.
// Los datos miembros privados no necesariamente tienen que permanecer 100
// por ciento ocultos.
// El acceso se puede programar explícitamente en funciones públicas, cuyo
// ámbito de acción es estrictamente limitado.
// Las funciones de acceso pueden estar fuera de la clase, e incluso se
// pueden declarar como funciones en línea (inline functions).
// Las funciones en línea son incorporadas por el compilador al código de la
// clase, al compilar, y están listas para ejecutar de inmediato en cuanto
// sean llamadas. Si son pequeñas, no afecta mucho el tamaño del código
// ejecutable, y le da mayor velocidad al procesamiento.
//
#include <iostream>
using namespace std;
class CCubo
{
public:
// Constructor explícito de la clase CCubo
explicit CCubo(double lng = 3.0, double anc = 2.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "\n\nSe ha llamado al constructor de la clase CCubo.\n\n";
}
195

// Función para calcular el volumen()


double volumen()
{
return m_longitud * m_ancho * m_alto;
}
// Función pública para tener acceso a la variable miembro longitud. Debo
// poner la palabra clave inline antes del nombre de la función, para
// indicarle al compilador que deseo que agregue el código a la compilación,
// haciéndola inline.
inline double accLongitud()
{
return m_longitud; // Da acceso de lectura al valor de m_longitud.
}
inline double accAncho()
{
return m_ancho; // Da acceso de lectura al valor de m_ancho.
}
inline double accAlto()
{
return m_alto; // Da acceso de lectura al valor de m_alto.
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCubo miCubo; // Crear el objeto miCubo, con los valores por defecto.
cout << "Los valores por defecto que pone el constructor de la clase"
<< " al objeto miCubo son:\n\n";
196

cout << "La longitud = " << miCubo.accLongitud() << " cm.\n\n";
cout << "El ancho = " << miCubo.accAncho() << " cm.\n\n";
cout << "El alto = " << miCubo.accAlto() << " cm.\n\n";
cout << "El volumen por defecto del objeto miCubo es "
<< miCubo.volumen() << " cm cúbicos.\n\n";
// Para guardar el valor de la longitud en otra variable, ya dentro de
// main(), se usa:
double miLong{ miCubo.accLongitud() };
cout << "El quíntuple de la longitud es = " << miLong * 5 << " cm.\n\n";
cout << endl << endl << endl;
}

// IH148.cpp: The friend functions of a class. Las funciones amigas de una clase.
//
// A veces, es deseable que una función especial que no es miembro de una
// clase, tenga acceso a todos los datos miembros de la clase.
// Dichas funciones forman un grupo élite, con privilegios especiales.
// A esas funciones se les denomina "friend functions of a class"
// (funciones amigas de una clase).
// Para definirlas, se usa la palabra clave "friend".
// MMM: Es factible incluir únicamente el prototipo de la función amiga,
// o incluir la definición completa de la función. Cuando dentro del
// código de la definición de la clase se incluye la definición
// completa de la función amiga, ésta se vuelve una función inline.
// Las funciones amigas no son funciones miembros de la clase.
// Los atributos de acceso (public, private, protected) no se aplican a
// las funciones amigas.
// Las funciones amigas son funciones ordinarias globales con privilegios
// especiales con respecto a la clase en la que están definidas.
//
#include <iostream>
#include <iomanip>
197

using namespace std;


class CCaja
{
public:
// Definición del constructor de CCaja
CCaja(double lng, double anc, double alt) : m_longitud{ lng },
m_ancho{ anc }, m_alto{ alt }
{ cout << "Se ha llamado al constructor de CCaja, con tres argumentos.\n\n"; }
// Definición explícita del constructor
explicit CCaja(double lado) : CCaja(lado, lado, lado)
{ cout << "Se ha llamado al constructor de CCaja, con un argumento repetido.\n\n"; }
// Definición del constructor por defecto
CCaja() = default;
// Función para calcular el volumen de la caja
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
// Definición de la función amiga areaCaja(), que es una función global ordinaria.
// Como no es una función miembro de la clase CCaja, el atributo "private" no se
// aplica.
// El parámetro de la función areaCaja() es la clase misma CCaja, lo que permite
// el acceso a los datos miembros privados m_longitud, m_ancho y m_alto.
// Debido a que la función amiga no es una función miembro de la clase, los datos
// miembros no pueden ser designados directamente por sus nombres, y por ello no
// se utilizan como argumentos de la función.
// Al declararse una función como amiga de una clase, automáticamente se le abre
// la posibilidad del acceso a todos los miembros de la clase, sin ninguna
// restricción.
198

// MMM: Aquí se escribe primero el prototipo de la función dentro del cuerpo de


// código de la clase. Luego, se define la función con su cuerpo de código
// afuera de la clase.
// En el siguiente programa IH149, se escribirá todo dentro del cuerpo de
// código de la clase, lo cual oculta del todo el código de la clase amiga.
// Esto puede a veces no ser tan recomendable.
friend double areaCaja(const CCaja& estaCaja); // Prototipo de la función amiga.
};
// Definición de la función amiga areaCaja(), que es una función global ordinaria.
double areaCaja(const CCaja& estaCaja)
{
return 2.0 * (estaCaja.m_longitud * estaCaja.m_ancho +
estaCaja.m_longitud * estaCaja.m_alto +
estaCaja.m_ancho * estaCaja.m_alto);
}
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja miCaja{ 88.4, 66.2, 22.0 }; // Crear el objeto usando tres argumentos.
CCaja suCaja{ 77.5 }; // Crear el objeto usando un lado repetido.
CCaja laCaja; // Crear el objeto usando el constructor por defecto.
// El compilador asigna áreas de memoria para los 3
// parámetros de la clase, sin contenido digital definido.
cout << "El volumen de miCaja es " << setw(10) << miCaja.volumen()
<< " El área de miCaja es " << setw(10) << areaCaja(miCaja) << endl << endl;
cout << setw(16) << "El volumen de suCaja es " << setw(10) << suCaja.volumen()
<< " El área de suCaja es " << setw(10) << areaCaja(suCaja) << endl << endl;
cout << "El volumen de laCaja es " << setw(10) << laCaja.volumen()
<< " El área de laCaja es " << setw(10) << areaCaja(laCaja) << endl << endl;
cout << endl << endl << endl;
}
199

// IH149.cpp: The friend functions of a class. Las funciones amigas de una clase. (2)
// Placing friend function definitions inside the class. Colocar las
// definiciones de las funciones amigas adentro de la clase.
//
// A veces, es deseable que una función especial que no es miembro de una
// clase, tenga acceso a todos los datos miembros de la clase.
// Dichas funciones forman un grupo élite, con privilegios especiales.
// A esas funciones se les denomina "friend functions of a class"
// (funciones amigas de una clase).
// Para definirlas, se usa la palabra clave "friend".
// MMM: Es factible incluir únicamente el prototipo de la función amiga,
// o incluir la definición completa de la función. Cuando dentro del
// código de la definición de la clase se incluye la definición
// completa de la función amiga, ésta se vuelve una función inline.
// Las funciones amigas no son funciones miembros de la clase.
// Los atributos de acceso (public, private, protected) no se aplican a
// las funciones amigas.
// Las funciones amigas son funciones ordinarias globales con privilegios
// especiales con respecto a la clase en la que están definidas.
//
#include <iostream>
#include <iomanip>
using namespace std;
class CCaja
{
public:
// Definición del constructor de CCaja
CCaja(double lng, double anc, double alt) : m_longitud{ lng },
m_ancho{ anc }, m_alto{ alt }
{ cout << "Se ha llamado al constructor de CCaja, con tres argumentos.\n\n"; }
// Definición explícita del constructor
explicit CCaja(double lado) : CCaja(lado, lado, lado)
{
200

cout << "Se ha llamado al constructor de CCaja, con un argumento repetido.\n\n";


}
// Definición del constructor por defecto
CCaja() = default;
// Función para calcular el volumen de la caja
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
// Definición de la función amiga areaCaja(), que es una función global
// ordinaria.
// Como no es una función miembro de la clase CCaja, el atributo "private"
// no se aplica.
// El parámetro de la función areaCaja() es la clase misma CCaja, lo que
// permite el acceso a los datos miembros privados m_longitud, m_ancho y
// m_alto.
// Debido a que la función amiga no es una función miembro de la clase,
// los datos miembros no pueden ser designados directamente por sus nombres,
// y por ello no se utilizan como argumentos de la función.
// Al declararse una función como amiga de una clase, automáticamente se le
// abre la posibilidad del acceso a todos los miembros de la clase, sin
// ninguna restricción.
// MMM: Aquí se escribe primero el prototipo de la función dentro del cuerpo
// de código de la clase. Luego, se define la función con su cuerpo de
// código afuera de la clase.
// En el actual programa IH149, se escribirá todo dentro del cuerpo de
// código de la clase, lo cual oculta del todo el código de la clase
// amiga. Esto puede a veces no ser tan recomendable.
friend double areaCaja(const CCaja& estaCaja)
201

{
return 2.0 * (estaCaja.m_longitud * estaCaja.m_ancho +
estaCaja.m_longitud * estaCaja.m_alto +
estaCaja.m_ancho * estaCaja.m_alto);
}
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja miCaja{ 88.4, 66.2, 22.0 }; // Crear el objeto usando tres argumentos.
CCaja suCaja{ 77.5 }; // Crear el objeto usando un lado repetido.
CCaja laCaja; // Crear el objeto usando el constructor por defecto.
// El compilador asigna áreas de memoria para los 3
// parámetros de la clase, sin contenido digital definido.
cout << "El volumen de miCaja es " << setw(10) << miCaja.volumen()
<< " El área de miCaja es " << setw(10) << areaCaja(miCaja) << endl << endl;
cout << setw(16) << "El volumen de suCaja es " << setw(10) << suCaja.volumen()
<< " El área de suCaja es " << setw(10) << areaCaja(suCaja) << endl << endl;
cout << "El volumen de laCaja es " << setw(10) << laCaja.volumen()
<< " El área de laCaja es " << setw(10) << areaCaja(laCaja) << endl << endl;
cout << endl << endl << endl;
}

// IH150.cpp: The default copy constructor. El constructor de copia por defecto.


//
// Supongamos que definimos una clase denominada CCaja, con sus constructores,
// funciones miembros, datos miembros y función amiga.
// En el programa main(), función main(), creamos un objeto miCaja. Así:
// CCaja miCaja{ 88.4, 66.2, 22.0 };
// // Crear el objeto usando tres argumentos.
// Ahora deseamos crear un nuevo objeto suCaja2, con las mismas características
202

// de los argumentos que tiene el objeto miCaja.


// Usamos la siguiente instrucción:
// CCaja suCaja2{ miCaja }; // Crear copia idéntica del objeto miCaja.
// El compilador llama al constructor de la clase por sólo una vez. En la
// segunda llamada, el compilador usa el objeto ya creado y lo copia. La
// versión por defecto del constructor copia procede a copiar miembro por
// miembro todo el contenido del objeto ya creado que es exactamente del mismo
// tipo que el nuevo objeto de la instrucción.
// MMM: Esto funciona perfectamente para clases que son muy sencillas. Las
// clases que contienen punteros miembros ya no se copian bien. Para
// lograrlo, el usuario tiene que crear sus propios constructores copia.
//
#include <iostream>
#include <iomanip>
using namespace std;
class CCaja
{
public:
// Definición del constructor de CCaja
CCaja(double lng, double anc, double alt) : m_longitud{ lng },
m_ancho{ anc }, m_alto{ alt }
{ cout << "Se ha llamado al constructor de CCaja, con tres argumentos.\n\n"; }
// Definición explícita del constructor.
explicit CCaja(double lado) : CCaja(lado, lado, lado)
{
cout << "Se ha llamado al constructor de CCaja, con un argumento repetido.\n\n";
}
// Definición del constructor por defecto.
CCaja() = default;
// Función para calcular el volumen de la caja
double volumen()
{
return m_longitud * m_ancho * m_alto;
203

}
private:
double m_longitud;
double m_ancho;
double m_alto;
// Definición de la función amiga areaCaja(), que es una función global ordinaria.
// Como no es una función miembro de la clase CCaja, el atributo "private" no se
// aplica.
// El parámetro de la función areaCaja() es la clase misma CCaja, lo que permite
// el acceso a los datos miembros privados m_longitud, m_ancho y m_alto.
// Debido a que la función amiga no es una función miembro de la clase, los datos
// miembros no pueden ser designados directamente por sus nombres, y por ello no se
// utilizan como argumentos de la función.
// Al declararse una función como amiga de una clase, automáticamente se le abre
// la posibilidad del acceso a todos los miembros de la clase, sin ninguna
// restricción.
// MMM: Aquí se escribe primero el prototipo de la función dentro del cuerpo de
// código de la clase. Luego, se define la función con su cuerpo de código
// afuera de la clase.
// En el actual programa IH149, se escribirá todo dentro del cuerpo de código
// de la clase, lo cual oculta del todo el código de la clase amiga. Esto
// puede a veces no ser tan recomendable.
friend double areaCaja(const CCaja& estaCaja)
{
return 2.0 * (estaCaja.m_longitud * estaCaja.m_ancho +
estaCaja.m_longitud * estaCaja.m_alto +
estaCaja.m_ancho * estaCaja.m_alto);
}
};
int main()
{
system("color FD");
cout << endl << endl << endl;
204

CCaja miCaja{ 88.4, 66.2, 22.0 };// Crear el objeto usando tres argumentos.
CCaja suCaja{ 77.5 }; // Crear el objeto usando un lado repetido.
CCaja laCaja; // Crear el objeto usando el constructor por defecto.
// El compilador asigna áreas de memoria para los 3
// parámetros de la clase, sin contenido digital definido.
cout << "El volumen de miCaja es " << setw(10) << miCaja.volumen()
<< " El área de miCaja es " << setw(10) << areaCaja(miCaja) << endl << endl;
cout << setw(16) << "El volumen de suCaja es " << setw(10) << suCaja.volumen()
<< " El área de suCaja es " << setw(10) << areaCaja(suCaja) << endl << endl;
cout << "El volumen de laCaja es " << setw(10) << laCaja.volumen()
<< " El área de laCaja es " << setw(10) << areaCaja(laCaja) << endl << endl;
//---------------------------------------------------------------------------------
CCaja suCaja2{ miCaja }; // Crear copia idéntica del objeto miCaja.
// El compilador usa un constructor copia.
cout << setw(16) << "El volumen de suCaja2 es " << setw(10) << suCaja2.volumen()
<< " El área de suCaja2 es " << setw(10) << areaCaja(suCaja2) << endl << endl;
cout << endl << endl << endl;
}

// IH151.cpp: The pointer "this". El puntero "this" (esto, este, esta). El operador de
// acceso indirecto ->.
//
// Al crear una clase con sus miembros de datos y de funciones, cada objeto
// derivado de la clase que se procede a crear tendrá su propio conjunto de
// datos y funciones miembros.
// Existe un mecanismo en las funciones para que se refieran a los miembros
// específicos de los objetos desde los cuales se ha llamado a la función.
// Cuando cualquier función miembro se ejecuta, existe un puntero oculto,
// con el nombre "this", el cual contiene la dirección de memoria del objeto
// desde el cual se ha llamado a la función.
// Así, cuando el nombre de un dato miembro de la clase, m_longitud, aparece
// en el cuerpo de código de la función volumen(), en realidad es
205

// this->m_longitud. Esta es la referencia completa y justa al dato miembro


// del objeto que es usado para llamar a la función.
// El compilador se hace cargo de agregarle this-> a los nombres de los
// datos miembros en la función.
// El usuario puede usar en forma explícita el puntero this-> dentro de una
// función miembro. Por ejemplo, para retornar un puntero a la dirección de
// memoria del objeto en uso.
//
#include <iostream>
using namespace std;
class CCaja
{
public:
// Definición del constructor.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor.\n\n";
}
// Definición de la función volumen().
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función comparar(), que compara si la primera caja es mayor
// que la segunda (true) o al contrario (false).
bool comparar(CCaja& miCaja)
{
return this->volumen() > miCaja.volumen();
/* MMM: Recuerde que se usa el operador de acceso directo a miembros de las
clases cuando el acceso es desde objetos; y se usa el operador de
acceso indirecto a miembros cuando el acceso es por medio de
punteros. Aquí, "this" es un puntero oculto, así que el acceso es
206

indirecto mediante el operador "->".


*/
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja lustrar{ 24.3, 31.8, 12.6 };
CCaja cigarros{ 18.0, 27.9, 22.6 };
if (lustrar.comparar(cigarros))
cout << "La caja de cigarros es más pequeña que la de lustrar.";
else
cout << "La caja de cigarros es igual o mayor que la de lustrar.";
cout << "\n\nEl volumen de la caja de cigarros es: " << cigarros.volumen()
<< " cm cúbicos.";
cout << "\n\nEl volumen de la caja de lustrar es: " << lustrar.volumen()
<< " cm cúbicos.";
cout << endl << endl << endl;
}

// IH152.cpp: The pointer "this". El puntero "this" (esto, este, esta). (2) Acceso directo.
//
// Al crear una clase con sus miembros de datos y de funciones, cada objeto
// derivado de la clase que se procede a crear tendrá su propio conjunto de datos y
// funciones miembros.
// Existe un mecanismo en las funciones para que se refieran a los miembros
// específicos de los objetos desde los cuales se ha llamado a la función.
207

// Cuando cualquier función miembro se ejecuta, existe un puntero oculto, con el


// nombre "this", el cual contiene la dirección de memoria del objeto desde el cual
// se ha llamado a la función.
// Así, cuando el nombre de un dato miembro de la clase, m_longitud, aparece en el
// cuerpo de código de la función volumen(), en realidad es this->m_longitud. Esta
// es la referencia completa y justa al dato miembro del objeto que es usado para
// llamar a la función.
// El compilador se hace cargo de agregarle this-> a los nombres de los datos
// miembros en la función.
// El usuario puede usar en forma explícita el puntero this-> dentro de una función
// miembro. Por ejemplo, para retornar un puntero a la dirección de memoria del
// objeto en uso.
//
#include <iostream>
using namespace std;
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor.\n\n";
}
// Definición de la función volumen().
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función comparar(), que compara si la primera caja es mayor que la
// segunda (true) o al contrario (false).
bool comparar(CCaja& miCaja)
{
208

return volumen() > miCaja.volumen();


/* MMM: Recuerde que se usa el operador de acceso directo a miembros de las
clases cuando el acceso es desde objetos; y se usa el operador de acceso
indirecto a miembros cuando el acceso es por medio de punteros. Aquí,
"this" es un puntero oculto, así que el acceso es indirecto mediante el
operador "->". Como esta función comparar() está dentro del cuerpo de
código de la clase CCaja, lo mismo que la función volumen(), es posible
usar la función volumen() en forma directa dentro de comparar().
*/
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja lustrar{ 24.3, 31.8, 12.6 };
CCaja cigarros{ 18.0, 27.9, 22.6 };
if (lustrar.comparar(cigarros))
cout << "La caja de cigarros es más pequeña que la de lustrar.";
else
cout << "La caja de cigarros es igual o mayor que la de lustrar.";
cout << "\n\nEl volumen de la caja de cigarros es: " << cigarros.volumen()
<< " cm cúbicos.";
cout << "\n\nEl volumen de la caja de lustrar es: " << lustrar.volumen()
<< " cm cúbicos.";
cout << endl << endl << endl;
}
209

// IH153.cpp: The pointer "this". El puntero "this" (esto, este, esta). (3)
//
// Al crear una clase con sus miembros de datos y de funciones, cada objeto
// derivado de la clase que se procede a crear tendrá su propio conjunto de datos y
// funciones miembros.
// Existe un mecanismo en las funciones para que se refieran a los miembros
// específicos de los objetos desde los cuales se ha llamado a la función.
// Cuando cualquier función miembro se ejecuta, existe un puntero oculto, con el
// nombre "this", el cual contiene la dirección de memoria del objeto desde el cual
// se ha llamado a la función.
// Así, cuando el nombre de un dato miembro de la clase, m_longitud, aparece en el
// cuerpo de código de la función volumen(), en realidad es this->m_longitud. Esta
// es la referencia completa y justa al dato miembro del objeto que es usado para
// llamar a la función.
// El compilador se hace cargo de agregarle this-> a los nombres de los datos
// miembros en la función.
// El usuario puede usar en forma explícita el puntero this-> dentro de una función
// miembro. Por ejemplo, para retornar un puntero a la dirección de memoria del
// objeto en uso.
//
#include <iostream>
using namespace std;
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor.\n\n";
}
// Definición de la función volumen().
double volumen()
210

{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Prototipo de la función comparar().
bool comparar(CCaja& miCaja, CCaja& suCaja);
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja lustrar{ 24.3, 31.8, 12.6 };
CCaja cigarros{ 18.0, 27.9, 22.6 };
if (comparar(lustrar, cigarros))
cout << "La caja de cigarros es más pequeña que la de lustrar.";
else
cout << "La caja de cigarros es igual o mayor que la de lustrar.";
cout << "\n\nEl volumen de la caja de cigarros es: " << cigarros.volumen() << " cm cúbicos.";
cout << "\n\nEl volumen de la caja de lustrar es: " << lustrar.volumen() << " cm cúbicos.";
cout << endl << endl << endl;
}
// Definición de la función comparar(), que compara si la primera caja es mayor que la
// segunda (true) o al contrario (false). Afuera de la clase, esta función es ahora una
// función ordinaria.
bool comparar(CCaja& miCaja, CCaja& suCaja)
{
// return volumen() > miCaja.volumen();// Ya esta instrucción no reconoce la función
// // volumen().
/* MMM: Recuerde que se usa el operador de acceso directo a miembros de las clases
cuando el acceso es desde objetos; y se usa el operador de acceso indirecto a
211

miembros cuando el acceso es por medio de punteros. Aquí, "this" es un


puntero oculto, así que el acceso es indirecto mediante el operador "->".
Como esta función comparar() está dentro del cuerpo de código de la clase
CCaja, lo mismo que la función volumen(), es posible usar la función volumen()
en forma directa dentro de comparar().
*/
return miCaja.volumen() > suCaja.volumen();
}

// IH154.cpp: Const objects. Objetos constantes.


//
// Muchas funciones actúan en forma que no altera el contenido de miembros del
// objeto para el cual son llamadas. El objeto para el que actúan continúa
// constante. Así como hay datos que permanecen constantes, como el factor pi
// 3.14159, por ejemplo, se puede tener también objetos constantes. Recordemos
// que un objeto es en realidad un tipo de dato, para la computadora, para el
// compilador.
// Para que un objeto creado a partir de una clase sea considerado constante
// por el compilador, se usa la palabra clave "const" antes de la declaración
// del objeto.
//
#include <iostream>
using namespace std;
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
212

// Definición de la función volumen().


double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Prototipo de la función comparar().
bool comparar(const CCaja& miCaja, const CCaja& suCaja);
int main()
{
system("color FD");
cout << endl << endl << endl;
const CCaja lustrar{ 24.3, 31.8, 12.6 };// Declaración del objeto lustrar como constante.
const CCaja cigarros{ 18.0, 27.9, 22.6 };// Intento de declarar el objeto cigarros como
// constante también.
if (comparar(lustrar, cigarros))
cout << "La caja de cigarros es más pequeña que la de lustrar.";
else
cout << "La caja de cigarros es igual o mayor que la de lustrar.";
cout << "\n\nEl volumen de la caja de cigarros es: "
<< cigarros.volumen() << " cm cúbicos.";
cout << "\n\nEl volumen de la caja de lustrar es: "
<< lustrar.volumen() << " cm cúbicos.";
cout << endl << endl << endl;
}
// Definición de la función comparar(), que compara si la primera caja es mayor que la
// segunda (true) o al contrario (false). Afuera de la clase, esta función es ahora una
// función ordinaria.
bool comparar(const CCaja& miCaja, const CCaja& suCaja)
213

{
// return volumen() > miCaja.volumen();// Ya esta instrucción no reconoce la función
// // volumen().
/* MMM: Recuerde que se usa el operador de acceso directo a miembros de las clases
cuando el acceso es desde objetos; y se usa el operador de acceso indirecto a
miembros cuando el acceso es por medio de punteros. Aquí, "this" es un
puntero oculto, así que el acceso es indirecto mediante el operador "->".
Como esta función comparar() está dentro del cuerpo de código de la clase
CCaja, lo mismo que la función volumen(), es posible usar la función volumen()
en forma directa dentro de comparar().
*/
return miCaja.volumen() > suCaja.volumen();
}

// IH155.cpp: Const member functions of a class. Funciones miembro constantes de una clase.
//
// Al definir un objeto de una clase como constante, utilizando la palabra clave
// const, este objeto siempre tendrá un puntero this de carácter constante.
// El compilador no permitirá ninguna función miembro que sea llamada que no
// asuma que este puntero this sea constante.
// Es necesario convertir este puntero this a un puntero constante.
// Para hacer que el puntero this sea constante en una función miembro, se debe
// definir la función miembro como función constante en el cuerpo de código de la
// clase.
// MMM: Para especificar que una función miembro es constante, se agrega la
// palabra clave "const" al final del encabezado de la clase.
// Esto sólo se puede hacer con funciones que son miembros de la clase. No
// se puede hacer con funciones globales ordinarias, fuera de la clase.
// El resultado que se obtiene es que el puntero this para la función se
// vuelve constante.
// Ello implica que un dato miembro no puede aparecer a la izquierda de una
// asignación dentro de la definición de la función. Un dato miembro no
214

// puede ser un iValor.


// Una función miembro constante no puede llamar a otra función miembro no
// constante, de la misma clase, pues esto significa que se podría modificar el
// objeto. Dado que la función comparar() llama a la función volumen(), ambas
// funciones miembros de la clase CCaja, la función miembro volumen() debe
// declararse constante también. La función comparar() ahora funciona bien con
// objetos constantes y no constantes.
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Función para calcular el volumen.
double volumen() const // Función constante.
{
return m_longitud * m_ancho * m_alto;
}
// Función para comparar los volúmenes de dos cajas. Devuelve un valor booleano.
bool comparar(const CCaja& laCaja) const // Función constante.
{
return this->volumen() > laCaja.volumen();
}
private:
double m_longitud;
double m_ancho;
215

double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
const CCaja miCaja{ 75.0, 58.6, 32.9 }; // Objeto constante.
const CCaja suCaja{ 60.4, 28.7, 16.3 }; // Otro objeto constante.
// MMM: La siguiente instrucción dio tremenda guerra. No aceptaba la sintaxis.
// Indicaba nombre no definido.
if (suCaja.comparar(miCaja))
cout << "La caja suCaja es más pequeña que miCaja.";
else
cout << "La caja suCaja es igual o mayor que miCaja.";

cout << "\n\nEl volumen de la caja suCaja es: " << suCaja.volumen() << " cm cúbicos.";
cout << "\n\nEl volumen de la caja miCaja es: " << miCaja.volumen() << " cm cúbicos.";
cout << endl << endl << endl;
}

// IH156.cpp: Member function definitions outside the class. Definiciones de funciones


// miembros afuera de la clase.
//
// Cuando la definición de la función miembro de la clase se escribe afuera del
// cuerpo de código de la clase, y esta función es constante, en el encabezado de la
// función debe escribirse el calificador o palabra clave const, al final. Lo mismo
// debe hacerse en la declaración de la función miembro dentro de la clase.
// Es una buena costumbre definir toda función miembro que no hace
// modificaciones en el objeto creado a partir de la clase, desde el que se llama a la
// función, con el calificador const al final del encabezado de la función.
//
#include <iostream>
216

using namespace std;


class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor.\n\n";
}
// Prototipo de la función miembro volumen().
double volumen() const;
// Prototipo de la función miembro comparar().
bool comparar(const CCaja& laCaja) const;
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
const CCaja lustrar{ 24.3, 31.8, 12.6 };// Declaración del objeto lustrar como constante.
const CCaja cigarros{ 18.0, 27.9, 22.6 };// Intento de declarar el objeto cigarros como
// constante también.
if (lustrar.comparar(cigarros))
cout << "La caja de cigarros es más pequeña que la de lustrar.";
else
cout << "La caja de cigarros es igual o mayor que la de lustrar.";
cout << "\n\nEl volumen de la caja de cigarros es: "
<< cigarros.volumen() << " cm cúbicos.";
cout << "\n\nEl volumen de la caja de lustrar es: "
217

<< lustrar.volumen() << " cm cúbicos.";


cout << endl << endl << endl;
}
// Definición de la función comparar(), que compara si la primera caja es mayor que la
// segunda (true) o al contrario (false). Afuera de la clase, esta función es ahora una
// función ordinaria.
bool CCaja::comparar(const CCaja& laCaja) const
{
// return volumen() > laCaja.volumen();// Ya esta instrucción no reconoce la función
// // volumen().
/* MMM: Recuerde que se usa el operador de acceso directo a miembros de las clases
cuando el acceso es desde objetos; y se usa el operador de acceso indirecto a
miembros cuando el acceso es por medio de punteros. Aquí, "this" es un
puntero oculto, así que el acceso es indirecto mediante el operador "->".
Como esta función comparar() está dentro del cuerpo de código de la clase
CCaja, lo mismo que la función volumen(), es posible usar la función volumen()
en forma directa dentro de comparar(). */
return this->volumen() > laCaja.volumen();
}
// Definición de la función volumen().
double CCaja::volumen() const
{
return m_longitud * m_ancho * m_alto;
}

// IH157.cpp: The pointer "this". El puntero "this" (esto, este, esta). El operador de acceso
// indirecto ->. Prueba con objetos constantes.
//
// Al crear una clase con sus miembros de datos y de funciones, cada objeto
// derivado de la clase que se procede a crear tendrá su propio conjunto de datos y
// funciones miembros.
// Existe un mecanismo en las funciones para que se refieran a los miembros
218

// específicos de los objetos desde los cuales se ha llamado a la función.


// Cuando cualquier función miembro se ejecuta, existe un puntero oculto, con el
// nombre "this", el cual contiene la dirección de memoria del objeto desde el cual
// se ha llamado a la función.
// Así, cuando el nombre de un dato miembro de la clase, m_longitud, aparece en el
// cuerpo de código de la función volumen(), en realidad es this->m_longitud. Esta
// es la referencia completa y justa al dato miembro del objeto que es usado para
// llamar a la función.
// El compilador se hace cargo de agregarle this-> a los nombres de los datos
// miembros en la función.
// El usuario puede usar en forma explícita el puntero this-> dentro de una función
// miembro. Por ejemplo, para retornar un puntero a la dirección de memoria del
// objeto en uso.
// MMM: Probaremos ahora con objetos constantes.
//
#include <iostream>
using namespace std;
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición de la función volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función comparar(), que compara si la primera caja es mayor que la
// segunda (true) o al contrario (false).
219

bool comparar(const CCaja& miCaja) const


{
return this->volumen() > miCaja.volumen();
/* MMM: Recuerde que se usa el operador de acceso directo a miembros de las
clases cuando el acceso es desde objetos; y se usa el operador de acceso
indirecto a miembros cuando el acceso es por medio de punteros. Aquí,
"this" es un puntero oculto, así que el acceso es indirecto mediante el
operador "->".
*/
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja lustrar{ 24.3, 31.8, 12.6 };
CCaja cigarros{ 18.0, 27.9, 22.6 };
if (lustrar.comparar(cigarros))
cout << "La caja de cigarros es más pequeña que la de lustrar.";
else
cout << "La caja de cigarros es igual o mayor que la de lustrar.";
cout << "\n\nEl volumen de la caja de cigarros es: " << cigarros.volumen() << " cm cúbicos.";
cout << "\n\nEl volumen de la caja de lustrar es: " << lustrar.volumen() << " cm cúbicos.";
cout << endl << endl << endl;
}
220

// IH158.cpp: The pointer "this". El puntero "this" (esto, este, esta). (2) Acceso directo.
// Prueba con objetos constantes.
//
// Al crear una clase con sus miembros de datos y de funciones, cada objeto
// derivado de la clase que se procede a crear tendrá su propio conjunto de datos y
// funciones miembros.
// Existe un mecanismo en las funciones para que se refieran a los miembros
// específicos de los objetos desde los cuales se ha llamado a la función.
// Cuando cualquier función miembro se ejecuta, existe un puntero oculto, con el
// nombre "this", el cual contiene la dirección de memoria del objeto desde el cual
// se ha llamado a la función.
// Así, cuando el nombre de un dato miembro de la clase, m_longitud, aparece en el
// cuerpo de código de la función volumen(), en realidad es this->m_longitud. Esta
// es la referencia completa y justa al dato miembro del objeto que es usado para
// llamar a la función.
// El compilador se hace cargo de agregarle this-> a los nombres de los datos
// miembros en la función.
// El usuario puede usar en forma explícita el puntero this-> dentro de una función
// miembro. Por ejemplo, para retornar un puntero a la dirección de memoria del
// objeto en uso.
//
#include <iostream>
using namespace std;
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición de la función volumen().
221

double volumen() const


{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función comparar(), que compara si la primera caja es mayor que la
// segunda (true) o al contrario (false).
bool comparar(const CCaja& miCaja) const
{
return volumen() > miCaja.volumen();
/* MMM: Recuerde que se usa el operador de acceso directo a miembros de las
clases cuando el acceso es desde objetos; y se usa el operador de acceso
indirecto a miembros cuando el acceso es por medio de punteros. Aquí,
"this" es un puntero oculto, así que el acceso es indirecto mediante el
operador "->". Como esta función comparar() está dentro del cuerpo de
código de la clase CCaja, lo mismo que la función volumen(), es posible
usar la función volumen() en forma directa dentro de comparar().
*/
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja lustrar{ 24.3, 31.8, 12.6 };
CCaja cigarros{ 18.0, 27.9, 22.6 };
if (lustrar.comparar(cigarros))
cout << "La caja de cigarros es más pequeña que la de lustrar.";
else
cout << "La caja de cigarros es igual o mayor que la de lustrar.";
222

cout << "\n\nEl volumen de la caja de cigarros es: " << cigarros.volumen() << " cm cúbicos.";
cout << "\n\nEl volumen de la caja de lustrar es: " << lustrar.volumen() << " cm cúbicos.";
cout << endl << endl << endl;
}

// IH159.cpp: Constructor separately defined. Un constructor definido separadamente.


//
// El c++ permite declarar un constructor como prototipo dentro del cuerpo de
// código de la clase, y definir el constructor fuera de la clase.
// Es lo mismo que permite con las funciones.
// El usuario puede definir todo el constructor dentro del código de la clase,
// lo mismo que las funciones.
// Aquí veremos un ejemplo de ambos.
// Los objetos y las funciones son constantes.
//
#include <iostream>
using namespace std;
class CCaja
{
public:
// Declaración del prototipo del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0, double alt = 1.0);
// Declaración del prototipo de la función miembro volumen().
double volumen() const;
// Declaración del prototipo de la función miembro comparar().
bool comparar(const CCaja& laCaja) const;
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
223

{
system("color FD");
cout << endl << endl << endl;
CCaja miCaja(80.3, 66.2, 48.0);
CCaja tuCaja(66.4, 33.9, 27.5);

if (miCaja.comparar(tuCaja))
cout << "El volumen de miCaja es mayor que el de tuCaja.\n\n";
else
cout << "El volumen de miCaja es menor o igual que el de tuCaja.\n\n";
cout << endl << endl << endl;
}
// Definición del constructor de la clase CCaja.
CCaja::CCaja(double lng, double anc,
double alt) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición de la función miembro volumen().
double CCaja::volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función miembro comparar().
bool CCaja::comparar(const CCaja& laCaja) const
{
return this->volumen() > laCaja.volumen();
}
224

// IH160.cpp: Arrays of class objects. Matrices de objetos del tipo clase.


//
// Los tipos de datos fundamentales, tales como int, double, short, float, pueden
// constituir elementos de los que se usan para crear matrices.
// Así como las matrices de elementos, es factible la creación de matrices de
// objetos.
// Cada elemento de una matriz de objetos que no es inicializado, provoca una
// llamada al constructor por defecto de la clase del objeto. Si el compilador no
// encuentra este constructor por defecto, emitirá una señal de error.
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición del constructor por defecto de la clase CCaja.
CCaja()
{
cout << "Se ha llamado al constructor por defecto de la clase CCaja.\n";
m_longitud = m_ancho = m_alto = 1.0;
}
// Definición de la función miembro volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
225

private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
// Definición de una matriz de 5 objetos CCaja.
CCaja mtzCajas[5];
cout << endl << endl;
CCaja cigarrosBox(8.0, 5.3, 2.8);
cout << "El volumen de la caja mtzCajas[2] es "
<< mtzCajas[2].volumen() << " cm cúbicos.\n\n"
<< "El volumen de la caja de cigarros es "
<< cigarrosBox.volumen() << "cm cúbicos.\n\n\n";
//---------------------------------------------------------------------------
CCaja boxes2[6]{ CCaja{44.1, 38.9, 28.0}, CCaja{77.3, 69.8, 36.6} };
/* Se crea la matriz de 6 objetos CCaja, de los cuales se inicializan los primeros
dos objetos, boxes2[0] y boxes2[1]. Los demás serán inicializados por el
constructor por defecto.
*/
cout << "\n\n\nEl volumen de la caja boxes2[0] es "
<< boxes2[0].volumen() << " cm cúbicos.\n\n"
<< "El volumen de la caja boxes2[1] es "
<< boxes2[1].volumen() << " cm cúbicos.\n\n"
<< "El volumen de la caja boxes2[2] es "
<< boxes2[2].volumen() << " cm cúbicos.\n\n";
cout << endl << endl << endl;
}
226

// IH161.cpp: Static members of a class. Los miembros estáticos de una clase. Static data
// members. Los datos miembros estáticos.
//
// Los datos miembros de una clase, y las funciones miembros, pueden ser
// declarados como estáticos.
// Esta es la misma funcionalidad que tienen las variables estáticas dentro de una
// función.
// Existe una diferencia en uso con respecto a la palabra clave static fuera de una
// clase y el uso de la palabra clave static dentro de una clase.
// Cuando se aplica la palabra static a un dato miembro, sólo se crea una instancia
// del dato, y esta instancia es compartida por todos los objetos creados a partir
// de la clase. Cada objeto logra obtener una copia de cada uno de los datos
// miembros ordinarios de la clase, pero del dato miembro estático sólo existe una
// copia.
// Uno de los usos de un dato miembro estático en una clase es para contar cuántos
// objetos de la clase han sido creados.
// Se hace estático un dato miembro así:
// public:
// static int conteoObj;
// No se puede inicializar un dato miembro estático dentro de la definición de la
// clase porque tendría que ser numérico y constante, o enumeración y constante.
// No se puede inicializar en un constructor porque se necesita incrementar el
// contador cada vez que el constructor sea llamado para crear un objeto de la
// clase.
// No se puede inicializar en alguna función miembro porque una función miembro
// está asociada con un objeto ya creado, y se necesita crear el contador antes de
// que un objeto haya sido creado.
// Sin embargo, es posible inicializar el contador fuera de la clase, así:
// int CCaja::conteoObj{};
// MMM: Note que la palabra static no se usa en la inicialización. El nombre de la
// clase es indispensable para marcar el tipo de dato, y que el compilador no
// haga la creación de una variable global que no tiene nada que ver con la
// clase.
227

// El dato miembro estático creado en la clase CCaja tiene un solo valor, y es


// posible tener acceso a ese dato desde cualquier parte, desde la clase CCaja, o
// desde uno de los objetos creados a partir de ella. El valor es el mismo.
// Aunque no se hayan creado objetos, el dato miembro estático existe antes de
// entrar a la función main(), pues ha sido ya inicializado con el valor cero.
// También se inicializa a cero por defecto así:
// int CCaja::conteoObj;
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, en ámbito global.
class CCaja
{
public:
// Declaración del objeto estático conteoObj. Se usará para contar los objetos.
static int conteoObj;
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
conteoObj++;
}
// Definición del constructor por defecto de la clase CCaja.
CCaja()
{
cout << "Se ha llamado al constructor por defecto de la clase CCaja.\n\n";
m_longitud = m_ancho = m_alto = 1.0;
conteoObj++;
}
// Definición de la función volumen(), para calcular el volumen de una caja.
double volumen() const
{
228

return m_longitud * m_ancho * m_alto;


}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Inicialización del dato miembro estático de la clase CCaja.
int CCaja::conteoObj{ };
int main()
{
system("color FD");
cout << endl << endl << endl;
// Crear matriz de 5 cajas.
CCaja cajas[5];
CCaja cigarros{ 10.0, 8.0, 4.6 };
cout << "Cantidad de objetos creados, dato de la clase CCaja = " << CCaja::conteoObj
<< endl << endl;
cout << "Cantidad de objetos creados, dato de un objeto = " << cajas[2].conteoObj;
cout << endl << endl << endl;
}

// IH162.cpp: Static function members of a class. Las funciones miembros estáticas de una
// clase.
//
// Al declarar una función miembro de una clase con el carácter de estática,
// (static), la hacemos independiente de todos los objetos creados a partir de esa
// clase.
// Una función estática no integra al puntero this.
// Una función miembro estática tiene la ventaja de que sí existe, es válida y se
// le puede llamar de una manera legítima, incluso aunque no exista ningún objeto
// de esa clase.
229

// Sólo datos miembros estáticos de la clase pueden ser accesados desde esta
// función.
// Por ello, se llama a estas funciones estáticas para comprobar los datos
// miembros de carácter estático, pues son los únicos seguros de existir, aunque
// no existan objetos creados.
// Con una función estática se puede verificar cuántos objetos han sido creados,
// o si en efecto se ha creado algún objeto de la clase.
// Un prototipo de una función estática sería así:
// static void miFuncion(int n);
// Una función estática se puede llamar desde un objeto de la clase:
// esteObj.miFuncion(12);
// Así, la función estática no tiene acceso a los datos miembros no estáticos de
// esteObj.
// También se puede llamar desde la clase, así:
// CCaja::miFuncion(8);
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, en ámbito global.
class CCaja
{
public:
// Declaración del objeto estático conteoObj. Se usará para contar los objetos.
static int conteoObj;
/*
// Declaración de la función miembro estática miFuncion(). Esto no funcionó.
static void miFuncion(int n);
*/
// Definición de la función miembro estática miFuncion().
static void miFuncion(int n)
{
cout << "Hasta ahora, la cantidad de objetos creados es: "
<< CCaja::conteoObj << ".\n\n";
230

}
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
conteoObj++;
}
// Definición del constructor por defecto de la clase CCaja.
CCaja()
{
cout << "Se ha llamado al constructor por defecto de la clase CCaja.\n\n";
m_longitud = m_ancho = m_alto = 1.0;
conteoObj++;
}
// Definición de la función volumen(), para calcular el volumen de una caja.
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Inicialización del dato miembro estático de la clase CCaja.
int CCaja::conteoObj{ };
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja::miFuncion(0);
CCaja cajas[5]; // Crear matriz de 5 cajas.
231

CCaja cigarros{ 10.0, 8.0, 4.6 };


cout << "Cantidad de objetos creados, dato de la clase CCaja = " << CCaja::conteoObj
<< endl << endl;
cout << "Cantidad de objetos creados, dato de un objeto = " << cajas[2].conteoObj << ".\n\n";
CCaja::miFuncion(0);
cajas[3].miFuncion(0);
cout << endl << endl << endl;
}

// IH163.cpp: Pointers and references to objects. Pointers to objects. Los punteros y las
// referencias a los objetos. Los punteros a los objetos.
//
// En la especificación de los parámetros de las funciones, es muy importante el
// uso de las referencias y los punteros a los objetos derivados de las clases.
// Los objetos pueden contener enormes volúmenes de datos, y el paso por valor
// significa que al inicializar la función, el compilador tendrá que copiar esos
// enormes volúmenes de datos.
// Este mecanismo de paso por valor consumirá además una gran cantidad de
// tiempo de ejecución.
// Esta sobrecarga de ejecución se evita al usar referencias y punteros.
// MMM: No es posible escribir un constructor copia sin utilizar los parámetros
// de referencia.
// Se define un puntero a un objeto de la misma manera en que se define un
// puntero a cualquier otro dato.
// Así se define un puntero a la clase CCaja, inicializado a nullptr:
// CCaja* rCaja { };
// Este puntero puede usarlo para almacenar la dirección de memoria de un objeto
// creado con la clase CCaja:
// CCaja miCaja;
// rCaja = &miCaja;
// Si la clase CCaja tiene la función miembro volumen(), el llamado a la función
// sería:
232

// cout << rCaja->volumen();


//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, en ámbito global.
class CCaja
{
public:
// Declaración del objeto estático conteoObj. Se usará para contar los objetos.
static int conteoObj;
/*
// Declaración de la función miembro estática miFuncion(). Esto no funcionó.
static void miFuncion(int n);
*/
// Definición de la función miembro estática miFuncion().
static void miFuncion(int n)
{
cout << "Hasta ahora, la cantidad de objetos creados es: "
<< CCaja::conteoObj << ".\n\n";
}
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
conteoObj++;
}
// Definición del constructor por defecto de la clase CCaja.
CCaja()
{
cout << "Se ha llamado al constructor por defecto de la clase CCaja.\n\n";
m_longitud = m_ancho = m_alto = 1.0;
conteoObj++;
233

}
// Definición de la función volumen(), para calcular el volumen de una caja.
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Inicialización del dato miembro estático de la clase CCaja.
int CCaja::conteoObj{ };
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja::miFuncion(0);
CCaja cajas[5]; // Crear matriz de 5 cajas.

CCaja cigarros{ 10.0, 8.0, 4.6 };


cout << "Cantidad de objetos creados, dato de la clase CCaja = " << CCaja::conteoObj
<< endl << endl;
cout << "Cantidad de objetos creados, dato de un objeto = " << cajas[2].conteoObj << ".\n\n";
CCaja::miFuncion(0);
cajas[3].miFuncion(0);
//----------------------------------------------------------------------
CCaja* rCaja{ }; // Puntero a la función CCaja. nullptr.
rCaja = &cigarros; // Asignación de la dirección de memoria del
// objeto "cigarros", del tipo CCaja, al puntero.
cout << "El volumen del objeto \"cigarros\" es: " << rCaja->volumen();
cout << endl << endl << endl;
}
234

// IH164.cpp: Pointers to classes. Exercising the indirect member access operator. Los punteros
// a las clases. Practicar el operador indirecto de acceso a los miembros de una
// clase.
//
// Al crear un objeto a partir de la definición de una clase, este objeto ocupa una
// dirección específica en la memoria de la computadora. Esa dirección específica
// es posible asignarla o guardarla en un tipo de dato especial llamado puntero.
// El puntero se asigna a la dirección de memoria del objeto creado con la clase.
// La clase también puede servir para crear una matriz de objetos. La matriz tiene
// asimismo una dirección de memoria. Y esta dirección de memoria se puede
// señalar mediante un puntero.
// Para manipular este tipo de punteros, usaremos el operador indirecto de acceso,
// ->.
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 3.0, double anc = 2.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición de la función volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función comparar(). Devuelve verdadero o falso.
bool comparar(const CCaja* rBox) const
235

{
if (!rBox)
return false;
else
return this->volumen() > rBox->volumen();
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja cajas[5]; // Matriz de 5 objetos caja.
CCaja fosforos{ 4.0, 3.0, 2.0 };
CCaja cigarros{ 8.0, 6.0, 3.4 };
CCaja* rCaja1{ &cigarros }; // Crear puntero rCaja1, del tipo CCaja,
// a la dirección de memoria del objeto
// cigarros.
CCaja* rCaja2{}; // Crear puntero rCaja2, del tipo CCaja,
// con valor inicial nullptr.
cout << "La dirección de memoria de cigarros es: " << rCaja1 << endl << endl;
cout << "El volumen de la caja cigarros es: " << rCaja1->volumen() << endl << endl;
rCaja2 = &fosforos; // Asignar dirección de memoria de fosforos
// al puntero rCaja2.
// Usar direcciones de memoria para comparar los objetos.
if (rCaja2->comparar(rCaja1))
cout << "La caja de fósforos es mayor que la caja de cigarros.\n\n";
else
cout << "La caja de fósforos es menor o igual que la caja de cigarros.\n\n";
rCaja1 = cajas; // Redefinir el puntero rCaja1 para apuntar a la
236

// dirección de memoria de la matriz cajas[].


cajas[4] = fosforos; // Hacer el último elemento de la matriz igual a
// la caja fosforos.
// Lograr acceso a cajas[4] mediante el puntero de la matriz cajas[].
cout << "El volumen de la caja cajas[4], la última caja, es: " << (rCaja1 + 4)->volumen()
<< endl << endl;
// Lograr acceso a cajas[0] mediante el puntero de la matriz cajas[].
cout << "El volumen de la caja cajas[0], la primera caja, es: " << rCaja1->volumen();
cout << endl << endl << endl;
}

// IH165.cpp: References to class objects. Las referencias a los objetos de las clases.
//
// Las referencias demuestran su utilidad cuando se aplican a las clases.
// Las referencias a tipos de datos primitivos son similares a las referencias a
// las clases.
// Para declarar una referencia a un objeto del tipo CCaja, llamado cigarros, por
// ejemplo, se escribe así:
// CCaja& rfmiCaja { cigarros };
// La referencia simplemente sustituye el nombre del objeto al que se aplica.
// cout << rfmiCaja.volumen();
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 3.0, double anc = 2.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
237

cout << "Se ha llamado al constructor de la clase CCaja.\n\n";


}
// Definición de la función volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función comparar(). Devuelve verdadero o falso.
// Parámetros por referencia.
bool comparar(const CCaja& rfBox) const
{
if (!&rfBox)
return false;
else
return this->volumen() > rfBox.volumen();
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja cajas[5]; // Matriz de 5 objetos caja.
CCaja fosforos{ 4.0, 3.0, 2.0 };
CCaja cigarros{ 8.0, 6.0, 3.4 };
CCaja& rfCaja1{ cigarros }; // Crear referencia rCaja1, del tipo CCaja,
// a la dirección de memoria del objeto cigarros.
CCaja& rfCaja2{ fosforos }; // Crear referencia rCaja2, del tipo CCaja,
// al objeto fosforos.
cout << "La dirección de memoria de cigarros es: " << &rfCaja1 << endl << endl;
238

cout << "El volumen de la caja cigarros es: " << rfCaja1.volumen() << endl << endl;
CCaja& rfCaja3{ cajas[0] }; // Asignar dirección de memoria de fosforos
// al objeto cajas[], primer elemento.
// Usar direcciones de memoria para comparar los objetos.
if (rfCaja2.comparar(rfCaja1))
cout << "La caja de fósforos es mayor que la caja de cigarros.\n\n";
else
cout << "La caja de fósforos es menor o igual que la caja de cigarros.\n\n";
cajas[4] = fosforos; // Hacer el último elemento de la matriz igual a
// la caja fosforos.
// Lograr acceso a cajas[4] mediante la referencia de la matriz cajas[].
cout << "El volumen de la caja cajas[4], la última caja, es: "
<< (&rfCaja1 + 4)->volumen() << endl << endl;
// Lograr acceso a cajas[0] mediante la referencia de la matriz cajas[].
cout << "El volumen de la caja cajas[0], la primera caja, es: "
<< rfCaja1.volumen();
cout << endl << endl << endl;
}

// IH166.cpp: Implementing a copy constructor. Implementar un constructor de copia.


//
// Las referencias poseen una trascendencia crucial cuando se trata de parámetros
// y valores de devolución en las funciones miembros de las clases.
// En este programa veremos cómo se hace para definir un constructor de copia.
// MMM: El constructor de copia es un constructor que crea un nuevo objeto a
// partir de otro objeto ya existente. Por lo tanto, necesita aceptar a un
// objeto de la clase como uno de los argumentos.
// Se escribe el prototipo del constructor de copia así:
// CCaja(CCaja cajaCopia);
// Una vez dado lo anterior, averigüemos qué sucede cuando llamamos al
// constructor. Supongamos que escribimos esta definición:
// CCaja miCaja{ cigarros };
239

// Esto genera una llamada al constructor de copia. El argumento "cigarros" es pa-


// sado por valor. Antes de que el objeto "cigarros" sea pasado al constructor de
// copia, el compilador tiene que arreglárselas para crear una copia del objeto.
// Para ello, el compilador llama al constructor de copia para hacer una copia del
// argumento en la instrucción, cuya copia será usada por el mismo constructor de
// copia. Desafortunadamente, como el argumento es pasado por valor, se requiere
// también hacer una copia de él. Así, el constructor de copia es llamado varias
// veces para realizar la misma operación, y cada nueva llamada genera otras más.
// El constructor de copia es llamado infinitas veces.
// MMM: La solución es utilizar un parámetro de referencia constante. El prototipo
// del constructor de copia debe escribirse mejor así:
// CCaja(const CCaja& cajaCopia);
// Aquí, el argumento que va a usar el constructor de copia no necesita copiarse.
// El argumento es usado por el compilador para inicializar el parámetro de refe-
// rencia sin tener que copiarlo. Un argumento de referencia no requiere copia
// cuando se llama a la función. La función tiene acceso al argumento en forma
// directa cuando se le llama. El calificador "const" se asegura de que el argu-
// mento no sea modificado cuando se le llama, dado que por referencia sí es
// posible modificarlo directamente dentro de la función.
// MMM: Es preferible usar el calificador const siempre cuando se llama por refe-
// rencia a un parámetro en una función. A menos que se desee modificarlo.
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 3.0, double anc = 2.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
240

}
// Prototipo del constructor de copia de la clase CCaja. Aquí se está aplicando la
// sobrecarga del constructor.
CCaja(const CCaja& laCaja);
// Definición de la función volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función comparar(). Devuelve verdadero o falso.
// Parámetros por referencia.
bool comparar(const CCaja& rfBox) const
{
if (!&rfBox)
return false;
else
return this->volumen() > rfBox.volumen();
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja cajas[5]; // Matriz de 5 objetos caja.
CCaja fosforos{ 4.0, 3.0, 2.0 };
CCaja cigarros{ 8.0, 6.0, 3.4 };
CCaja& rfCaja1{ cigarros }; // Crear referencia rCaja1, del tipo CCaja,
// a la dirección de memoria del objeto cigarros.
CCaja& rfCaja2{ fosforos }; // Crear referencia rCaja2, del tipo CCaja,
241

// al objeto fosforos.
cout << "La dirección de memoria de cigarros es: " << &rfCaja1 << endl << endl;
cout << "El volumen de la caja cigarros es: " << rfCaja1.volumen() << endl << endl;
CCaja& rfCaja3{ cajas[0] }; // Asignar dirección de memoria de fosforos
// al objeto cajas[], primer elemento.
// Usar direcciones de memoria para comparar los objetos.
if (rfCaja2.comparar(rfCaja1))
cout << "La caja de fósforos es mayor que la caja de cigarros.\n\n";
else
cout << "La caja de fósforos es menor o igual que la caja de cigarros.\n\n";
cajas[4] = fosforos; // Hacer el último elemento de la matriz igual a
// la caja fosforos.
// Lograr acceso a cajas[4] mediante la referencia de la matriz cajas[].
cout << "El volumen de la caja cajas[4], la última caja, es: " << (&rfCaja1 + 4)->volumen()
<< endl << endl;
// Lograr acceso a cajas[0] mediante la referencia de la matriz cajas[].
cout << "El volumen de la caja cajas[0], la primera caja, es: " << rfCaja1.volumen();
cout << endl << endl << endl;
}
// Definición del constructor de copia de la clase CCaja.
CCaja::CCaja(const CCaja& laCaja) : m_longitud{ laCaja.m_longitud },
m_ancho{ laCaja.m_ancho }, m_alto{ laCaja.m_alto }
{
cout << "Se ha llamado al constructor de copia de la clase CCaja.\n\n";
}
242

// IH167.cpp: Class destructors. The default destructor. Los destructores de las clases.
// El destructor por defecto.
//
// Los destructores están relacionados con el alojamiento dinámico de la memoria,
// o asignación dinámica de memoria (RAM).
// Cuando se utiliza la memoria disponible en la RAM, para miembros de clase, es
// necesario usar un constructor y a la vez un destructor. Se destruyen los objetos
// en memoria con la finalidad de que no queden obstruyendo los procesos.
// La asignación dinámica de la memoria además exigirá el uso de un constructor de
// copia.
// Un destructor es una función que destruye los objetos de la "free store" cuando
// ya no son requeridos o cuando se salen del ámbito de los programas.
// Al destruir un objeto se libera la memoria que ocupaba. No así en los miembros
// estáticos, quienes siguen existiendo aún cuando ya no existan los objetos a los
// que pertenecen.
// MMM: El destructor de una clase es el nombre de la clase, precedido por el signo
// "~". Un destructor no tiene valor de retorno y tampoco tiene parámetros.
// Ejemplo:
// ~CCaja();
// Sólo puede existir un destructor para una clase dentro del código de la
// clase.
// Todos los objetos que hasta ahora han sido creados en los programas o en las
// clases han sido destruidos por el destructor por defecto.
// El compilador genera automáticamente un destructor por defecto cuando el
// usuario no define un destructor explícitamente.
// Sin embargo, el destructor por defecto no elimina automáticamente los objetos
// cuando han sido creados en la tienda gratis ("free store").
// Si se ha usado espacio de memoria para alojar dinámicamente objetos mediante
// un constructor, el usuario tiene que proveer también un destructor para liberar
// la memoria ocupada.
// MMM: A veces no es necesario usar destructores, cuando se hace uso de los pun-
// teros inteligentes.
#include <iostream>
243

using namespace std;


// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del destructor de la clase CCaja.
~CCaja()
{
cout << "Se ha llamado al destructor de la clase CCaja.\n\n";
}
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición de la función volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función comparar().
bool comparar(const CCaja* rCaja) const
{
if (!rCaja)
return false;
return this->volumen() > rCaja->volumen(); // Por punteros.
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
244

int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja lasCajas[5]; // Matriz de 5 cajas.
CCaja cigarros{ 50.4, 40.8, 28.6 };
CCaja fosforos{ 12.7, 8.5, 3.2 };
CCaja* rCigarros{ &cigarros }; // Puntero al objeto cigarros.
CCaja* rCajaVacia{}; // Puntero a una caja vacía.
cout << "El volumen de la caja cigarros es: " << rCigarros->volumen()
<< " cm cúbicos.\n\n";
rCajaVacia = lasCajas; // Puntero a la matriz.
lasCajas[2] = fosforos; // Tercer elemento de la matriz.
cout << "El volumen de lasCajas[2] es: " << (rCajaVacia + 2)->volumen()
<< " cm cúbicos.\n\n";
cout << endl << endl << endl;
}
/* MMM: Al finalizar main(), el compilador automáticamente llama al destructor de la clase
y procede a destruir cada uno de los objetos que main() ha creado a partir de la
clase CCaja. En total, son 7.
Si el destructor tiene un error, el programa se bloquea porque ya ha terminado la
ejecución de la función main().
*/

// IH168.cpp: Destructors and dynamic memory allocation. Los destructores y la asignación


// dinámica de memoria.
//
// El usuario necesitará utilizar la memoria dinámica para alojar miembros de las
// clases en forma periódica. Para lograrlo, usará el operador new en un constructor
// que le permitirá asignar memoria dinámica para un objeto. Ya una vez utilizado el
// objeto y no más requerido, el mismo usuario está obligado a destruir dicho objeto
// liberando la memoria asignada. Lo hará mediante un destructor que contenga el
245

// código requerido. En el destructor, usará el operador delete.


// Otra manera de hacerlo es mediante un puntero inteligente.
// Vamos a definir una clase ejemplo.
// La clase consiste en un mensaje que es transmitido mediante una cadena de carac-
// teres (string). Cada objeto creado es un mensaje.
// La clase se conocerá como CMensaje.
//
#include <iostream>
#include <cstring> // Para strlen() y strcpy_s().
// Aparentemente no se necesita, pues está en std.
using namespace std;
class CMensaje
{
private:
char* m_rMensaje; // Puntero a una cadena de texto.
public:
// Definición para mostrar un mensaje.
void mostrarMsj() const
{
cout << m_rMensaje << endl << endl;
}
// Definición del constructor de la clase CMensaje.
CMensaje(const char* texto = "Mensaje por defecto.")
{
size_t largo{ strlen(texto) + 1 };
// Asignación dinámica de memoria para texto.
m_rMensaje = new char[ largo + 1 ];
// Copiar el texto a la nueva memoria.
strcpy_s( m_rMensaje, largo + 1, texto );
}
/* La clase consta únicamente de un dato miembro. El dato es privado. Sólo tiene acceso
desde adentro de la clase. El dato es un puntero, m_rMensaje, a una cadena de carac-
teres.
246

La función mostrarMsj() muestra el mensaje con el que se ha creado un objeto de la


clase CMensaje.
La clase tiene un constructor y un destructor. El constructor está completamente
definido, y el destructor está en la forma de prototipo.
El constructor requiere una cadena de caracteres como argumento. Si no se pasa una
cadena, usa un texto por defecto.
El constructor usa la función strlen() de la librería cstring. No se ha codificado
la prevención de falla en la asignación de memoria por el operador new. En el caso
de que ya no haya suficiente memoria disponible.
Para copiar la cadena, se usa la función strcpy_s(), también en la librería cstring.
La función copia la cadena en el espacio de memoria dinámica que se ha asignado por
medio del puntero m_rMensaje. La función copia el texto (tercer argumento) en la
dirección de memoria del primer argumento. El segundo argumento indica el tamaño o
longitud de la cadena a copiar.
El programa usará un espacio de memoria nuevo cada vez que se use. Por lo tanto, si
no se borra la memoria asignada dinámicamente, el espacio asignado irá creciendo
hasta causar problemas de memoria insuficiente.
*/
// Prototipo del destructor de la clase CMensaje.
~CMensaje();
};
CMensaje::~CMensaje()
{
cout << "Se ha llamado al destructor de la clase CMensaje.\n\n";
delete[] m_rMensaje; // Liberar la memoria asignada al puntero.
// El comando delete[] usa los corchetes debido
// a que la memoria está ocupada por una matriz.
}
int main()
{
system("color FD");
cout << endl << endl << endl;
// Declarar un objeto estándar.
247

CMensaje estribillo{ "Una mula es tan buena como una dama." };


// Declarar un objeto dinámico.
CMensaje* rM{ new CMensaje {"Una gata podría verse como una princesa."} };
estribillo.mostrarMsj();
rM->mostrarMsj();
delete rM;
cout << endl << endl << endl;
}

// IH169.cpp: Implementing a copy constructor. Implementar un constructor de copia.


//
// Algunas cosas pueden resultar inesperadas cuando se usa la memoria dinámica.
// Para la clase CMensaje del programa IH168, el constructor de copia por defecto
// es en el fondo inadecuado.
// Supongamos las siguientes instrucciones:
// CMensaje estribillo1{"La vida es una tómbola."};
// CMensaje estribillo2{estribillo1};
// La segunda instrucción llama al constructor copia por defecto. Este constructor
// copia la dirección de memoria que está guardada en el puntero miembro de la clase
// CMensaje, desde el objeto estribillo1 hasta el objeto estribillo2. El constructor
// hace eso porque está programado para copiar el valor de la dirección de memoria
// del objeto original (estribillo1) hasta el nuevo objeto (estribillo2).
// Al final, lo que tendremos es una cadena de caracteres, colocada en memoria, y
// cuya dirección de memoria está alojada en dos punteros, en dos objetos distintos.
// Si la cadena se modifica, desde cualquiera de estos dos punteros, se modifica
// para los dos punteros, y por tanto, para los dos objetos.
// MMM: Si uno de los dos objetos (o punteros) es destruido, la cadena ya no existirá
// más en memoria. El objeto destruido ya no existirá más. Pero el otro objeto
// continuará existiendo, apuntando a una parte de la memoria dinámica, la
// cual ya no posee significado alguno. Eso generará caos en la computadora. El
// puntero estará señalando una cadena de caracteres que no tiene extensión en
// la memoria.
248

// La crisis se resuelve aportando un nuevo constructor copia que reemplace el cons-


// tructor por defecto.
//
#include <iostream>
using namespace std;
class CMensaje
{
private:
char* m_rMensaje; // Puntero a una cadena de texto.
public:
// Definición para mostrar un mensaje.
void mostrarMsj() const
{
cout << m_rMensaje << endl << endl;
}
// Definición del constructor de la clase CMensaje.
CMensaje(const char* texto = "Mensaje por defecto.")
{
size_t largo{ strlen(texto) + 1 };
// Asignación dinámica de memoria para texto.
m_rMensaje = new char[largo + 1];
// Copiar el texto a la nueva memoria.
strcpy_s(m_rMensaje, largo + 1, texto);
}
/* La clase consta únicamente de un dato miembro. El dato es privado. Sólo tiene acceso
desde adentro de la clase. El dato es un puntero, m_rMensaje, a una cadena de carac-
teres.
La función mostrarMsj() muestra el mensaje con el que se ha creado un objeto de la
clase CMensaje.
La clase tiene un constructor y un destructor. El constructor está completamente
definido, y el destructor está en la forma de prototipo.
El constructor requiere una cadena de caracteres como argumento. Si no se pasa una
cadena, usa un texto por defecto.
249

El constructor usa la función strlen() de la librería cstring. No se ha codificado


la prevención de falla en la asignación de memoria por el operador new. En el caso
de que ya no haya suficiente memoria disponible.
Para copiar la cadena, se usa la función strcpy_s(), también en la librería cstring.
La función copia la cadena en el espacio de memoria dinámica que se ha asignado por
medio del puntero m_rMensaje. La función copia el texto (tercer argumento) en la
dirección de memoria del primer argumento. El segundo argumento indica el tamaño o
longitud de la cadena a copiar.
El programa usará un espacio de memoria nuevo cada vez que se use. Por lo tanto, si
no se borra la memoria asignada dinámicamente, el espacio asignado irá creciendo
hasta causar problemas de memoria insuficiente.
*/
// Definición del constructor copia de la clase CMensaje.
CMensaje(const CMensaje& evitarCaos)
{
size_t largo{ strlen(evitarCaos.m_rMensaje) + 1 };
// Asignación dinámica de memoria para texto.
m_rMensaje = new char[largo];
// Copiar el texto a la nueva memoria.
strcpy_s(m_rMensaje, largo, evitarCaos.m_rMensaje);
}
/* MMM: Recuerde que el parámetro de CMensaje debe ser del tipo "const", para evitar que
el compilador sea obligado a llamar infinitas veces al constructor copia.
El constructor copia primero asigna memoria suficiente para alojar la cadena de carac-
teres del texto mensaje (contenida en evitarCaos). Luego, guarda la dirección de
memoria en el puntero m_rMensaje, miembro del nuevo objeto que está siendo creado.
Finalmente, copia la cadena de caracteres desde el objeto original hasta el nuevo
objeto.
El nuevo objeto y el original serán idénticos e independientes.
*/
// Prototipo del destructor de la clase CMensaje.
~CMensaje();
};
250

CMensaje::~CMensaje()
{
cout << "Se ha llamado al destructor de la clase CMensaje.\n\n";
delete[] m_rMensaje; // Liberar la memoria asignada al puntero.
// El comando delete[] usa los corchetes debido
// a que la memoria está ocupada por una matriz.
}
int main()
{
system("color FD");
cout << endl << endl << endl;
// Declarar un objeto estándar.
CMensaje estribillo{ "Una mula es tan terca como una dama." };
// Declarar un objeto dinámico.
CMensaje* rM{ new CMensaje {"Una gata podría verse como una princesa."} };
// Declarar un objeto estándar adicional.
CMensaje estribillo2{ "La vida es una tómbola." };
// Declarar un objeto estándar adicional, copia del objeto anterior.
CMensaje estribillo3{ estribillo2 };
estribillo.mostrarMsj();
estribillo2.mostrarMsj();
estribillo3.mostrarMsj();
rM->mostrarMsj();
delete rM;
cout << endl << endl << endl;
}
251

// IH170.cpp: Implementing a copy constructor. Implementar un constructor de copia. (2).


//
// En el programa IH169, vimos cómo se puede implementar un constructor de copia
// y usarlo para inicializar un objeto segundo a partir de un objeto original.
// El objeto era un mensaje, creado a partir de la clase CMensaje.
// El propósito del programa fue evitar que los dos punteros a los objetos apun-
// taran a la misma dirección de la memoria dinámica.
// De otra forma, el constructor de copia podría generar caos de memoria aunque
// no se copie explícitamente un objeto original en un objeto secundario.
// Consideremos las siguientes instrucciones:
// CMensaje miIdea {"Mis ideas fluyen como la miel."};
// mostrarMsj( miIdea );
// La función mostrarMsj() está definida así:
// void mostrarMsj(CMensaje msjLocal)
// {
// cout << "El mensaje que se transmite es: " << endl << endl;
// msjLocal.mostrarlo();
// }
//
// El problema en la función mostrarMsj() reside en el tipo de parámetro. msjLocal
// es del tipo CMensaje. El argumento en una llamada a la función es pasado por
// valor. Por consiguiente, el compilador crea una copia utilizando al constructor
// de copia.
// El proceso seguido por el constructor de copia es como sigue:
// Primero, el objeto miIdea es creado alojando un espacio suficiente para guardar
// la cadena de caracteres: "Mis ideas fluyen como la miel.". El espacio de memo-
// ria se aplica en la memoria dinámica.
// Segundo, al llamarse la función mostrarMsj(), el constructor de copia crea una
// copia del argumento de la función, msjLocal. El compilador usa un constructor
// por defecto de copia. El puntero a miIdea y el puntero a msjLocal apuntan a la
// misma dirección de memoria, asumiendo cada uno un objeto distinto.
// Tercero, al finalizar la función mostrarMsj(), el ámbito de acción de los objetos
// locales se termina, y el objeto msjLocal desaparece. El compilador llama
252

// automáticamente al destructor por defecto, el cual destruye el objeto local,


// la copia, msjLocal, y libera la memoria del puntero m_rMensaje.
// Cuarto, al retorno de la función mostrarMsj(), el puntero a miIdea aún existe
// y apunta a la dirección de memoria que el destructor acaba de borrar. Si el
// programa vuelve a usar este puntero, el compilador no sabrá qué hacer. Se le
// estará ordenando que utilice una área de memoria indefinida. Tiene un supuesto
// inicio, pero no tiene fin.
// MMM: En conclusión, cualquier llamada a una función que pasa por valor un ob-
// jeto de una clase cuyo miembro ha sido definido en forma dinámica, gene-
// rará graves problemas.
// Si se va a asignar espacio de memoria para un miembro de una clase en
// forma dinámica, siempre se debe declarar un constructor de copia. Al
// mismo tiempo, siempre se debe aplicar un destructor, a menos que se esté
// usando punteros inteligentes.
//
#include <iostream>
using namespace std;
class CMensaje
{
private:
char* m_rMensaje; // Puntero a una cadena de texto.
public:
// Definición para mostrar un mensaje.
void mostrarMsj(CMensaje msjLocal)
{
cout << "El mensaje que se transmite es: " << endl << endl;
msjLocal.mostrarlo();
}
// Definición de la función mostrarlo().
void mostrarlo()
{
cout << m_rMensaje << endl << endl;
}
253

// Definición del constructor de la clase CMensaje.


CMensaje(const char* texto = "Mensaje por defecto.")
{
size_t largo{ strlen(texto) + 1 };
// Asignación dinámica de memoria para texto.
m_rMensaje = new char[largo + 1];
// Copiar el texto a la nueva memoria.
strcpy_s(m_rMensaje, largo + 1, texto);
}
/* La clase consta únicamente de un dato miembro. El dato es privado. Sólo tiene acceso
desde adentro de la clase. El dato es un puntero, m_rMensaje, a una cadena de carac-
teres.
La función mostrarMsj() muestra el mensaje con el que se ha creado un objeto de la
clase CMensaje.
La clase tiene un constructor y un destructor. El constructor está completamente
definido, y el destructor está en la forma de prototipo.
El constructor requiere una cadena de caracteres como argumento. Si no se pasa una
cadena, usa un texto por defecto.
El constructor usa la función strlen() de la librería cstring. No se ha codificado
la prevención de falla en la asignación de memoria por el operador new. En el caso
de que ya no haya suficiente memoria disponible.
Para copiar la cadena, se usa la función strcpy_s(), también en la librería cstring.
La función copia la cadena en el espacio de memoria dinámica que se ha asignado por
medio del puntero m_rMensaje. La función copia el texto (tercer argumento) en la
dirección de memoria del primer argumento. El segundo argumento indica el tamaño o
longitud de la cadena a copiar.
El programa usará un espacio de memoria nuevo cada vez que se use. Por lo tanto, si
no se borra la memoria asignada dinámicamente, el espacio asignado irá creciendo
hasta causar problemas de memoria insuficiente.
*/
// Definición del constructor copia de la clase CMensaje.
CMensaje(const CMensaje& evitarCaos)
{
254

size_t largo{ strlen(evitarCaos.m_rMensaje) + 1 };


// Asignación dinámica de memoria para texto.
m_rMensaje = new char[largo];
// Copiar el texto a la nueva memoria.
strcpy_s(m_rMensaje, largo, evitarCaos.m_rMensaje);
}
/* MMM: Recuerde que el parámetro de CMensaje debe ser del tipo "const", para evitar que
el compilador sea obligado a llamar infinitas veces al constructor copia.
El constructor copia primero asigna memoria suficiente para alojar la cadena de carac-
teres del texto mensaje (contenida en evitarCaos). Luego, guarda la dirección de
memoria en el puntero m_rMensaje, miembro del nuevo objeto que está siendo creado.
Finalmente, copia la cadena de caracteres desde el objeto original hasta el nuevo
objeto.
El nuevo objeto y el original serán idénticos e independientes.
*/
// Prototipo del destructor de la clase CMensaje.
~CMensaje();
};
CMensaje::~CMensaje()
{
cout << "Se ha llamado al destructor de la clase CMensaje.\n\n";
delete[] m_rMensaje; // Liberar la memoria asignada al puntero.
// El comando delete[] usa los corchetes debido
// a que la memoria está ocupada por una matriz.
}
int main()
{
system("color FD");
cout << endl << endl << endl;
// Declarar un objeto estándar.
CMensaje estribillo{ "Una mula es tan terca como una dama." };
// Declarar un objeto dinámico.
CMensaje* rM{ new CMensaje {"Una gata podría verse como una princesa."} };
255

// Declarar un objeto estándar adicional.


CMensaje estribillo2{ "La vida es una tómbola." };
// Declarar un objeto estándar adicional, copia del objeto anterior.
CMensaje estribillo3{ estribillo2 };
estribillo.mostrarMsj(*rM);
estribillo2.mostrarMsj(estribillo);
estribillo3.mostrarMsj(*rM);
rM->mostrarMsj(estribillo3);
delete rM;
cout << endl << endl << endl;
}

// IH171.cpp: Operator overloading. Sobrecarga de operadores.


//
// La sobrecarga de operadores consiste en definir operaciones adicionales a los
// operadores estándares de c++, tales como +, -, /, *, <, >, %, etc. Estas opera-
// ciones adicionales actúan sobre los tipos de datos definidos por el usuario,
// tales como clases, funciones, objetos y otros.
// La sobrecarga de operadores no consiste en crear nuevos operadores, sino en dar
// funciones adicionales a los operadores que ya existen.
// Un ejemplo sería redefinir al operador >, para comparar los volúmenes de dos
// objetos CCaja.
// La precedencia de los operadores sigue igual. No es factible modificarla mediante
// la sobrecarga.
// MMM: Los operadores que no se pueden sobrecargar son:
// ::, ?:, ., sizeof, .*
//
#include <iostream>
using namespace std;
class CCaja
{
public:
256

// Definición del constructor de la clase CCaja.


explicit CCaja(double lng, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición del constructor por defecto de la clase CCaja.
CCaja()
{
cout << "Se ha llamado al constructor por defecto de la clase CCaja.\n";
m_longitud = m_ancho = m_alto = 1.0;
}
// Definición de la función miembro volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Declaración del prototipo de la sobrecarga del operador >.
bool operator>(const CCaja& laCaja) const;
private:
double m_longitud;
double m_ancho;
double m_alto;
};

int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja mtzCajas[5]; // Definición de una matriz de 5 objetos CCaja.
cout << endl << endl;
CCaja cigarrosBox(8.0, 5.3, 2.8);
cout << "El volumen de la caja mtzCajas[2] es "
257

<< mtzCajas[2].volumen() << " cm cúbicos.\n\n"


<< "El volumen de la caja de cigarros es "
<< cigarrosBox.volumen() << "cm cúbicos.\n\n\n";
//-----------------------------------------------------------------------------------------
CCaja boxes2[6]{ CCaja{44.1, 38.9, 28.0}, CCaja{77.3, 69.8, 36.6} };
/* Se crea la matriz de 6 objetos CCaja, de los cuales se inicializan los primeros
dos objetos, boxes2[0] y boxes2[1]. Los demás serán inicializados por el
constructor por defecto.
*/
cout << "\n\n\nEl volumen de la caja boxes2[0] es "
<< boxes2[0].volumen() << " cm cúbicos.\n\n"
<< "El volumen de la caja boxes2[1] es " << boxes2[1].volumen() << " cm cúbicos.\n\n"
<< "El volumen de la caja boxes2[2] es " << boxes2[2].volumen() << " cm cúbicos.\n\n";
// Uso del operador sobrecargado ">", con dos objetos CCaja.
if (boxes2[1] > boxes2[0])
cout << "El volumen de la caja boxes2[1] es mayor que el de la caja boxes2[0].\n\n";
else
cout << "El volumen de la caja boxes2[1] es igual o menor”
<< “ que el de la caja boxes2[0].\n\n";
if (boxes2[1].operator>(cigarrosBox))
cout << "El volumen de la caja boxes2[1] es mayor que el de la caja cigarrosBox.\n\n";
else
cout << "El volumen de la caja boxes2[1] es igual o menor”
<< “ que el de la caja cigarrosBox.\n\n";
cout << endl << endl << endl;
}
// Definición de la sobrecarga del operador >.
bool CCaja::operator>(const CCaja& laCaja) const
{
return this->volumen() > laCaja.volumen();
}
258

// IH172.cpp: Exercising the overloaded 'less than' and equality operators. Practicar los
// operadores sobrecargados para 'menor que' e 'igual que'.
//
// La sobrecarga de operadores es una función de la clase CCaja. Como función, se
// puede declarar el prototipo de la función dentro de la clase CCaja y luego escribir
// la definición por afuera del código de la clase; y se puede escribir toda la definición
// de la función dentro del cuerpo de código de la clase.
// Si escribe la definición por fuera del código de la clase, no se olvide de que está
// obligado a calificar el ámbito de la función como miembro de cierta clase, en este
// caso, de la clase CCaja. CCaja::operator<.
// La sobrecarga de operadores funciona perfectamente con objetos constantes y no
// constantes.
//
#include <iostream>
using namespace std;
// Definición de la clase CCaja, con ámbito de acción global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición de la función volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Declaración del prototipo de la sobrecarga del operador <.
bool operator<(const CCaja& laCaja) const;
// Definición de la sobrecarga del operador ==.
259

bool operator==(const CCaja& laCaja) const


{
return this->volumen() == laCaja.volumen();
}
// Definición del destructor de la clase CCaja.
~CCaja()
{
cout << "Se ha llamado al destructor de la clase CCaja.\n\n";
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Definición de la función de sobrecarga del operador <.
inline bool CCaja::operator<(const CCaja& laCaja) const
{
return this->volumen() < laCaja.volumen();
}
int main()
{
system("color FD");
cout << endl << endl << endl;
const CCaja cajaPeq{ 18.0, 12.0, 6.8 };
const CCaja cajaMed{ 24.2, 14.3, 8.4 };
CCaja cajaGde{ 32.6, 18.3, 12.4 };
CCaja esaCaja{ 10.2, 12.2, 8.2 };
if (cajaPeq < cajaMed)
cout << "La caja pequeña es menor que la caja mediana.\n\n";
if (cajaMed < cajaGde)
cout << "La caja mediana es menor que la caja grande.\n\n";
if (esaCaja == cajaPeq)
cout << "La caja pequeña es igual que la caja esaCaja.\n\n";
260

else
cout << "La caja pequeña no es igual que la caja esaCaja.\n\n";
cout << endl << endl << endl;
}

// IH173.cpp: Implementing full support for comparison operators. Implementar soporte total
// para los operadores de comparación.
//
// En el programa IH172, se definió un operador sobrecargado, del tipo: operator<().
// Este operador compara los volúmenes de dos objetos derivados de la clase CCaja.
// Si deseamos saber si el volumen del objeto en curso es menor que 120 cm cúbicos,
// usando este operador, el programa simplemente no compila.
// if( laCaja < 120.0 )
// 120.0 es un tipo de dato double, no CCaja, ni CCaja*, ni CCaja&.
// Eso se resolvería así:
//
// Función para comparar el volumen de un objeto CCaja con una constante numérica.
// bool CCaja::operator<(const double valor) const
// {
// return this->volumen() < valor;
// }
//
// El operando derecho del operador < corresponde al parámetro de la función: "valor".
// El operando izquierdo, por otro lado, corresponde al objeto CCaja, y será pasado
// a la función como el puntero implícito "this".
// Y si queremos comparar de este otro modo:
// if(110.0 < laCaja)
// Habría que reprogramar la función. Lo cual es engorroso. Pues una función a base
// de un operador miembro de una clase siempre aporta el operando izquierdo como un
// puntero "this".
// La solución pudiera ser una función amiga; pero no es necesario.
// Una función ordinaria resuelve el problema, así:
261

//
// Función que compara a una constante con el volumen de un objeto CCaja.
// inline bool operator<(const double valor, const CCaja& laCaja)
// {
// return valor < laCaja.volumen();
// }
// Como la función miembro volumen() es pública, no genera dificultad de acceso. Si
// la función volumen() no fuese pública, entonces una función amiga podría tener
// acceso directamente al dato privado de la clase y del objeto.
// Todavía se necesitan los comparadores >, >=, <=, !=. En la librería estándar del
// c++ existe un archivo encabezado llamado "utility", el cual contiene un conjunto
// de "templates" para funciones con operadores.
// template <class T> bool operator!=(const T& x, const T& y); // Requiere ==
// template <class T> bool operator>(const T& x, const T& y); // Requiere <
// template <class T> bool operator<=(const T& x, const T& y); // Requiere <
// template <class T> bool operator>=(const T& x, const T& y); // Requiere <
// Estas plantillas crean funciones comparativas con operadores para cualquier
// clase. Para la primera plantilla, es necesario implementar el operator==().
// MMM: Las plantillas están en el espacio de nombres (namespace) std::rel_ops.
// Por lo que debe usarse la directiva:
// using namespace std::rel_ops;
//
#include <iostream>
#include <utility>
using namespace std;
using namespace std::rel_ops; // No debería ser necesario.
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
262

{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición de la función volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función del operador "menor que", la cual compara los volúmenes de
// dos objetos CCaja.
bool operator<(const CCaja& laCaja) const
{
return this->volumen() < laCaja.volumen();
}
// Definición de la función del operador "menor que", la cual compara los volúmenes de
// un objetos CCaja y una constante numérica.
bool operator<(const double valor) const
{
return this->volumen() < valor;
}
// Definición de la función del operador "mayor que", la cual compara los volúmenes de
// un objetos CCaja y una constante numérica.
bool operator>(const double valor) const
{
return this->volumen() > valor;
}
// Definición del operador de igualdad con sobrecarga.
bool operator==(const CCaja& laCaja) const
{
return this->volumen() == laCaja.volumen();
}
// Definición del destructor de la clase CCaja.
~CCaja()
263

{
cout << "Se ha llamado al destructor de la clase CCaja.\n\n";
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Definición de una función con operador para comparar un valor constante con el volumen
// de un objeto CCaja.
inline bool operator<(const double valor, const CCaja& laCaja)
{
return valor < laCaja.volumen();
}
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja cajaPeq{ 4.2, 3.6, 2.0 };
CCaja cajaMed{ 8.3, 6.4, 2.8 };
CCaja otraCj{ 2.7, 5.5, 1.9 };
if (cajaPeq != cajaMed)
cout << "La caja pequeña no es igual a la caja mediana.\n\n";
if (cajaMed > cajaPeq)
cout << "La caja mediana es mayor que la caja pequeña.\n\n";
else
cout << "La caja mediana no es mayor que la caja pequeña.\n\n";
if (otraCj >= cajaPeq)
cout << "La otra caja es mayor que o igual que la caja pequeña.\n\n";
else
cout << "La otra caja es más pequeña que la caja pequeña.\n\n";
if (cajaMed > 70.0)
cout << "La caja mediana es mayor que 70.0 cm cúbicos.\n\n";
264

else
cout << "La caja mediana no es mayor que 70.0 cm cúbicos.\n\n";
if (26.0 < cajaPeq)
cout << "La caja pequeña es mayor que 26.0 cm cúbicos.\n\n";
else
cout << "La caja pequeña es igual o menor que 26.0 cm cúbicos.\n\n";
cout << endl << endl << endl;
}

// IH174.cpp: Incomplete class declaration. Forward declaration of the class type. Declaración
// incompleta de la clase. Declaración adelantada del tipo de la clase.
//
// El compilador procede a crear los códigos necesarios para convertir el programa
// en c++ en un código máquina ejecutable, siguiendo un orden muy estricto. Al tope
// del programa están las directivas del tipo "#include", que le ordenan incluir
// las librerías del c++. Luego, los espacios de nombres, como "std". Después, las
// variables globales y las definiciones de clases. También las funciones
// ordinarias globales. Si la función ordinaria (que no es miembro de una clase) se
// refiere a un objeto de una clase, debe escribirse después de la definición de
// esa clase. Si por razones especiales, se necesita que la función aparezca antes
// de la clase que aún no ha sido definida, se puede recurrir al procedimiento de
// declarar en forma incompleta a dicha clase, luego el prototipo de la función, y
// finalmente las definiciones de la clase y de la función.
//
#include <iostream>
#include <utility>
using namespace std;
using namespace std::rel_ops; // No debería ser necesario.
//----------------------------------------------------------------------------------------
// Definición incompleta de la clase CCaja.
class CCaja;
// Prototipo de la función ordinaria con operador para comparar un valor constante con el
265

// volumen de un objeto CCaja.


inline bool operator<(const double valor, const CCaja& laCaja);
//----------------------------------------------------------------------------------------
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición de la función volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función del operador "menor que", la cual compara los volúmenes de
// dos objetos CCaja.
bool operator<(const CCaja& laCaja) const
{
return this->volumen() < laCaja.volumen();
}
// Definición de la función del operador "menor que", la cual compara los volúmenes de
// un objeto CCaja y una constante numérica.
bool operator<(const double valor) const
{
return this->volumen() < valor;
}
// Definición de la función del operador "mayor que", la cual compara los volúmenes de
// un objeto CCaja y una constante numérica.
bool operator>(const double valor) const
266

{
return this->volumen() > valor;
}
// Definición del operador de igualdad con sobrecarga.
bool operator==(const CCaja& laCaja) const
{
return this->volumen() == laCaja.volumen();
}
// Definición del destructor de la clase CCaja.
~CCaja()
{
cout << "Se ha llamado al destructor de la clase CCaja.\n\n";
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Definición de una función con operador para comparar un valor constante con el volumen
// de un objeto CCaja.
inline bool operator<(const double valor, const CCaja& laCaja)
{
return valor < laCaja.volumen();
}
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja cajaPeq{ 4.2, 3.6, 2.0 };
CCaja cajaMed{ 8.3, 6.4, 2.8 };
CCaja otraCj{ 2.7, 5.5, 1.9 };
if (cajaPeq != cajaMed)
cout << "La caja pequeña no es igual a la caja mediana.\n\n";
267

if (cajaMed > cajaPeq)


cout << "La caja mediana es mayor que la caja pequeña.\n\n";
else
cout << "La caja mediana no es mayor que la caja pequeña.\n\n";
if (otraCj >= cajaPeq)
cout << "La otra caja es mayor que o igual que la caja pequeña.\n\n";
else
cout << "La otra caja es más pequeña que la caja pequeña.\n\n";
if (cajaMed > 70.0)
cout << "La caja mediana es mayor que 70.0 cm cúbicos.\n\n";
else
cout << "La caja mediana no es mayor que 70.0 cm cúbicos.\n\n";
if (26.0 < cajaPeq)
cout << "La caja pequeña es mayor que 26.0 cm cúbicos.\n\n";
else
cout << "La caja pequeña es igual o menor que 26.0 cm cúbicos.\n\n";
cout << endl << endl << endl;
}

// IH175.cpp: Overloading the assignment operator. La sobrecarga del operador de asignación "=".
//
// Cuando el usuario no escribe una función de sobrecarga para el operador de
// asignación, el compilador utilizará una función de sobrecarga por defecto.
// Esta versión del compilador procede a copiar la asignación miembro por miembro.
// El operador de asignación es llamado cuando ambos lados de una asignación son
// objetos del mismo tipo.
// MMM: El constructor de copia por defecto es llamado cuando se define un objeto
// que es inicializado con otro objeto del mismo tipo.
// También será llamado cuando se llama a una función pasándole un objeto por
// valor.
// El operador de asignación por defecto opera eficázmente con la clase CCaja. Sin
// embargo, si la clase ocupa espacio de memoria para miembros que se crearán de
268

// modo dinámico (en la free store), el usuario está obligado a implementar su


// propio operador de asignación. El compilador no está preparado para operar por
// su propia cuenta con la memoria dinámica; por lo tanto, dejar este vacío signi-
// ficará avanzar hacia un gran caos con la memoria dinámica.
// Consideremos ahora el ejemplo de la clase CMensaje. Esta clase la usamos en el
// programa 168. Esta clase tiene un puntero a una cadena de texto, m_rMensaje.
// Supongamos que de esta clase tenemos dos instancias, dos objetos, llamado uno
// "estribillo", y el otro "rM". El primero es un objeto estándar; el segundo se
// ha creado en la free store, en la memoria dinámica.
// Siendo dos instancias del mismo tipo de dato, CMensaje, podemos igualar los
// valores de los objetos. Así, estribillo = rM; le asignará a estribillo la di-
// rección de memoria de rM; y a la inversa. Ambos terminarán con la misma di-
// rección de memoria. Al eliminar uno de los dos, la dirección de memoria que-
// dará sin significado para la computadora. Luego, habrá caos, pues un objeto
// quedará activo, apuntando con el puntero a una dirección de memoria que ahora
// está libre.
// MMM: Lo que se ocupa ahora es programar el operador de asignación de manera
// que copie el texto del objeto dValor a una área de la memoria que le
// pertenezca por separado al objeto iValor.
//
// Siempre implemente una función de operador de asignación si se asigna un
// espacio de memoria dinámica para un dato miembro de una clase.
//
#include <iostream>
#include <cstring> // Para strlen() y strcpy_s().
// Aparentemente no se necesita, pues está en std.
using namespace std;
class CMensaje
{
private:
char* m_rMensaje; // Puntero a una cadena de texto.
public:
// Definición de la función mostrarMsj(), para mostrar un mensaje.
269

void mostrarMsj() const


{
cout << m_rMensaje << endl << endl;
}
//-------------------------------------------------------------------------------
// Definición de la función resetear(). Esta función llena de asteriscos (*)
// la cadena de texto contenida en el mensaje.
void resetear()
{
char* temporal{ m_rMensaje };
while (*temporal)
*(temporal++) = '*';
}
// Sobrecarga del operador de asignación para objetos CMensaje.
CMensaje& operator=(const CMensaje& elMsj)
{
if (this != &elMsj) // Chequear que las dos direcciones no son iguales.
{
// Liberar memoria para el primer operando.
delete[] m_rMensaje;
size_t longitud{ strlen(elMsj.m_rMensaje) + 1 };
m_rMensaje = new char(longitud);
// Copiar la cadena del segundo operando al primer operando.
strcpy_s(this->m_rMensaje, longitud, elMsj.m_rMensaje);
}
return *this; // Devolver una referencia al primer operando.
}
//-------------------------------------------------------------------------------
// Definición del constructor de la clase CMensaje.
CMensaje(const char* texto = "Mensaje por defecto.")
{
size_t largo{ strlen(texto) + 1 };
// Asignación dinámica de memoria para texto.
270

m_rMensaje = new char[largo];


// Copiar el texto a la nueva memoria.
strcpy_s(m_rMensaje, largo, texto);
}
// Definición del constructor copia de la clase CMensaje.
CMensaje(const CMensaje& elMsj)
{
size_t largo{ strlen(elMsj.m_rMensaje) + 1 };
// Asignación dinámica de memoria para texto.
m_rMensaje = new char[largo];
// Copiar el texto a la nueva memoria.
strcpy_s(m_rMensaje, largo, elMsj.m_rMensaje);
}
/* La clase consta únicamente de un dato miembro. El dato es privado. Sólo tiene acceso
desde adentro de la clase. El dato es un puntero, m_rMensaje, a una cadena de carac-
teres.
La función mostrarMsj() muestra el mensaje con el que se ha creado un objeto de la
clase CMensaje.
La clase tiene un constructor y un destructor. El constructor está completamente
definido, y el destructor está en la forma de prototipo.
El constructor requiere una cadena de caracteres como argumento. Si no se pasa una
cadena, usa un texto por defecto.
El constructor usa la función strlen() de la librería cstring. No se ha codificado
la prevención de falla en la asignación de memoria por el operador new. En el caso
de que ya no haya suficiente memoria disponible.
Para copiar la cadena, se usa la función strcpy_s(), también en la librería cstring.
La función copia la cadena en el espacio de memoria dinámica que se ha asignado por
medio del puntero m_rMensaje. La función copia el texto (tercer argumento) en la
dirección de memoria del primer argumento. El segundo argumento indica el tamaño o
longitud de la cadena a copiar.
El programa usará un espacio de memoria nuevo cada vez que se use. Por lo tanto, si
no se borra la memoria asignada dinámicamente, el espacio asignado irá creciendo
hasta causar problemas de memoria insuficiente.
271

*/
// Prototipo del destructor de la clase CMensaje.
~CMensaje();
};
CMensaje::~CMensaje()
{
cout << "Se ha llamado al destructor de la clase CMensaje.\n\n";
delete[] m_rMensaje; // Liberar la memoria asignada al puntero.
// El comando delete[] usa los corchetes
// debido a que la memoria está ocupada por una matriz.
}
int main()
{
system("color FD");
cout << endl << endl << endl;
// Declarar un objeto estándar.
CMensaje estribillo{ "Una mula es tan terca como una dama." };
CMensaje estribillo2;
cout << "El mensaje estribillo2 contiene: \n\n";
estribillo2.mostrarMsj();
cout << "El mensaje estribillo contiene: \n\n";
estribillo.mostrarMsj();
estribillo2 = estribillo; // Usar el operador de asignación sobrecargado.
cout << "El mensaje estribillo2 ahora contiene: \n\n";
estribillo2.mostrarMsj();
// Declarar un objeto dinámico.
CMensaje* rM{ new CMensaje {"Una gata podría verse como una princesa."} };
rM->mostrarMsj();
estribillo.resetear(); // Convertir texto a *.
cout << "El mensaje estribillo reseteado contiene: \n\n";
estribillo.mostrarMsj();
cout << "El mensaje estribillo2 ha quedado así: \n\n";
estribillo2.mostrarMsj();
272

delete rM;
cout << endl << endl << endl;
}

// IH176.cpp: Overloading the addition operator. La sobrecarga del operador de adición "+".
//
// La sobrecarga de operadores nos ofrece muchas sorpresas. Con ella, se puede
// modificar los objetos ya existentes, sin convertirlos en otros objetos
// distintos. Y también se puede crear nuevos objetos.
// Para el operador suma (+), la sobrecarga consistirá en una especie de
// "adición" de características, por la que de dos objetos obtendremos un
// tercero.
// Por ejemplo, la suma de dos objetos CCaja. Podríamos simplemente sumar el
// volumen de las dos cajas. O podríamos crear un tercer objeto, con la
// propiedad de que tenga la facilidad de contener físicamente a las dos cajas
// que son sus argumentos.
// Para poner las dos cajas juntas, vamos a asumir que las queremos una encima
// de la otra.
// Le vamos a poner a la nueva caja resumen el largo y el ancho de mayor
// magnitud en los operandos. Y el alto será la suma de los altos de las dos
// cajas.
// Asimismo, verificaremos en el constructor que la longitud sea mayor, luego
// el ancho y finalmente el alto, en todas las cajas.
// MMM: Dado que la función operator+() tendrá que accesar a los datos miembros
// privados de la clase CCaja, deberá estar como función miembro de la
// clase CCaja.
// La declaración del prototipo de la función operator+() será:
// CCaja operator+(const CCaja& laCaja) const;
// El parámetro de la función es una referencia para evitar que se copie el
// argumento de la derecha cuando se llame a la función.
// La referencia es constante porque la función no modifica al argumento.
// El parámetro debe ser constante para permitir que un objeto constante sea
273

// pasado a la función.
// La función debe ser constante para permitir que el operando izquierdo sea
// constante.
// MMM: El encabezado algorithm, contiene las definiciones de las plantillas
// de las funciones max() y min().
// No se ha programado un destructor para la clase CCaja; así que se dejó el
// destructor por defecto del compilador.
// La plantilla de la función swap() intercambia los valores entre sus dos
// argumentos.
// Se puede hacer una variación con la función del operator+(). Se puede
// declarar como una función amiga (friend function). Eso se probará en el
// siguiente programa (IH177).
//
#include <iostream>
#include <algorithm> // Para usar max(), min() y swap().
#include <utility> // Para la plantilla de los operadores.
using namespace std;
using namespace std::rel_ops;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
// Cálculos iniciales. Asegurarse de que la longitud sea mayor, el ancho medio y el
// alto inferior.
if (m_alto > m_longitud)
{
// Orden incorrecto. Intercambiar (swap) los valores.
swap(m_alto, m_longitud);
274

swap(m_ancho, m_alto);
}
else
if (m_alto > m_ancho)
{
// Orden incorrecto. Intercambiar (swap) los valores.
swap(m_alto, m_ancho);
}
}
// Definición de la función volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función operator<(). Esta función compara los volúmenes.
bool operator<(const CCaja& laCaja) const
{
return this->volumen() < laCaja.volumen();
}
// Definición de la función operator<(). Compara con una constante.
bool operator<(const double valor) const
{
return this->volumen() < valor;
}
// Definición de la función operator>(). Compara con una constante.
bool operator>(const double valor) const
{
return this->volumen() > valor;
}
// Definición de la función sobrecargada operator==(). Esta función compara los
// volúmenes.
bool operator==(const CCaja& laCaja) const
{
275

return this->volumen() == laCaja.volumen();


}
// Definición de la función operator+(). Adiciona dos cajas.
CCaja operator+(const CCaja& laCaja) const
{
// Crear un objeto que sea capaz de contener los volúmenes de las dos cajas.
return CCaja(max(m_longitud, laCaja.m_longitud), max(m_ancho, laCaja.m_ancho),
(m_alto + laCaja.m_alto));
}
// Definición de la función mostrarCaja().
void mostrarCaja() const
{
cout << "\n\nLongitud: " << m_longitud
<< "\nAncho: " << m_ancho
<< "\nAlto: " << m_alto
<< endl << endl;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Definición de la función comparar(). Compara un valor con el volumen.
inline bool operator>(const double valor, const CCaja& laCaja)
{
return valor > laCaja.volumen();
}
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja cajaPeq{ 5.0, 3.0, 2.0 };
CCaja cajaMed{ 8.0, 6.0, 3.0 };
276

CCaja cajaA;
CCaja cajaB;
cout << "Las dimensiones de la caja pequeña son: ";
cajaPeq.mostrarCaja();
cout << "Las dimensiones de la caja mediana son: ";
cajaMed.mostrarCaja();
//----------------------------------------------------------------------------------
cajaA = cajaPeq + cajaMed; // Operadores sobrecargados.
cout << "Las dimensiones de la caja A son: ";
cajaA.mostrarCaja();
cajaB = cajaA + cajaMed;
cout << "Las dimensiones de la caja B son: ";
cajaB.mostrarCaja();
cout << endl << endl << endl;
}

// IH177.cpp: Overloading the addition operator. La sobrecarga del operador de adición "+".
// Friend function. La función amiga.
//
// La sobrecarga de operadores nos ofrece muchas sorpresas. Con ella, se puede
// modificar los objetos ya existentes, sin convertirlos en otros objetos distintos.
// Y también se puede crear nuevos objetos.
// Para el operador suma (+), la sobrecarga consistirá en una especie de "adición" de
// características, por la que de dos objetos obtendremos un tercero.
// Por ejemplo, la suma de dos objetos CCaja. Podríamos simplemente sumar el volumen
// de las dos cajas. O podríamos crear un tercer objeto, con la propiedad de que tenga la
// facilidad de contener físicamente a las dos cajas que son sus argumentos.
// Para poner las dos cajas juntas, vamos a asumir que las queremos una encima de la
// otra.
// Le vamos a poner a la nueva caja resumen el largo y el ancho de mayor magnitud en
// los operandos. Y el alto será la suma de los altos de las dos cajas.
// Asimismo, verificaremos en el constructor que la longitud sea mayor, luego el ancho
277

// y finalmente el alto, en todas las cajas.


// MMM: Dado que la función operator+() tendrá que accesar a los datos miembros
// privados de la clase CCaja, deberá estar como función miembro de la clase
// CCaja.
// La declaración del prototipo de la función operator+() será:
// CCaja operator+(const CCaja& laCaja) const;
// El parámetro de la función es una referencia para evitar que se copie el argumento
// de la derecha cuando se llame a la función.
// La referencia es constante porque la función no modifica al argumento.
// El parámetro debe ser constante para permitir que un objeto constante sea pasado a
// la función.
// La función debe ser constante para permitir que el operando izquierdo sea constante.
// MMM: El encabezado algorithm, contiene las definiciones de las plantillas de las
// funciones max() y min().
// No se ha programado un destructor para la clase CCaja; así que se dejó el destructor
// por defecto del compilador.
// La plantilla de la función swap() intercambia los valores entre sus dos argumentos.
// Se puede hacer una variación con la función del operator+(). Se puede declarar como
// una función amiga (friend function). Eso se probará en este programa (IH177).
//
#include <iostream>
#include <algorithm> // Para usar max(), min() y swap().
#include <utility> // Para la plantilla de los operadores.
using namespace std;
using namespace std::rel_ops;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng = 1.0, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
278

cout << "Se ha llamado al constructor de la clase CCaja.\n\n";


// Cálculos iniciales. Asegurarse de que la longitud sea mayor, el ancho medio y el
// alto inferior.
if (m_alto > m_longitud)
{
// Orden incorrecto. Intercambiar (swap) los valores.
swap(m_alto, m_longitud);
swap(m_ancho, m_alto);
}
else
if (m_alto > m_ancho)
{
// Orden incorrecto. Intercambiar (swap) los valores.
swap(m_alto, m_ancho);
}
}
// Definición de la función volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función operator<(). Esta función compara los volúmenes.
bool operator<(const CCaja& laCaja) const
{
return this->volumen() < laCaja.volumen();
}
// Definición de la función operator<(). Compara con una constante.
bool operator<(const double valor) const
{
return this->volumen() < valor;
}
// Definición de la función operator>(). Compara con una constante.
bool operator>(const double valor) const
279

{
return this->volumen() > valor;
}
// Definición de la función sobrecargada operator==(). Esta función compara los
// volúmenes.
bool operator==(const CCaja& laCaja) const
{
return this->volumen() == laCaja.volumen();
}
/*
// Definición de la función operator+(). Adiciona dos cajas.
CCaja operator+(const CCaja& laCaja) const
{
// Crear un objeto que sea capaz de contener los volúmenes de las dos cajas.
return CCaja(max(m_longitud, laCaja.m_longitud), max(m_ancho, laCaja.m_ancho),
(m_alto + laCaja.m_alto));
}
*/
// Definición de la función mostrarCaja().
void mostrarCaja() const
{
cout << "\n\nLongitud: " << m_longitud
<< "\nAncho: " << m_ancho
<< "\nAlto: " << m_alto
<< endl << endl;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
// Declaración del prototipo de la función amiga de la clase, operator+().
// Esta función usará los operadores de acceso directo en los dos argumentos.
// Así: laCajaA.m_longitud, etc.
280

friend CCaja operator+(const CCaja& laCajaA, const CCaja& laCajaB);


};
// Definición de la función amiga de la clase,
CCaja operator+(const CCaja& laCajaA, const CCaja& laCajaB)
{
// Crear un objeto que sea capaz de contener los volúmenes de las dos cajas.
return CCaja(max(laCajaA.m_longitud, laCajaB.m_longitud),
max(laCajaA.m_ancho, laCajaB.m_ancho), (laCajaA.m_alto + laCajaB.m_alto));
}
// Definición de la función operator>(). Compara un valor con el volumen.
inline bool operator>(const double valor, const CCaja& laCaja)
{
return valor > laCaja.volumen();
}
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja cajaPeq{ 5.0, 3.0, 2.0 };
CCaja cajaMed{ 8.0, 6.0, 3.0 };
CCaja cajaA;
CCaja cajaB;
cout << "Las dimensiones de la caja pequeña son: ";
cajaPeq.mostrarCaja();
cout << "Las dimensiones de la caja mediana son: ";
cajaMed.mostrarCaja();
//----------------------------------------------------------------------------------
cajaA = cajaPeq + cajaMed; // Operadores sobrecargados.
cout << "Las dimensiones de la caja A son: ";
cajaA.mostrarCaja();
cajaB = cajaA + cajaMed;
cout << "Las dimensiones de la caja B son: ";
cajaB.mostrarCaja();
281

cout << endl << endl << endl;


}

// IH178.cpp: Overloading the increment and decrement operators. La sobrecarga de los


// operadores de incremento y decremento.
//
// Los operadores de incremento (++) y decremento (--) son operadores unarios.
// Estos operadores funcionan a base de un prefijo o de un postfijo. O sea, el
// parámetro que modifican puede aparecer antes del operador o después del ope-
// rador.
//
#include <iostream>
using namespace std;
// Definición de la clase CLongitud.
class CLongitud
{
public:
// Definición del constructor de la clase CLongitud.
explicit CLongitud(const double lng = 1.0) : m_longitud{ lng }
{
cout << "Se ha llamado al constructor de la clase CLongitud.\n\n";
}
// Prototipo de la función miembro prefijo operador de incremento,
// operator++().
CLongitud& operator++();
// Prototipo de la función miembro postfijo operador de incremento,
// operator++(int). El parámetro "int" se usa sólo como diferenciador
// de las funciones prefijo y postfijo.
const CLongitud operator++(int);
// Prototipo de la función miembro prefijo operador de decremento,
// operator--().
CLongitud& operator--();
282

// Prototipo de la función miembro postfijo operador de decremento,


// operator--(int). El parámetro "int" se usa sólo como diferenciador
// de las funciones prefijo y postfijo.
const CLongitud operator--(int);
// Definición de la función mostrarLng().
void mostrarLng() const
{
cout << "La longitud es: " << m_longitud << endl << endl;
}
private:
double m_longitud;
};
// Definición de la función operator++(). El operador actúa antes de que se use
// el valor de m_longitud en una expresión. Es un operador prefijo. El operador
// incrementa (o decrementa) el valor de m_longitud, y después se retorna a la
// función el nuevo valor.
inline CLongitud& CLongitud::operator++()
{
++(this->m_longitud);
return *this;
}
// Definición de la función operator++(int). Es un operador postfijo. El operador
// actúa después de que se use el valor de m_longitud en una expresión. Para hacer
// esto, primero se copia el valor inicial de m_longitud; luego se modifica el
// valor en esa misma dirección de memoria y se retorna el valor anterior. El
// valor de retorno de la función se declara constante, para evitar el uso de ex-
// presiones más allá de lo esperado; por ejemplo, laCaja++++. Esta expresión sí
// modificaría el objeto laCaja.
inline const CLongitud CLongitud::operator++(int)
{
CLongitud longitud{*this};
++*this;
return longitud;
283

}
// Definición de la función operator--(). El operador actúa antes de que se use
// el valor de m_longitud en una expresión. Es un operador prefijo. El operador
// incrementa (o decrementa) el valor de m_longitud, y después se retorna a la
// función el nuevo valor.
inline CLongitud& CLongitud::operator--()
{
--(this->m_longitud);
return *this;
}
// Definición de la función operator--(int). Es un operador postfijo. El operador
// actúa después de que se use el valor de m_longitud en una expresión. Para hacer
// esto, primero se copia el valor inicial de m_longitud; luego se modifica el
// valor en esa misma dirección de memoria y se retorna el valor anterior. El
// valor de retorno de la función se declara constante, para evitar el uso de ex-
// presiones más allá de lo esperado; por ejemplo, laCaja----. Esta expresión sí
// modificaría el objeto laCaja.
inline const CLongitud CLongitud::operator--(int)
{
CLongitud longitud{ *this };
--* this;
return longitud;
}
int main()
{
system("color FD");
cout << endl << endl << endl;
CLongitud miLng{ 88.8 };
cout << "El objeto miLng (0) tiene el valor: ";
miLng.mostrarLng();
//---------------------------------------------------------------------------
// Aumentar el valor de miLng prefijo.
miLng++;
284

cout << "El objeto miLng (++) prefijo (1) tiene el valor: ";
miLng.mostrarLng();
// Aumentar el valor de miLng postfijo.
++miLng;
cout << "El objeto miLng (++) postfijo (2) tiene el valor: ";
miLng.mostrarLng();
//---------------------------------------------------------------------------
// Disminuir el valor de miLng prefijo.
miLng--;
cout << "El objeto miLng (--) prefijo (3) tiene el valor: ";
miLng.mostrarLng();
// Disminuir el valor de miLng postfijo.
--miLng;
cout << "El objeto miLng (--) postfijo (4) tiene el valor: ";
miLng.mostrarLng();
cout << endl << endl << endl;
}

// IH179.cpp: Overloading the function call operator. La sobrecarga del operador de llamada
// a función "()".
//
// El operador de llamada a una función es "()". El operador sobrecargado será
// operator()().
// A un objeto de una clase que sobrecarga el operador de llamada a función se
// le llama: función objeto, función-objeto, o functor. Un objeto de este tipo
// se puede utilizar como si fuese una función real.
// Usaremos una clase denominada CArea, la cual tendrá programada la sobrecarga
// del operador de llamada a función, operator()().
// La función de sobrecarga ejecutará la siguiente tarea: calculará el producto
// de los dos argumentos numéricos dobles de la función.
// Un functor se puede utilizar para pasar una función como un argumento a otra
// función, lo que hace más simple y más fácil la operación, que si se usara un
285

// puntero a una función. Vea imprArea().


//
#include <iostream>
using namespace std;
// Definición de la clase CArea.
class CArea
{
public:
double operator()(double m_longitud, double m_ancho) { return m_longitud * m_ancho; };
// Definición de la función imprArea(). Esta función tiene tres argumentos. Los dos
// primeros son la longitud y el ancho de una superficie. El tercero es una llamada
// al constructor por defecto de la clase CArea, para crear un objeto de ese tipo,
// que sea usado en la misma función para calcular el área de la superficie.
void imprArea(double m_longitud, double m_ancho, CArea& laArea) const
{
cout << "El área del objeto es: " << laArea(m_longitud, m_ancho) << endl << endl;
}
/* Estas clases no necesitan datos miembros.
private:
double m_longitud;
double m_ancho;
*/
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CArea miArea; // Crear una función objeto.
double campoLng{ 100 }; // Largo de un campo.
double campoAnc{ 60 }; // Ancho de un campo.
// Crear el objeto areaDelCampo, inicializado mediante la función objeto
// miArea(), aprovechando la sobrecarga del operator()().
double areaDelCampo{ miArea(campoLng, campoAnc) };
286

cout << "El largo del campo es: " << campoLng << endl << endl;
cout << "El ancho del campo es: " << campoAnc << endl << endl;
cout << "El área del campo es: " << areaDelCampo << endl << endl;
// Usar la función imprArea(). No resulta el tercer argumento como CArea().
// La función imprArea() no es reconocida si no se especifica el objeto en
// el que aparece; no se puede usar la clase CArea aquí como objeto; tampoco
// se puede usar miArea() en el tercer argumento, es decir como función ob-
// jeto.
miArea.imprArea(15.0, 23.0, miArea);
cout << endl << endl << endl;
}

// IH180.cpp: The object copying problem. Avoiding unnecessary copy operations. El problema de
// copiar objetos. Evitar las operaciones innecesarias de copiado.
//
// Al realizar un paso por valor en los parámetros de una función, implícitamente
// siempre se activa el proceso de copiado por el compilador. El diseño del
// compilador ha considerado por defecto la copia de los tipos fundamentales de
// datos, y con ellos no se da ningún problema. Empero, cuando se trata de pasar
// objetos por valor, el compilador hace lo mismo, y copia por defecto al objeto.
// En algunos objetos, eso puede significar serias dificultades.
// Si el objeto es muy voluminoso, la copia será una labor de mucho consumo de
// recursos.
// Si tiene miembros declarados en la memoria dinámica, se podría complicar.
// El constructor de copia realiza la copia de estos objetos.
// El operador de asignación también implica la copia por defecto de objetos.
// Existen circunstancias en las cuales la copia de objetos es innecesaria.
// MMM: Los parámetros de referencia del tipo dValor son muy útiles para evitar la
// copia de objetos pasados por valor.
// En este programa falta agregar al operador de adición la concatenación de
// cadenas de caracteres del tipo string, para que no esté limitado a las cadenas
// de tipo char*.
287

//
#include <iostream>
#include <cstring>
using namespace std;
// Definición de la clase CMensaje.
class CMensaje
{
public:
// Definición de la función mostrarMsj().
void mostrarMsj() const
{
cout << m_rMensaje << endl << endl;
}
// Definición de la función sobrecargada del operador de adición. Se usa la función
// *.mostrarMsj() varias veces para demostrar que la función de sobrecarga funciona
// adecuadamente.
CMensaje& operator+(const CMensaje& elMsj) const
{
cout << "Se ha llamado a la función del operador de adición.\n\n";
size_t longitud { strlen(this->m_rMensaje) + strlen(elMsj.m_rMensaje) + 1 };
cout << "La longitud en el operador de adición es: " << longitud << "\n\n";
CMensaje miMsj;
miMsj.mostrarMsj(); // Mensaje por defecto.
miMsj.m_rMensaje = new char[longitud];
strcpy_s(miMsj.m_rMensaje, longitud, this->m_rMensaje);
miMsj.mostrarMsj(); // Mensaje del operando 1.
strcat_s(miMsj.m_rMensaje, longitud, elMsj.m_rMensaje);
miMsj.mostrarMsj(); // Mensaje del operando 1 + 2.
// MMM: Aquí hay un inconveniente. La función retorna la dirección de memoria de
// un objeto derivado. Este objeto no ha sido destruido.
return miMsj;
}
// Definición de la función de sobrecarga del operador de asignación, para los
288

// objetos CMensaje. Esta función no puede ser constante porque anula la acción
// del puntero m_rMensaje.
// Sobrecarga del operador de asignación para objetos CMensaje.
CMensaje& operator=(const CMensaje& elMsj)
{
if (this != &elMsj) // Chequear que las dos direcciones no son iguales.
{
// Liberar memoria para el primer operando.
delete[] m_rMensaje;
size_t longitud{ strlen(elMsj.m_rMensaje) + 1 };
m_rMensaje = new char(longitud);
// Copiar la cadena del segundo operando al primer operando.
strcpy_s(this->m_rMensaje, longitud, elMsj.m_rMensaje);
}
return *this; // Devolver una referencia al primer operando.
}
//-------------------------------------------------------------------------------
// Definición del constructor de la clase CMensaje.
CMensaje(const char* texto = "Mensaje por defecto.\n\n")
{
cout << "Se ha llamado al constructor de la clase CMensaje.\n\n";
size_t longitud{ strlen(texto) + 1 };
m_rMensaje = new char[longitud];
strcpy_s(m_rMensaje, longitud, texto);
}
// Definición del constructor de copia de la clase CMensaje.
CMensaje(const CMensaje& elMsj)
{
cout << "Se ha llamado al constructor de copia de la clase CMensaje.\n\n";
size_t longitud{ strlen(elMsj.m_rMensaje) + 1 };
m_rMensaje = new char[longitud];
strcpy_s(m_rMensaje, longitud, elMsj.m_rMensaje);
}
289

// Definición del destructor de la clase CMensaje. Se liberará la memoria asignada


// por el operador new.
~CMensaje()
{
cout << "Se ha llamado al destructor de la clase CMensaje.\n\n";
delete[] m_rMensaje;
}
private:
char* m_rMensaje; // Puntero char* a un objeto string.
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CMensaje estribillo1{ "Una gata se puede convertir en una dama. " };
CMensaje estribillo2{ "De noche, todos los gatos son pardos. " };
CMensaje estribillo3; // Mensaje vacío. Usará el mensaje por defecto.
cout << "estribillo3 contiene por defecto:\n\n";
estribillo3.mostrarMsj();
cout << "Los 2 mensajes: estribillo3 = estribillo1 + estribillo2.\n\n";
estribillo3 = estribillo1 + estribillo2;
cout << "estribillo3 contiene ahora:\n\n";
estribillo3.mostrarMsj();
cout << "Los 3 mensajes: estribillo3 = estribillo3 + estribillo1 + estribillo2.\n\n";
estribillo3 = estribillo3 + estribillo1 + estribillo2;
cout << "estribillo3 contiene ahora:\n\n";
estribillo3.mostrarMsj();
cout << endl << endl << endl;
}
290

// IH181.cpp: Applying rvalue reference parameters. La aplicación de parámetros de referencia


// del tipo dValor.
//
// Al realizar un paso por valor en los parámetros de una función, implícitamente
// siempre se activa el proceso de copiado por el compilador. El diseño del
// compilador ha considerado por defecto la copia de los tipos fundamentales de
// datos, y con ellos no se da ningún problema. Empero, cuando se trata de pasar
// objetos por valor, el compilador hace lo mismo, y copia por defecto al objeto.
// En algunos objetos, eso puede significar serias dificultades.
// Si el objeto es muy voluminoso, la copia será una labor de mucho consumo de
// recursos.
// Si tiene miembros declarados en la memoria dinámica, se podría complicar.
// El constructor de copia realiza la copia de estos objetos.
// El operador de asignación también implica la copia por defecto de objetos.
// Existen circunstancias en las cuales la copia de objetos es innecesaria.
// MMM: Los parámetros de referencia del tipo dValor son muy útiles para evitar
// la copia de objetos pasados por valor.
// En este programa falta agregar al operador de adición la concatenación de
// cadenas de caracteres del tipo string, para que no esté limitado a las cadenas
// de tipo char*.
// Si el objeto fuente del tipo CMensaje se trata de un objeto de carácter
// temporal, el objeto será copiado e inmediatamente después será destruido por
// el compilador. Eso se convierte en una complicación. Para evitarla, es mejor
// proceder directamente con la memoria en la que está alojado el objeto temporal,
// y pasar su ubicación a la dirección del objeto destino. Esto sobrepasa la
// necesidad de copiarlo, y a la vez, evita su destrucción por el compilador.
// No se necesitará alojar memoria aparte para el objeto destino.
// Siempre el objeto fuente será destruido por el compilador, sin riesgo alguno.
// MMM: La clave para lograr esta operación consiste en saber determinar cuando el
// objeto en una transacción de copia es un dValor. Esta determinación se
// alcanza
// utilizando un parámetro de referencia hacia un dValor.
#include <iostream>
291

#include <cstring>
using namespace std;
// Definición de la clase CMensaje.
class CMensaje
{
public:
// Definición de la función mostrarMsj().
void mostrarMsj() const
{
cout << m_rMensaje << endl << endl;
}
// Definición de la función sobrecargada del operador de adición. Se usa la función
// *.mostrarMsj() varias veces para demostrar que la función de sobrecarga funciona
// adecuadamente.
CMensaje& operator+(const CMensaje& elMsj) const
{
cout << "Se ha llamado a la función del operador de adición.\n\n";
size_t longitud{ strlen(this->m_rMensaje) + strlen(elMsj.m_rMensaje) + 1 };
cout << "La longitud en el operador de adición es: " << longitud << "\n\n";
CMensaje miMsj;
miMsj.mostrarMsj(); // Mensaje por defecto.
miMsj.m_rMensaje = new char[longitud];
strcpy_s(miMsj.m_rMensaje, longitud, this->m_rMensaje);
miMsj.mostrarMsj(); // Mensaje del operando 1.
strcat_s(miMsj.m_rMensaje, longitud, elMsj.m_rMensaje);
miMsj.mostrarMsj(); // Mensaje del operando 1 + 2.
// MMM: Aquí hay un inconveniente. La función retorna la dirección de memoria de
// un objeto derivado. Este objeto no ha sido destruido.
return miMsj;
}
// Definición de la función de sobrecarga del operador de asignación, para los
// objetos CMensaje. Esta función no puede ser constante porque anula la acción
// del puntero m_rMensaje. Esta función se usará para los casos en los que el
292

// operando de la derecha tenga el carácter de un iValor. Y cuando sea del tipo


// dValor, se usará la función de sobrecarga siguiente.
// Sobrecarga del operador de asignación para objetos CMensaje.
CMensaje& operator=(const CMensaje& elMsj)
{
if (this != &elMsj) // Chequear que las dos direcciones no son iguales.
{
// Liberar memoria para el primer operando.
delete[] m_rMensaje;
size_t longitud{ strlen(elMsj.m_rMensaje) + 1 };
m_rMensaje = new char(longitud);
// Copiar la cadena del segundo operando al primer operando.
strcpy_s(this->m_rMensaje, longitud, elMsj.m_rMensaje);
}
// Devolver una referencia al primer operando.
return *this;
}
// Definición de la función de sobrecarga del operador de asignación, para los
// objetos CMensaje. Esta función no puede ser constante porque anula la acción
// del puntero m_rMensaje. Ahora se usará un parámetro de referencia a dValor.
// Sobrecarga del operador de asignación para objetos CMensaje.
CMensaje& operator=(CMensaje&& elMsj)
{
cout << "Se ha llamado a la función de traslado del operador de asignación.\n\n";
// if (this != &elMsj) // Chequear que las dos direcciones no son iguales.
// {
// Liberar memoria para el operando de la izquierda en la asignación.
delete[] m_rMensaje;
// size_t longitud{ strlen(elMsj.m_rMensaje) + 1 };
// m_rMensaje = new char(longitud);
m_rMensaje = elMsj.m_rMensaje; // Trasladar la dirección de memoria.
elMsj.m_rMensaje = nullptr; // Pasar el puntero antiguo a nulo.
// Copiar la cadena del segundo operando al primer operando.
293

// strcpy_s(this->m_rMensaje, longitud, elMsj.m_rMensaje);


// }
// Devolver una referencia al primer operando.
return *this;
}
//-------------------------------------------------------------------------------
// Definición del constructor de la clase CMensaje.
CMensaje(const char* texto = "Mensaje por defecto.\n\n")
{
cout << "Se ha llamado al constructor de la clase CMensaje.\n\n";
size_t longitud{ strlen(texto) + 1 };
m_rMensaje = new char[longitud];
strcpy_s(m_rMensaje, longitud, texto);
}
// Definición del constructor de copia de la clase CMensaje. Este constructor
// funciona con un operando de la derecha del tipo iValor. Para un operando
// del tipo dValor, se usará la siguiente definición.
CMensaje(const CMensaje& elMsj)
{
cout << "Se ha llamado al constructor de copia de la clase CMensaje.\n\n";
size_t longitud{ strlen(elMsj.m_rMensaje) + 1 };
m_rMensaje = new char[longitud];
strcpy_s(m_rMensaje, longitud, elMsj.m_rMensaje);
}
// Definición del constructor de copia de la clase CMensaje. Este constructor
// funciona con un operando de la derecha del tipo dValor. Para un operando
// del tipo iValor, se usará la anterior definición.
// MMM: Un constructor de copia con un parámetro de referencia del tipo dValor
// se conoce como "constructor de movimiento", o "constructor de traslado".
CMensaje(CMensaje&& elMsj)
{
cout << "Se ha llamado al constructor de traslado de la clase CMensaje.\n\n";
// size_t longitud{ strlen(elMsj.m_rMensaje) + 1 };
294

m_rMensaje = elMsj.m_rMensaje;
// strcpy_s(m_rMensaje, longitud, elMsj.m_rMensaje);
elMsj.m_rMensaje = nullptr;
}
// Definición del destructor de la clase CMensaje. Se liberará la memoria asignada
// por el operador new.
~CMensaje()
{
cout << "Se ha llamado al destructor de la clase CMensaje.\n\n";
delete[] m_rMensaje;
}
private:
char* m_rMensaje; // Puntero char* a un objeto string.
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CMensaje estribillo1{ "Una gata se puede convertir en una dama. " };
CMensaje estribillo2{ "De noche, todos los gatos son pardos. " };
CMensaje estribillo3; // Mensaje vacío. Usará el mensaje por defecto.
cout << "estribillo3 contiene por defecto:\n\n";
estribillo3.mostrarMsj();
cout << "Los 2 mensajes: estribillo3 = estribillo1 + estribillo2.\n\n";
estribillo3 = estribillo1 + estribillo2;
cout << "estribillo3 contiene ahora:\n\n";
estribillo3.mostrarMsj();
cout << "Los 3 mensajes: estribillo3 = estribillo3 + estribillo1 + estribillo2.\n\n";
estribillo3 = estribillo3 + estribillo1 + estribillo2;
cout << "estribillo3 contiene ahora:\n\n";
estribillo3.mostrarMsj();
cout << endl << endl << endl;
}
295

// IH182.cpp: Named objects are lvalues. Los objetos que son nombrados constituyen iValores.
// Con errores.
//
// Cualquier expresión a la que se ha nombrado o designado como un tipo de dato
// es un iValor.
// Así ocurre con el parámetro "elMsj", que se declara en las funciones de sobre-
// carga de operadores de los programas IH180 y IH181.
//
#include <iostream>
#include <cstring>
#include <utility>
#include "CTEXTO.H"
using namespace std;
// Definición incompleta de la clase CTexto.
// class CTexto;
// No funciona.
// Definición de la clase CMensaje, con ámbito global.
class CMensaje
{
private:
// Crear el objeto m_Texto, del tipo CTexto. No funciona bien.
CTexto m_Texto;
public:
// Definición de la función mostrarMsj().
void mostrarMsj() const
{
m_Texto.mostrarTxt();
}
// Definición de la función de sobrecarga del operator+().
CMensaje operator+(const CMensaje& elMsj) const
{
cout << "Se ha llamado a la función de sobrecarga del operator+().\n\n";
CMensaje miMsj;
296

miMsj.m_Texto = m_Texto + elMsj.m_Texto;


return miMsj;
}
// Definición del operador de copia para la asignación de objetos CMensaje.
CMensaje& operator=(const CMensaje& elMsj)
{
cout << "Se ha llamado a la función del operador de copia de asignación.\n\n";
// Verificar que las direcciones de memoria no coincidan.
if (this != &elMsj)
{
m_Texto = elMsj.m_Texto;
}
// Devolver la referencia al primer operando de la llamada a la función.
return *this;
}
// Definición del operador de asignación para el traslado de objetos CMensaje.
CMensaje& operator=(CMensaje&& elMsj) noexcept
{
cout << "Se ha llamado a la función del operador de asignación para el traslado de objetos.\n\n";
m_Texto = move(elMsj.m_Texto);
return *this;
}
// Definición del constructor de la clase CMensaje.
CMensaje(const char* cadena = "Mensaje por defecto.")
{
cout << "Se ha llamado al constructor de la clase CMensaje.\n\n";
m_Texto = CTexto(cadena);
}
// Definición del constructor de copia de la clase CMensaje.
CMensaje(const CMensaje& elMsj)
{
cout << "Se ha llamado al constructor de copia de la clase CMensaje.\n\n";
m_Texto = elMsj.m_Texto;
297

}
// Definición del constructor de traslado de objetos de la clase CMensaje.
CMensaje(CMensaje&& elMsj) noexcept
{
cout << "Se ha llamado al constructor de traslado de objetos CMensaje.\n\n";
m_Texto = move(elMsj.m_Texto);
}
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CMensaje estribillo1{ "Una gata puede llegar a verse como una dama. " };
CMensaje estribillo2{ "De noche, todos los gatos son pardos.\n\n" };
cout << "La expresión estribillo3{estribillo1 + estribillo2} es:\n\n";
CMensaje estribillo3{ estribillo1 + estribillo2 };
estribillo3.mostrarMsj();
CMensaje estribillo4;
cout << "La expresión estribillo4 = estribillo3 + estribillo2 es:\n\n";
estribillo4 = estribillo3 + estribillo2;
estribillo4.mostrarMsj();
cout << endl << endl << endl;
}

// IH183.cpp: Class templates. Defining a class template. Las plantillas de las clases.
// La definición de la plantilla de una clase.
//
// Una plantilla de una clase no es una clase completa. Es una especie de receta
// para que el compilador construya una cierta clase.
// En la plantilla, para definir el tipo de dato, o tipo de objeto, de la clase,
// se utiliza un par de signos <>, dentro de los cuales se designa el tipo de dato.
// Generalmente se usa un tipo genérico, denominado "template" o "plantilla”,
298

// el cual se abrevia a la letra mayúscula T.


// Así se genera una clase muy particular denominada "una instancia" o "una es-
// pecialización" de la plantilla.
// MMM: Al crear una clase verdadera a partir de la plantilla de una clase, de-
// cimos que estamos "instanciando la plantilla de la clase".
// Las plantillas de funciones y de las clases pueden tener varios parámetros.
// El parámetro T es un parámetro para el cual el usuario hace la sustitución
// con un "tipo" de dato. No hay restricciones en cuanto al tipo de dato o de
// objeto que el usuario puede utilizar.
// El tipo T puede ser: int, double, una Clase CCaja, una Clase CMensaje, una
// Clase CTexto.
// Desde un simple Template de una clase, se pueden generar tantas instancia-
// ciones de la plantilla como se quiera.
// MMM: El uso primordial de las plantillas es en la creación de librerías de
// clases, con las que se puede elaborar colecciones inmensas de objetos.
// A una plantilla de clase de este tipo se le llama "clase contenedora".
// La palabra clave "template" especifica que se está definiendo un tipo de
// dato nuevo, una plantilla.
// A la letra T mayúscula le puede preceder la palabra "class" o la palabra
// "typename".
// En el ejemplo, el compilador creará una definición para la clase CMuestras
// <T> cuando se defina un objeto de este tipo.
// Siempre que aparezca la letra T en la definición de la plantilla, el compilador
// la reemplazará por el tipo de dato o de objeto que el usuario determine en la
// declaración del objeto. El compilador creará una definición de clase que coincida
// con el tipo del argumento.
// MMM: Se puede especificar cualquier tipo de dato para T. Una vez hecho eso,
// la plantilla se basará en el tipo declarado.
// Una clase tipo que se use para instanciar una plantilla debe contener
// todos los operadores que las funciones miembros utilicen para actuar
// sobre los objetos.
// Si la clase no tiene definido el operator>(), éste no funcionará con
// la plantilla de la clase.
299

#include <iostream>
using namespace std;
template <class T>
class CMuestras
{
public:
// Definición del constructor de la clase CMuestras. La clase tiene un argumento
// tipo T[].
CMuestras(const T valores[], int cuenta)
{
m_Siguiente = cuenta < maXmuestras ? cuenta : maXmuestras;
for (int i{}; i < m_Siguiente; i++)
m_Valores[i] = valores[i];
}
// Definición del constructor de la clase CMuestras, para aceptar un valor.
CMuestras(const T& valor)
{
m_Valores[0] = valor;
m_Siguiente = 1;
}
// Definición del constructor por defecto de la clase CMuestras.
CMuestras() = default;
// Definición de la función para agregar una muestra.
bool agregar(const T& valor)
{
// Probar si hay espacio disponible.
bool MM{ m_Siguiente < maXmuestras };
if (MM)
m_Valores[m_Siguiente++] = valor;
return MM;
}
// Definición de la función para obtener el valor máximo de una muestra.
T maX() const
300

{
// Iniciar con la primera muestra como el máXimo.
T elMaX{ m_Valores[0] };
// Verificar las demás muestras.
for (int i{ 1 }; i < m_Siguiente; i++)
if (m_Valores[i] > elMaX)
elMaX = m_Valores[i];
return elMaX;
}
private:
// Establecer el máximo número de muestras.
static const size_t maXmuestras{ 200 };
// Matriz para almacenar las muestras.
T m_Valores[maXmuestras];
// Valor índice del siguiente espacio disponible.
int m_Siguiente{};
};
int main()
{
system("color FD");
cout << endl << endl << endl;
// Creación de la matriz de edades. Valores enteros.
CMuestras<int> misEdades[6]{ 8 };
// Agregar un dato a la muestra de edades.
misEdades->agregar(12);
cout << "El valor más alto de la muestra misEdades es: "
<< misEdades->maX();
cout << endl << endl << endl;
}
301

// IH184.cpp: Template member functions. Las funciones miembros de la plantilla de la clase.


//
// Una plantilla de una clase no es una clase completa. Es una especie de receta
// para que el compilador construya una cierta clase.
// En la plantilla, para definir el tipo de dato, o tipo de objeto, de la clase,
// se utiliza un par de signos <>, dentro de los cuales se designa el tipo de dato.
// Generalmente se usa un tipo genérico, denominado "template" o "plantilla”,
// el cual se abrevia a la letra mayúscula T.
// Así se genera una clase muy particular denominada "una instancia" o "una es-
// pecialización" de la plantilla.
// MMM: Al crear una clase verdadera a partir de la plantilla de una clase, de-
// cimos que estamos "instanciando la plantilla de la clase".
// Las plantillas de funciones y de las clases pueden tener varios parámetros.
// El parámetro T es un parámetro para el cual el usuario hace la sustitución
// con un "tipo" de dato. No hay restricciones en cuanto al tipo de dato o de
// objeto que el usuario puede utilizar.
// El tipo T puede ser: int, double, una Clase CCaja, una Clase CMensaje, una
// Clase CTexto.
// Desde un simple Template de una clase, se pueden generar tantas instancia-
// ciones de la plantilla como se quiera.
// MMM: El uso primordial de las plantillas es en la creación de librerías de
// clases, con las que se puede elaborar colecciones inmensas de objetos.
// A una plantilla de clase de este tipo se le llama "clase contenedora".
// La palabra clave "template" especifica que se está definiendo un tipo de dato
// nuevo, una plantilla.
// A la letra T mayúscula le puede preceder la palabra "class" o la palabra
// "typename".
// En el ejemplo, el compilador creará una definición para la clase CMuestras
// <T> cuando se defina un objeto de este tipo.
// Siempre que aparezca la letra T en la definición de la plantilla, el compilador
// la reemplazará por el tipo de dato o de objeto que el usuario determine en la
// declaración del objeto. El compilador creará una definición de clase que
// coincida con el tipo del argumento.
302

// MMM: Se puede especificar cualquier tipo de dato para T. Una vez hecho eso,
// la plantilla se basará en el tipo declarado.
// Una clase tipo que se use para instanciar una plantilla debe contener
// todos los operadores que las funciones miembros utilicen para actuar
// sobre los objetos.
// Si la clase no tiene definido el operator>(), éste no funcionará con
// la plantilla de la clase.
//
// Se puede poner la definición de una función miembro de la plantilla de una
// clase por fuera de la declaración de la plantilla.
// Al hacerlo, la definición de la función debe aparecer en el archivo de ca-
// becera (*.h) que contiene la definición de la plantilla, y no en un archivo
// *.cpp separado.
// La plantilla de una función miembro de la plantilla de una clase debe tener
// los mismos tipos de parámetros de la definición de la plantilla de la clase.
// Si la plantilla de la clase tiene tres tipos de parámetros, esos mismos tipos
// de parámetros deberá tener cualquier plantilla de función miembro.
//
#include <iostream>
using namespace std;
template <class T>
class CMuestras
{
public:
// Definición del constructor de la clase CMuestras. La clase tiene un argumento
// tipo T[].
CMuestras(const T valores[], int cuenta)
{
m_Siguiente = cuenta < maXmuestras ? cuenta : maXmuestras;
for (int i{}; i < m_Siguiente; i++)
m_Valores[i] = valores[i];
}
// Definición del constructor de la clase CMuestras, para aceptar un valor.
303

CMuestras(const T& valor)


{
m_Valores[0] = valor;
m_Siguiente = 1;
}
// Definición del constructor por defecto de la clase CMuestras.
CMuestras() = default;
// Definición de la función para agregar una muestra.
bool agregar(const T& valor)
{
// Probar si hay espacio disponible.
bool MM{ m_Siguiente < maXmuestras };
if (MM)
m_Valores[m_Siguiente++] = valor;
return MM;
}
// Definición de la función para obtener el valor máximo de una muestra.
T maX() const;
private:
// Establecer el máximo número de muestras.
static const size_t maXmuestras{ 200 };
// Matriz para almacenar las muestras.
T m_Valores[maXmuestras];
// Valor índice del siguiente espacio disponible.
int m_Siguiente{};
};
// Definición de la función para obtener el valor máximo de una muestra.
template<typename T>
T CMuestras<T>::maX() const
{
// Iniciar con la primera muestra como el máXimo.
T elMaX{ m_Valores[0] };
// Verificar las demás muestras.
304

for (int i{ 1 }; i < m_Siguiente; i++)


if (m_Valores[i] > elMaX)
elMaX = m_Valores[i];
return elMaX;
}
int main()
{
system("color FD");
cout << endl << endl << endl;
// Creación de la matriz de edades. Valores enteros.
CMuestras<int> misEdades[6]{ 8 };
// Agregar un dato a la muestra de edades.
misEdades->agregar(12);
cout << "El valor más alto de la muestra misEdades es: "
<< misEdades->maX();
cout << endl << endl << endl;
}

// IH185.cpp: To define a constructor or a destructor outside of a class template. Definir un


// constructor o un destructor por fuera de la plantilla de la clase.
//
// Una plantilla de una clase no es una clase completa. Es una especie de receta
// para que el compilador construya una cierta clase.
// En la plantilla, para definir el tipo de dato, o tipo de objeto, de la clase,
// se utiliza un par de signos <>, dentro de los cuales se designa el tipo de
// dato. Generalmente se usa un tipo genérico, denominado "template" o "plantilla”,
// el cual se abrevia a la letra mayúscula T.
// Así se genera una clase muy particular denominada "una instancia" o "una es-
// pecialización" de la plantilla.
// MMM: Al crear una clase verdadera a partir de la plantilla de una clase, de-
// cimos que estamos "instanciando la plantilla de la clase".
// Las plantillas de funciones y de las clases pueden tener varios parámetros.
305

// El parámetro T es un parámetro para el cual el usuario hace la sustitución


// con un "tipo" de dato. No hay restricciones en cuanto al tipo de dato o de
// objeto que el usuario puede utilizar.
// El tipo T puede ser: int, double, una Clase CCaja, una Clase CMensaje, una
// Clase CTexto.
// Desde un simple Template de una clase, se pueden generar tantas instancia-
// ciones de la plantilla como se quiera.
// MMM: El uso primordial de las plantillas es en la creación de librerías de
// clases, con las que se puede elaborar colecciones inmensas de objetos.
// A una plantilla de clase de este tipo se le llama "clase contenedora".
// La palabra clave "template" especifica que se está definiendo un tipo de
// dato nuevo, una plantilla.
// A la letra T mayúscula le puede preceder la palabra "class" o la palabra
// "typename".
// En el ejemplo, el compilador creará una definición para la clase CMuestras
// <T> cuando se defina un objeto de este tipo.
// Siempre que aparezca la letra T en la definición de la plantilla, el compilador
// la reemplazará por el tipo de dato o de objeto que el usuario determine en la
// declaración del objeto. El compilador creará una definición de clase que
// coincida con el tipo del argumento.
// MMM: Se puede especificar cualquier tipo de dato para T. Una vez hecho eso,
// la plantilla se basará en el tipo declarado.
// Una clase tipo que se use para instanciar una plantilla debe contener
// todos los operadores que las funciones miembros utilicen para actuar
// sobre los objetos.
// Si la clase no tiene definido el operator>(), éste no funcionará con la
// plantilla de la clase.
//
// Se puede poner la definición de una función miembro de la plantilla de una
// clase por fuera de la declaración de la plantilla.
// Al hacerlo, la definición de la función debe aparecer en el archivo de ca-
// becera (*.h) que contiene la definición de la plantilla, y no en un archivo
// *.cpp separado.
306

// La plantilla de una función miembro de la plantilla de una clase debe tener


// los mismos tipos de parámetros de la definición de la plantilla de la clase.
// Si la plantilla de la clase tiene tres tipos de parámetros, esos mismos tipos
// de parámetros deberá tener cualquier plantilla de función miembro.
//
#include <iostream>
using namespace std;
template <class T>
class CMuestras
{
public:
// Definición del constructor de la clase CMuestras. La clase tiene un argumento
// tipo T[].
CMuestras(const T valores[], int cuenta);
// Definición del constructor de la clase CMuestras, para aceptar un valor.
CMuestras(const T& valor)
{
m_Valores[0] = valor;
m_Siguiente = 1;
}
// Definición del constructor por defecto de la clase CMuestras.
CMuestras() = default;
// Definición de la función para agregar una muestra.
bool agregar(const T& valor)
{
// Probar si hay espacio disponible.
bool MM{ m_Siguiente < maXmuestras };
if (MM)
m_Valores[m_Siguiente++] = valor;
return MM;
}
// Definición de la función para obtener el valor máximo de una muestra.
T maX() const;
307

private:
// Establecer el máximo número de muestras.
static const size_t maXmuestras{ 200 };
// Matriz para almacenar las muestras.
T m_Valores[maXmuestras];
// Valor índice del siguiente espacio disponible.
int m_Siguiente{};
};
// Definición de la función para obtener el valor máximo de una muestra.
template<typename T>
T CMuestras<T>::maX() const
{
// Iniciar con la primera muestra como el máXimo.
T elMaX{ m_Valores[0] };
// Verificar las demás muestras.
for (int i{ 1 }; i < m_Siguiente; i++)
if (m_Valores[i] > elMaX)
elMaX = m_Valores[i];
return elMaX;
}
// Definición del constructor de la clase CMuestras. La clase tiene un argumento
// tipo T[]. El constructor no requiere la T separada inicial, como en el caso
// de la función maX().
template<typename T>
CMuestras<T>::CMuestras(const T valores[], int cuenta)
{
m_Siguiente = cuenta < maXmuestras ? cuenta : maXmuestras;
for (int i{}; i < m_Siguiente; i++)
m_Valores[i] = valores[i];
}
int main()
{
system("color FD");
308

cout << endl << endl << endl;


// Creación de la matriz de edades. Valores enteros.
CMuestras<int> misEdades[6]{ 8 };
// Agregar un dato a la muestra de edades.
misEdades->agregar(12);
cout << "El valor más alto de la muestra misEdades es: "
<< misEdades->maX();
cout << endl << endl << endl;
}

// IH186.cpp: Creating objects from a class template. La creación de objetos a partir de la


// plantilla de una clase.
//
// Una plantilla de una clase no es una clase completa. Es una especie de receta
// para que el compilador construya una cierta clase.
// En la plantilla, para definir el tipo de dato, o tipo de objeto, de la clase,
// se utiliza un par de signos <>, dentro de los cuales se designa el tipo de
// dato. Generalmente se usa un tipo genérico, denominado "template" o "plantilla”,
// el cual se abrevia a la letra mayúscula T.
// Así se genera una clase muy particular denominada "una instancia" o "una es-
// pecialización" de la plantilla.
// MMM: Al crear una clase verdadera a partir de la plantilla de una clase, de-
// cimos que estamos "instanciando la plantilla de la clase".
// Las plantillas de funciones y de las clases pueden tener varios parámetros.
// El parámetro T es un parámetro para el cual el usuario hace la sustitución
// con un "tipo" de dato. No hay restricciones en cuanto al tipo de dato o de
// objeto que el usuario puede utilizar.
// El tipo T puede ser: int, double, una Clase CCaja, una Clase CMensaje, una
// Clase CTexto.
// Desde un simple Template de una clase, se pueden generar tantas instancia-
// ciones de la plantilla como se quiera.
// MMM: El uso primordial de las plantillas es en la creación de librerías de
309

// clases, con las que se puede elaborar colecciones inmensas de objetos.


// A una plantilla de clase de este tipo se le llama "clase contenedora".
// La palabra clave "template" especifica que se está definiendo un tipo de dato
// nuevo, una plantilla.
// A la letra T mayúscula le puede preceder la palabra "class" o la palabra
// "typename".
// En el ejemplo, el compilador creará una definición para la clase CMuestras
// <T> cuando se defina un objeto de este tipo.
// Siempre que aparezca la letra T en la definición de la plantilla, el compilador
// la reemplazará por el tipo de dato o de objeto que el usuario de termine en la
// declaración del objeto. El compilador creará una definición de clase que
// coincida con el tipo del argumento.
// MMM: Se puede especificar cualquier tipo de dato para T. Una vez hecho eso,
// la plantilla se basará en el tipo declarado.
// Una clase tipo que se use para instanciar una plantilla debe contener
// todos los operadores que las funciones miembros utilicen para actuar
// sobre los objetos.
// Si la clase no tiene definido el operator>(), éste no funcionará con la
// plantilla de la clase.
//
// Se puede poner la definición de una función miembro de la plantilla de una
// clase por fuera de la declaración de la plantilla.
// Al hacerlo, la definición de la función debe aparecer en el archivo de ca-
// becera (*.h) que contiene la definición de la plantilla, y no en un archivo
// *.cpp separado.
// La plantilla de una función miembro de la plantilla de una clase debe tener
// los mismos tipos de parámetros de la definición de la plantilla de la clase.
// Si la plantilla de la clase tiene tres tipos de parámetros, esos mismos tipos
// de parámetros deberá tener cualquier plantilla de función miembro.
//
#include <iostream>
using namespace std;
template <class T>
310

class CMuestras
{
public:
// Definición del constructor de la clase CMuestras. La clase tiene un argumento
// tipo T[].
CMuestras(const T valores[], int cuenta);
// Definición del constructor de la clase CMuestras, para aceptar un valor.
CMuestras(const T& valor)
{
m_Valores[0] = valor;
m_Siguiente = 1;
}
// Definición del constructor por defecto de la clase CMuestras.
CMuestras() = default;
// Definición de la función para agregar una muestra.
bool agregar(const T& valor)
{
// Probar si hay espacio disponible.
bool MM{ m_Siguiente < maXmuestras };
if (MM)
m_Valores[m_Siguiente++] = valor;
return MM;
}
// Definición de la función para obtener el valor máximo de una muestra.
T maX() const;
private:
// Establecer el máximo número de muestras.
static const size_t maXmuestras{ 200 };
// Matriz para almacenar las muestras.
T m_Valores[maXmuestras];
// Valor índice del siguiente espacio disponible.
int m_Siguiente{};
};
311

// Definición de la función para obtener el valor máximo de una muestra.


template<typename T>
T CMuestras<T>::maX() const
{
// Iniciar con la primera muestra como el máXimo.
T elMaX{ m_Valores[0] };
// Verificar las demás muestras.
for (int i{ 1 }; i < m_Siguiente; i++)
if (m_Valores[i] > elMaX)
elMaX = m_Valores[i];
return elMaX;
}
// Definición del constructor de la clase CMuestras. La clase tiene un argumento
// tipo T[]. El constructor no requiere la T separada inicial, como en el caso
// de la función maX().
template<typename T>
CMuestras<T>::CMuestras(const T valores[], int cuenta)
{
m_Siguiente = cuenta < maXmuestras ? cuenta : maXmuestras;
for (int i{}; i < m_Siguiente; i++)
m_Valores[i] = valores[i];
}
int main()
{
system("color FD");
cout << endl << endl << endl;
CMuestras<double> misIngresos[]{ 5.8 };
misIngresos->agregar(7.4);
misIngresos->agregar(9.1);
cout << "El valor más alto de mis ingresos es: "
<< misIngresos->maX();
cout << endl << endl << endl;
}
312

// IH187.cpp: Class templates with multiple parameters. Las plantillas de clases con múltiples
// parámetros.
//
// En las plantillas de clases es factible tanto usar un solo parámetro como usar
// muchos parámetros.
// Los tipos de datos de los datos miembros de la clase CEjemplo (m_valor y
// m_valor2) estarán determinados por los tipos de datos que se aporten al momento
// de crear una instancia de la plantilla.
// Además, se puede crear una instancia de la plantilla de una clase sin definir un
// objeto específico, dando únicamente los datos iniciales de los tipos de datos que
// contiene.
//
#include <iostream>
using namespace std;
// Definición de la plantilla de una clase con dos parámetros.
template<typename T1, typename T2>
class CEjemplo
{
private:
// Datos miembros.
T1 m_valor{};
T2 m_valor2{};
public:
// Cuerpo de código de la clase.
};
// Instanciación e inicialización de la plantilla de la clase CEjemplo.
// Aquí, los tipos de datos que se aplicarán son: int para m_valor y double
// para m_valor2.
template class CEjemplo<int, double>;
int main()
{
system("color FD");
std::cout << endl << endl << endl;
313

CEjemplo<int, double> miEj[4]{};


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

// IH188.cpp: Parameters that require constants or constant expressions to be substituted


// in the class definition. Parámetros que requieren constantes o expresiones
// constantes que serán sustituidas en la definición de la clase.
//
// En las plantillas de la clase CMuestras que hemos definido hasta ahora, dimos
// un tamaño definido de la matriz de muestras, establecido en 200.
// Ahora podemos permitir que sea el usuario quien seleccione el tamaño de la
// matriz.
// MMM: Una plantilla que permite una constante como parámetro, da lugar a ob-
// jetos distintos cuando el parámetro constante es distinto en los objetos.
// No se puede comparar una matriz de 10 elementos con una matriz de 30
// elementos. Las matrices tienen que ser de la misma dimensión.
//
#include <iostream>
using namespace std;
// Definición de la plantilla de la clase CMuestras. Al crearse una instancia de la
// clase, debe darse como parámetro el tamaño que queremos de la matriz.
template <typename T, size_t TAM> class CMuestras
{
private:
T m_valores[TAM];
int m_Siguiente{};
public:
// Definición del constructor de la clase CMuestras.
CMuestras(const T valores[], int cuenta)
{
// Para no excederse en el tamaño.
m_Siguiente = cuenta < TAM ? cuenta : TAM;
314

for (int i{}; i < m_Siguiente; i++)


m_valores[i] = valores[i]; // Guardar el número de muestras.
}
// Definición del constructor de la clase CMuestras, para aceptar una muestra simple.
CMuestras(const T& valor)
{
m_valores[0] = valor;
m_Siguiente = 1;
}
// Definición del constructor por defecto de la clase CMuestras.
CMuestras() = default;
// Definición de la función agregar(). Esta función agrega una muestra a la matriz.
int agregar(const T& valor)
{
int MM{ m_Siguiente < TAM }; // Indica que hay espacio disponible.
if (MM)
// Si MM es verdadero, guardar el dato.
m_valores[m_Siguiente++] = valor;
return MM;
}
// Definición de la función maX(). Obtiene la muestra más grande.
T maX() const
{
// Fijar la primera muestra como máxima, para iniciar comparaciones.
T laMaX{ m_valores[0] };
// Chequear todas las demás.
for (int i{ 1 }; i < m_Siguiente; i++)
if (m_valores[i] > laMaX)
// Conservar el valor más alto.
laMaX = m_valores[i];
return laMaX;
}
};
315

// Definición de la clase CCaja, con ámbito global.


class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición del constructor por defecto de la clase CCaja.
CCaja()
{
cout << "Se ha llamado al constructor por defecto de la clase CCaja.\n";
m_longitud = m_ancho = m_alto = 1.0;
}
// Definición de la función miembro volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja cajas[4]; // "cajas" tendrá 4 valores del objeto "CCaja".
316

// Crear el objeto "misCajas", a partir de la clase CCaja, en el formato


// de la plantilla de la clase CMuestras. En el segundo argumento de
// la plantilla, decimos que el objeto tendrá 4 elementos.
CMuestras<CCaja, 4> misCajas{ cajas, _countof(cajas) };
// Crear un objeto similar, y que el compilador detecte la cantidad de
// elementos que se han asignado al objeto "cajas".
CMuestras<CCaja, _countof(cajas)> misCajas2{ cajas, _countof(cajas) };
cout << endl << endl << endl;
}

// IH189.cpp: Templates for function objects. Las plantillas para funciones objetos. (1)
//
// Típicamente, se define una clase mediante una plantilla cuando la clase se
// dedica a la definición de funciones objeto. La explicación es que esta clase
// permite definir todo tipo de funciones objeto, es decir, funciones que serán
// capaces de procesar datos miembros de cualquier tipo (según se defina en
// cada función objeto).
//
#include <iostream>
using namespace std;
// Definición de la plantilla de la clase CArea.
template<typename T> class CArea
{
public:
T operator() (const T longitud, const T ancho) { return longitud * ancho; }
// Definición de la plantilla de la función objeto imprimirArea().
template<typename T> void imprimirArea(const T longitud, const T ancho, CArea<T> elArea)
{
cout << "El área es: " << elArea(longitud, ancho);
}
};
317

int main()
{
system("color FD");
cout << endl << endl << endl;
CArea<double> miArea;
miArea.imprimirArea(3.3, 2.4, CArea<double>());
cout << endl << endl;
miArea.imprimirArea(6, 4, CArea<int>());
cout << endl << endl << endl;
}

// IH190.cpp: Templates for function objects. Las plantillas para funciones objetos. (2)
//
// Típicamente, se define una clase mediante una plantilla cuando la clase se
// dedica a la definición de funciones objeto. La explicación es que esta clase
// permite definir todo tipo de funciones objeto, es decir, funciones que serán
// capaces de procesar datos miembros de cualquier tipo (según se defina en
// cada función objeto).
//
#include <iostream>
using namespace std;
// Definición de la plantilla de la clase CArea.
template<typename T> class CArea
{
public:
T operator() (const T longitud, const T ancho) { return longitud * ancho; }
// Definición de la plantilla de la función objeto imprimirArea(). Se cambia
// la definición que se usó en el programa anterior IH189, con el propósito
// de reducir los argumentos a únicamente dos.
template<typename T> void imprimirArea(const T longitud, const T ancho,
CArea<T> elArea = CArea<T>())
{
318

cout << "El área es: " << elArea(longitud, ancho);


}
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CArea<double> miArea;
// Nuevo llamado a la función objeto imprimirArea(), con solo dos parámetros.
miArea.imprimirArea(3.3, 2.4);
cout << endl << endl;
miArea.imprimirArea(6, 4);
cout << endl << endl << endl;
}

// IH191.cpp: Perfect forwarding concept. El concepto del adelanto perfecto.


//
// El concepto del adelanto perfecto es útil sólo en el contexto de la
// plantilla de una clase o de una función.
// Este concepto es aplicable cuando se tiene objetos muy voluminosos
// que tardarán mucho en copiarse o crearse.
// Supongamos que tenemos una función func01(), parametrizada por un
// tipo de dato clase T. Esta función podría provenir de una plantilla
// de función o estar ubicada dentro de la plantilla de una clase.
// Supóngase además que la función func01() se ha definido con un pa-
// rámetro de referencia dValor, tipo T&&.
// Sabemos que la función func01() se puede llamar con un argumento
// iValor o dValor, o una referencia iValor. Un iValor o una referencia
// iValor como argumento provoca que la instancia de la plantilla de
// func01() tenga un parámetro de referencia iValor. De otro modo, ten-
// drá un parámetro de referencia dValor.
// Más aún, supongamos que la función func01() llama a la función
319

// func02(). Y ésta tiene dos versiones: una que tiene el parámetro de


// referencia iValor, y otra que tiene el parámetro de referencia dValor.
// La func01() pasa el argumento que recibe a la func02(), también como
// argumento. Se desea que cuando la func01() reciba un argumento del
// tipo dValor, la func01() llame a la versión de func02() que tiene como
// argumento el parámetro de referencia dValor. De esta manera, no
// habrá ni traslado ni copia del argumento.
// Asimismo, cuando se llame a func01() con un argumento iValor, o con
// un parámetro de referencia iValor, se desea que llame a la versión de
// func02() que tiene el parámetro de referencia iValor.
// A todo lo anterior se le denomina "adelanto perfecto". El adelanto
// perfecto maximiza la eficiencia.
// Si la func02() tiene un argumento dValor, y func01() recibe un pará-
// metro de referencia iValor, será preciso convertir el argumento de
// func01() a un argumento dValor. Así no existirá ni traslado ni copia
// del argumento.
// Cuando el argumento de func01() es iValor o parámetro de referencia
// iValor, y el de func02() es iValor, se dejará tal como está.
// En la librería estándar std, existe una función forward() que hace
// precisamente esto, y es una plantilla de función.
// La plantilla está definida en el encabezado "utility".
// Esta función devuelve un dValor si se le pasa un argumento de refe-
// rencia dValor.
// Si se le pasa una referencia iValor, devuelve una referencia iValor.
// MMM: Se puede mover un argumento de referencia dValor gracias a que
// la clase "string" admite la semántica de traslado de objetos.
// En este programa, la plantilla de la clase CPersona nos permite
// crear un objeto CPersona a partir de dos argumentos, donde cada uno
// de los argumentos puede ser: a) un objeto string temporal, b) un
// objeto string iValor, c) una cadena string temporal, terminada en
// carácter nulo, d) una cadena string iValor, terminada en carácter
// nulo.
//
320

#include <iostream>
#include <string>
#include <utility>
using namespace std;
// Definición de la clase CNombre, con ámbito global. Esta clase debe definirse
// antes de la clase CPersona, pues en ella se usa como tipo de dato.
class CNombre
{
public:
// Definición del constructor de la clase CNombre, iValor.
CNombre(const string& elNombre) : m_nombre{ elNombre }
{
cout << "Se ha llamado al constructor de la clase CNombre, iValor.\n\n";
}
// Definición del constructor de la clase CNombre, dValor.
CNombre(string&& elNombre) : m_nombre{ move(elNombre) }
{
cout << "Se ha llamado al constructor de la clase CNombre, dValor.\n\n";
}
// Prototipo de la función accNombre().
const string& accNombre() const { return m_nombre; }
private:
string m_nombre;
};
// Definición de la clase CPersona, con ámbito global.
class CPersona
{
public:
// Definición de la plantilla del constructor de la clase CPersona.
// El comentario de la cuarta línea se puede desactivar y pasarlo a la tercera
// línea para dejar sin efecto el adelanto perfecto. Note que al hacerlo, el
// último caso de llamada de la función continúa igual; esto es debido a que
// un literal es siempre un dValor, que no es susceptible de modificación.
321

// El que sí es susceptible de cambio es el iValor.


template<typename T1, typename T2>
CPersona(T1&& primer, T2&& segundo) :
m_primerNombre {forward<T1>(primer)},
m_segundoNombre {forward<T2>(segundo)}{}
// m_primerNombre{primer}, m_segundoNombre{segundo}{}
// Definición de la función accNombre().
string accNombre() const
{
return m_primerNombre.accNombre() + ' ' + m_segundoNombre.accNombre();
}
private:
CNombre m_primerNombre;
CNombre m_segundoNombre;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
cout << "Se procede a la creación de la CPersona{string{\"Juan\"},"
<< "string{\"Rivas\"}} - con argumentos dValor:";
cout << endl << endl;
CPersona yo{ string {"Juan"}, string {"Rivas"} };
cout << "La persona es: " << yo.accNombre();
//-------------------------------------------------------------------------
string primero{ "Francisco" };
string segundo{ "Rosales" };
cout << endl << endl << endl;
cout << "Se procede a la creación de la CPersona{primero, segundo}"
<< " - con argumentos iValor:";
cout << endl << endl;
CPersona tu{ primero, segundo };
cout << "La persona es: " << tu.accNombre();
322

//-------------------------------------------------------------------------
cout << endl << endl << endl;
cout << "Se procede a la creación de la CPersona{primero, string {\"Flores\"}}"
<< " - con argumentos iValor, dValor:";
cout << endl << endl;
CPersona paisa{ primero, string {"Flores"} };
cout << "La persona es: " << paisa.accNombre();
//-------------------------------------------------------------------------
cout << endl << endl << endl;
cout << "Se procede a la creación de la CPersona{string {\"Manuel\"},"
<< "string {\"Flores\"}} - con argumentos dValor const char*:";
cout << endl << endl;
CPersona icaro{ "Manuel", "Flores" };
cout << "La persona es: " << icaro.accNombre();
cout << endl << endl << endl;
}

// IH192.cpp: Default arguments for template parameters. Default function template arguments.
// Los argumentos por defecto para los parámetros de las plantillas. Los argumentos
// por defecto de las plantillas de funciones. Desviación estándar, media.
//
// Los parámetros por defecto se pueden incluir en las plantillas de clases y de
// funciones.
// Es muy probable que en su mayoría se usen en las plantillas de clases.
// El compilador es muy eficiente en deducir los parámetros por defecto de las
// plantillas de funciones.
// Un parámetro por defecto de la plantilla de una función podría resultar útil
// especificarlo para proveer flexibilidad en el tipo de dato de retorno de la
// función, dado que el compilador no tenga claro cómo deducir el tipo de dato.
//
// En este programa, la plantilla de la función "sigma()" (σ), calcula la des-
// viación estándar de una matriz de datos numéricos estadísticos. La desvia-
323

// ción de los datos individuales se calcula con respecto a un único valor:


// la media o promedio de todos los datos.
// La desviación estándar es una medida acumulada de cuánto varían los datos
// alrededor de la media. Un valor pequeño indica que los datos están muy cer-
// canos a la media; y un valor alto señala que los datos se alejan mucho del
// valor promedio.
// Otra manera de decirlo es considerando la dispersión de los datos. Un valor
// pequeño indica que los datos están muy agrupados, cercanos unos de los
// otros. Un valor alto señala una alta dispersión de los datos; muy alejados
// los unos de los otros.
//
// El tipo de valor de retorno está determinado por el segundo parámetro de la
// plantilla de la función, R, al que se asignó por defecto el tipo "double".
//
#include <iostream>
#include <cmath> // Archivo de encabezado opcional.
using namespace std;
// Definición de la plantilla de la función sigma(). Esta función se
// aplica a los datos estadísticos de una población.
template<typename T, typename R = double>
R sigma(T valores[], size_t cuenta)
{
double media{}; // Media de la población.
for (size_t i{}; i < cuenta; i++)
media += valores[i];
media /= cuenta;
double desv_std{}; // Desviación estándar. Es tipo R.
// Es la suma de los cuadrados.
for (size_t i{}; i < cuenta; i++)
// Es la suma de los cuadrados.
desv_std += pow(valores[i] - media, 2);
// Es la desviación estándar real.
return static_cast<R>(sqrt(desv_std / cuenta));
324

}
int main()
{
system("color FD");
cout << endl << endl << endl;
// Una colección cualquiera de datos numéricos.
int alturas[]{ 38, 97, 45, 72, 51, 88, 36, 29 };
// Aplicación de la función sigma.
cout << "La desviación estándar de las alturas es: " << sigma(alturas, _countof(alturas));
/* Note que en la llamada no se usa el especificador del tipo de dato. Los datos de la
matriz alturas[] son números enteros, y el compilador usa por defecto el tipo
double, que está definido en la plantilla. typename R = double. Así que el compilador
procede a hacer una conversión predefinida, de tipo menor a tipo mayor. De entero
a double.
*/
cout << endl << endl << endl;
// Otra aplicación de la función sigma; esta vez definiendo el tipo de datos. Los datos
// originales de la matriz son números enteros, así que es más consistente publicar el
// promedio como un número entero. El número no se aproxima.
cout << "La desviación estándar de las alturas es: "
<< sigma<int, int>(alturas, _countof(alturas));
cout << endl << endl << endl;
// Otra aplicación de la función sigma; esta vez definiendo dos tipos de datos. Los datos
// originales de la matriz son números enteros, así que es más consistente publicar el
// promedio como un número entero. El número se publicará como flotante, para demostrar
// la flexibilidad de la plantilla.
cout << "La desviación estándar de las alturas es: "
<< sigma<int, float>(alturas, _countof(alturas));
cout << endl << endl << endl;
}
325

// IH193.cpp: Default arguments for template parameters. Default function template arguments.
// New data return type. Los argumentos por defecto para los parámetros de las
// plantillas. Los argumentos por defecto de las plantillas de funciones.
// Nuevo tipo de retorno de los datos. Desviación estándar, media.
//
// Los parámetros por defecto se pueden incluir en las plantillas de clases y de
// funciones.
// Es muy probable que en su mayoría se usen en las plantillas de clases.
// El compilador es muy eficiente en deducir los parámetros por defecto de las
// plantillas de funciones.
// Un parámetro por defecto de la plantilla de una función podría resultar útil
// especificarlo para proveer flexibilidad en el tipo de dato de retorno de la
// función, dado que el compilador no tenga claro cómo deducir el tipo de dato.
//
// En este programa, la plantilla de la función "sigma()" (σ), calcula la des-
// viación estándar de una matriz de datos numéricos estadísticos. La desvia-
// ción de los datos individuales se calcula con respecto a un único valor:
// la media o promedio de todos los datos.
// La desviación estándar es una medida acumulada de cuánto varían los datos
// alrededor de la media. Un valor pequeño indica que los datos están muy cer-
// canos a la media; y un valor alto señala que los datos se alejan mucho del
// valor promedio.
// Otra manera de decirlo es considerando la dispersión de los datos. Un valor
// pequeño indica que los datos están muy agrupados, cercanos unos de los
// otros. Un valor alto señala una alta dispersión de los datos; muy alejados
// los unos de los otros.
//
// El tipo de valor de retorno está determinado por el segundo parámetro de la
// plantilla de la función, R, al que se asignó por defecto el tipo "double".
//
// Los parámetros de la plantilla de la función, que tengan valores por defecto,
// deben estar colocados después de los parámetros que no tengan valores
// por defecto, en la definición.
326

// Dicho de otra forma, si un parámetro tiene un tipo de dato asignado, o un


// valor asignado, todos los parámetros que le preceden deben también tener
// asignados sus tipos y sus valores.
//
#include <iostream>
#include <cmath> // Archivo de encabezado opcional.
using namespace std;
// Definición de la plantilla de la función sigma(). Esta función se
// aplica a los datos estadísticos de una población.
template<typename T, class R = T>
R sigma(T valores[], size_t cuenta)
{
double media{}; // Media de la población.
for (size_t i{}; i < cuenta; i++)
media += valores[i];
media /= cuenta;
double desv_std{}; // Desviación estándar. Es tipo R.
// Es la suma de los cuadrados.
for (size_t i{}; i < cuenta; i++)
// Es la suma de los cuadrados.
desv_std += pow(valores[i] - media, 2);
// Es la desviación estándar real.
return static_cast<R>(sqrt(desv_std / cuenta));
}
int main()
{
system("color FD");
cout << endl << endl << endl;
// Una colección cualquiera de datos numéricos.
int alturas[]{ 38, 97, 45, 72, 51, 88, 36, 29 };
// Aplicación de la función sigma.
cout << "La desviación estándar de las alturas es: "
<< sigma(alturas, _countof(alturas));
327

/* Note que en la llamada no se usa el especificador del tipo de dato. Los datos de la
matriz alturas[] son números enteros, y el compilador usa por defecto el tipo
double, que está definido en la plantilla. typename R = double. Así que el compila-
dor procede a hacer una conversión predefinida, de tipo menor a tipo mayor. De ente-
ro a double.
*/
cout << endl << endl << endl;
// Otra aplicación de la función sigma; esta vez definiendo el tipo de datos. Los datos
// originales de la matriz son números enteros, así que es más consistente publicar el
// promedio como un número entero. El número no se aproxima.
cout << "La desviación estándar de las alturas es: "
<< sigma<int, int>(alturas, _countof(alturas));
cout << endl << endl << endl;
// Otra aplicación de la función sigma; esta vez definiendo dos tipo de datos. Los datos
// originales de la matriz son números enteros, así que es más consistente publicar el
// promedio como un número entero. El número se publicará como flotante, para demostrar
// la flexibilidad de la plantilla.
cout << "La desviación estándar de las alturas es: "
<< sigma<int, float>(alturas, _countof(alturas));
cout << endl << endl << endl;
}
328

// IH194.cpp: Default class template arguments. Los argumentos por defecto en las plantillas
// de las clases. Con errores.
//
// Desde uno hasta todos los parámetros de la plantilla de una clase pueden tener
// argumentos por defecto.
// Un argumento con valor por defecto especificado debe tener a todos los argumen-
// tos previos con su valor por defecto especificado. Si no es así, el compilador
// señalará error.
//
#include <iostream>
#include <utility> // Genera errores al compilar.
#include <algorithm>
using namespace std;
using namespace std::rel_ops;
using std::cout;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición del constructor por defecto de la clase CCaja.
CCaja()
{
cout << "Se ha llamado al constructor por defecto de la clase CCaja.\n\n";
m_longitud = m_ancho = m_alto = 1.0;
}
329

// Definición de la función miembro volumen().


double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Definición de la plantilla de la clase CMuestras.
template <typename T = double, size_t TAM = 200> class CMuestras
{
public:
// Definición de los constructores de la clase CMuestras.
CMuestras(const T valores[], int cuenta);
CMuestras(const T& valor);
CMuestras(T&& valor);
CMuestras() = default;
bool agregar(const T& valor);
bool agregar(T&& valor);
T maX() const;
private:
T m_valores[TAM];
int m_Siguiente{};
};
// Definición del constructor de la clase CMuestras. La lista de parámetros debe
// coincidir con la definición de la plantilla de la clase CMuestras.
template <typename T, size_t TAM>
CMuestras<T, TAM>::CMuestras(const T valores[], int cuenta)
{
// Para no excederse en el tamaño.
m_Siguiente = cuenta < TAM ? cuenta : TAM;
330

for (int i{}; i < m_Siguiente; i++)


// Guardar el número de muestras.
m_valores[i] = valores[i];
}
// Definición del constructor de la clase CMuestras, para aceptar una muestra simple.
template <typename T, size_t TAM>
CMuestras<T, TAM>::CMuestras(const T& valor)
{
m_valores[0] = valor;
m_Siguiente = 1;
}
// Definición del constructor de la clase CMuestras, para aceptar una muestra
// temporalmente. Constructor de traslado o movimiento de datos.
template <typename T, size_t TAM>
CMuestras<T, TAM>::CMuestras(T&& valor)
{
cout << "Se ha llamado al constructor de traslado de la clase CMuestras.\n\n";
m_valores[0] = std::move(valor);
m_Siguiente = 1;
}
// Definición de la función agregar(). Esta función agrega una muestra a la matriz.
template <typename T, size_t TAM>
bool CMuestras<T, TAM>::agregar(const T& valor)
{
cout << "Se ha llamado a la función agregar().\n\n";
// Indica que hay espacio disponible.
bool MM{ m_Siguiente < TAM };
if (MM)
// Si MM es verdadero, guardar el dato.
m_valores[m_Siguiente++] = valor;
return MM;
}
331

// Definición de la función agregar(). Esta función traslada una muestra en la matriz.


template <typename T, size_t TAM>
bool CMuestras<T, TAM>::agregar(T&& valor)
{
cout << "Se ha llamado a la función agregar() para mover una muestra.\n\n";

// Indica que hay espacio disponible.


bool MM{ m_Siguiente < TAM };
if (MM)
// Si MM es verdadero, mover el dato.
m_valores[m_Siguiente++] = std::move(valor);
return MM;
}
// Definición de la función maX(). Obtiene la muestra más grande. Esta función tiene
// errores cuando se ejecuta.
template <typename T, size_t TAM>
T CMuestras<T, TAM>::maX() const
{
// Fijar la primera muestra como máxima, para iniciar comparaciones.
T laMaX{ m_valores[0] };
// Chequear todas las demás.
for (int i{ 1 }; i < m_Siguiente; i++)
if (m_valores[i] > laMaX)
// Conservar el valor más alto.
laMaX = m_valores[i];
return laMaX;
}
int main()
{
system("color FD");
cout << endl << endl << endl;
// Crear una matriz de cajas, inicializando las cajas.
CCaja cajas[]{
332

CCaja{ 12.0, 9.0, 6.0 },


CCaja{ 10.0, 8.0, 5.0 },
CCaja{ 9.0, 7.0, 4.0 }
};
// Crear un objeto CMuestras, como contenedor de objetos CCaja. El tipo de
// dato tiene que ser CMuestras<CCaja, _countof(cajas)>, con dos argumentos
// en la lista.
CMuestras<CCaja, _countof(cajas)> misCajas{ cajas, _countof(cajas) };
// Buscar la caja más grande.
CCaja cajaMaX{ misCajas.maX() }; // Aquí aparece el error.
cout << "La caja más grande tiene un volumen de: " << cajaMaX.volumen();
cout << endl << endl;
double valores[]{ 4.5, -6.2, 8.3, 2.0, -9.1, 5.7, 3.8 };
// La siguiente instrucción es rechazada por el compilador, señalando que
// la lista de argumentos de la plantilla de la clase CMuestras tiene muy
// pocos argumentos, en realidad cero. Se supone que aquí deberá funcionar
// el constructor por defecto. Se corrigió en la siguiente línea.
// CMuestras<> datos{ valores, _countof(valores) };
CMuestras< double, _countof(valores)> datos{ valores, _countof(valores) };
// Mostrar el elemento mayor de la matriz datos[].
cout << "El valor máximo de la matriz datos[] es " << datos.maX() << "\n\n";
int cuentas1[]{ 2, 16, 35, 22, 14, 7, 9, 18 };
CMuestras< int, _countof(cuentas1)> losDatos{ cuentas1, _countof(cuentas1) };
cout << "El mayor valor entero es " << losDatos.maX();
cout << endl << endl << endl;
}
333

// IH195.cpp: Aliases for class templates. Los alias para las plantillas de las clases. Con errores.
//
// Es posible definir un alias para la plantilla de una clase, con la especificación
// de todos o algunos de sus parámetros.
// Se utiliza la palabra clave "using". Esto se hace dentro del código de la función
// main().
//
#include <iostream>
#include <utility> // Genera errores al compilar.
#include <algorithm>
using namespace std;
using namespace std::rel_ops;
using std::cout;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición del constructor por defecto de la clase CCaja.
CCaja()
{
cout << "Se ha llamado al constructor por defecto de la clase CCaja.\n\n";
m_longitud = m_ancho = m_alto = 1.0;
}
// Definición de la función miembro volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
334

}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Definición de la plantilla de la clase CMuestras.
template <typename T = double, size_t TAM = 200> class CMuestras
{
public:
// Definición de los constructores de la clase CMuestras.
CMuestras(const T valores[], int cuenta);
CMuestras(const T& valor);
CMuestras(T&& valor);
CMuestras() = default;
bool agregar(const T& valor);
bool agregar(T&& valor);
T maX() const;
private:
T m_valores[TAM];
int m_Siguiente{};
};
// Definición del constructor de la clase CMuestras. La lista de parámetros debe
// coincidir con la definición de la plantilla de la clase CMuestras.
template <typename T, size_t TAM>
CMuestras<T, TAM>::CMuestras(const T valores[], int cuenta)
{
// Para no excederse en el tamaño.
m_Siguiente = cuenta < TAM ? cuenta : TAM;
for (int i{}; i < m_Siguiente; i++)
// Guardar el número de muestras.
m_valores[i] = valores[i];
}
335

// Definición del constructor de la clase CMuestras, para aceptar una muestra simple.
template <typename T, size_t TAM>
CMuestras<T, TAM>::CMuestras(const T& valor)
{
m_valores[0] = valor;
m_Siguiente = 1;
}
// Definición del constructor de la clase CMuestras, para aceptar una muestra
// temporalmente. Constructor de traslado o movimiento de datos.
template <typename T, size_t TAM>
CMuestras<T, TAM>::CMuestras(T&& valor)
{
cout << "Se ha llamado al constructor de traslado de la clase CMuestras.\n\n";
m_valores[0] = std::move(valor);
m_Siguiente = 1;
}
// Definición de la función agregar(). Esta función agrega una muestra a la matriz.
template <typename T, size_t TAM>
bool CMuestras<T, TAM>::agregar(const T& valor)
{
cout << "Se ha llamado a la función agregar().\n\n";
// Indica que hay espacio disponible.
bool MM{ m_Siguiente < TAM };
if (MM)
// Si MM es verdadero, guardar el dato.
m_valores[m_Siguiente++] = valor;
return MM;
}
// Definición de la función agregar(). Esta función traslada una muestra en la matriz.
template <typename T, size_t TAM>
bool CMuestras<T, TAM>::agregar(T&& valor)
{
cout << "Se ha llamado a la función agregar() para mover una muestra.\n\n";
336

// Indica que hay espacio disponible.


bool MM{ m_Siguiente < TAM };
if (MM)
// Si MM es verdadero, mover el dato.
m_valores[m_Siguiente++] = std::move(valor);
return MM;
}
// Definición de la función maX(). Obtiene la muestra más grande. Esta función tiene
// errores cuando se ejecuta.
template <typename T, size_t TAM>
T CMuestras<T, TAM>::maX() const
{
// Fijar la primera muestra como máxima, para iniciar comparaciones.
T laMaX{ m_valores[0] };
// Chequear todas las demás.
for (int i{ 1 }; i < m_Siguiente; i++)
if (m_valores[i] > laMaX)
// Conservar el valor más alto.
laMaX = m_valores[i];
return laMaX;
}
int main()
{
system("color FD");
cout << endl << endl << endl;
// Crear una matriz de cajas, inicializando las cajas.
CCaja cajas[]{
CCaja{ 12.0, 9.0, 6.0 },
CCaja{ 10.0, 8.0, 5.0 },
CCaja{ 9.0, 7.0, 4.0 }
};
337

//-----------------------------------------------------------------------
// Usar un alias para la plantilla de la clase CMuestras<CCaja>.
using CajasMuestras = CMuestras<CCaja>;
CajasMuestras tusCajas{ cajas, _countof(cajas) };
// Transfiere los parámetros por defecto.
using Muestras = CMuestras<>;
//-----------------------------------------------------------------------
// Crear un objeto CMuestras, como contenedor de objetos CCaja. El tipo de
// dato tiene que ser CMuestras<CCaja, _countof(cajas)>, con dos argumentos
// en la lista.
CMuestras<CCaja, _countof(cajas)> misCajas{ cajas, _countof(cajas) };
// Buscar la caja más grande.
CCaja cajaMaX{ misCajas.maX() };
cout << "La caja más grande tiene un volumen de: " << cajaMaX.volumen();
cout << endl << endl;
double valores[]{ 4.5, -6.2, 8.3, 2.0, -9.1, 5.7, 3.8 };
// La siguiente instrucción es rechazada por el compilador, señalando que
// la lista de argumentos de la plantilla de la clase CMuestras tiene muy
// pocos argumentos, en realidad cero. Se supone que aquí deberá funcionar
// el constructor por defecto. Se corrigió en la siguiente línea.
// CMuestras<> datos{ valores, _countof(valores) };
CMuestras< double, _countof(valores)> datos{ valores, _countof(valores) };
// Mostrar el elemento mayor de la matriz datos[].
cout << "El valor máximo de la matriz datos[] es " << datos.maX() << "\n\n";
int cuentas1[]{ 2, 16, 35, 22, 14, 7, 9, 18 };
CMuestras< int, _countof(cuentas1)> losDatos{ cuentas1, _countof(cuentas1) };
cout << "El mayor valor entero es " << losDatos.maX();
//-----------------------------------------------------------------------
Muestras susDatos{ valores, _countof(valores) };
cout << endl << endl << endl;
}
338

// IH196.cpp: Template specialization. La especialización de las plantillas. (1).


//
// Habrá siempre casos en los que ciertos valores de parámetros para los
// argumentos de la plantilla de una clase o de una función no serán acep-
// tables por el compilador.
// Los tipos de datos punteros, por ejemplo, son muy difíciles de usar en
// los argumentos de las plantillas.
// Se aplica aquí el criterio y la técnica conocida como "especialización"
// de las plantillas. Esta técnica consiste en la definición adicional de
// de la plantilla para un conjunto especial de valores en los parámetros.
// El compilador no creará el código de la plantilla hasta que en la función
// main() se declare, mediante una instrucción, el uso de la plantilla de
// especialización.
//
#include <iostream>
using namespace std;
// Definición de la plantilla de la función promedio().
template <typename T>
T promedio(T valores[], size_t cuenta)
{
T media{};
for (size_t i{}; i < cuenta; i++)
media += valores[i];
return media / cuenta;
}
class CCaja
{
public:
// Constructor de la clase CCaja
explicit CCaja(double lng = 3.0, double anc = 2.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "\n\nSe ha llamado al constructor de la clase CCaja.\n\n";
339

}
// Definición del constructor por defecto de la clase CCaja.
CCaja() = default;
// Función para calcular el volumen()
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
// Función pública para tener acceso a la variable miembro longitud. Debo poner la
// palabra clave inline antes del nombre de la función, para indicarle al compilador
// que deseo que agregue el código a la compilación, haciéndola inline.
inline double accLongitud()
{
// Da acceso de lectura al valor de m_longitud.
return m_longitud;
}
inline double accAncho()
{
// Da acceso de lectura al valor de m_ancho.
return m_ancho;
}
inline double accAlto()
{
// Da acceso de lectura al valor de m_alto.
return m_alto;
}
// Definición de la función miembro mostrarCaja().
void mostrarCaja() const
{
cout << "La longitud de la caja es: " << m_longitud << endl << endl;
cout << "La anchura de la caja es: " << m_ancho << endl << endl;
cout << "La altura de la caja es: " << m_alto << endl << endl;
}
340

private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Definición de la especialización de la plantilla de la función promedio() para cajas.
template<> CCaja promedio(CCaja cajas[], size_t cuenta)
{
double alto{}, ancho{}, longitud{};
for (size_t i{}; i < cuenta; i++)
{
alto += cajas[i].accAlto();
ancho += cajas[i].accAncho();
longitud += cajas[i].accLongitud();
}
return CCaja{ alto / cuenta, ancho / cuenta, longitud / cuenta };
}
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja lasCajas[]{ CCaja {9.6, 8.4, 5.2}, CCaja {7.8, 6.4, 4.9}, CCaja {6.2, 4.8, 3.5} };
cout << "\n\n\nEl promedio de dimensiones de todas las cajas es:";
promedio(lasCajas, _countof(lasCajas)).mostrarCaja();
cout << endl << endl << endl;
}
341

// IH197.cpp: Template specialization. La especialización de las plantillas. (2).


// Parámetro explícito en la lista de argumentos de la plantilla.
//
// Habrá siempre casos en los que ciertos valores de parámetros para los
// argumentos de la plantilla de una clase o de una función no serán acep-
// tables por el compilador.
// Los tipos de datos punteros, por ejemplo, son muy difíciles de usar en
// los argumentos de las plantillas.
// Se aplica aquí el criterio y la técnica conocida como "especialización"
// de las plantillas. Esta técnica consiste en la definición adicional de
// de la plantilla para un conjunto especial de valores en los parámetros.
// Escribir el valor del parámetro de especialización en forma explícita.
// El compilador no creará el código de la plantilla hasta que en la función
// main() se declare, mediante una instrucción, el uso de la plantilla de
// especialización.
//
#include <iostream>
using namespace std;
// Definición de la plantilla de la función promedio().
template <typename T>
T promedio(T valores[], size_t cuenta)
{
T media{};
for (size_t i{}; i < cuenta; i++)
media += valores[i];
return media / cuenta;
}
class CCaja
{
public:
// Constructor de la clase CCaja.
explicit CCaja(double lng = 3.0, double anc = 2.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
342

{
cout << "\n\nSe ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición del constructor por defecto de la clase CCaja.
CCaja() = default;
// Función para calcular el volumen()
double volumen()
{
return m_longitud * m_ancho * m_alto;
}
// Función pública para tener acceso a la variable miembro longitud. Debo poner la
// palabra clave inline antes del nombre de la función, para indicarle al compilador
// que deseo que agregue el código a la compilación, haciéndola inline.
inline double accLongitud()
{
// Da acceso de lectura al valor de m_longitud.
return m_longitud;
}
inline double accAncho()
{
// Da acceso de lectura al valor de m_ancho.
return m_ancho;
}
inline double accAlto()
{
// Da acceso de lectura al valor de m_alto.
return m_alto;
}
// Definición de la función miembro mostrarCaja().
void mostrarCaja() const
{
cout << "La longitud de la caja es: " << m_longitud << endl << endl;
cout << "La anchura de la caja es: " << m_ancho << endl << endl;
343

cout << "La altura de la caja es: " << m_alto << endl << endl;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Definición de la especialización de la plantilla de la función promedio()
// para cajas. El parámetro de especialización se vuelve explícito escribién-
// dolo inmediatamente después del nombre de la función, <CCaja>. Esto se
// hace cuando el compilador tiene problemas para identificar el parámetro
// del tipo de dato T en la plantilla. Aquí origina errores.
template<>
CCaja promedio<CCaja>(CCaja cajas[], size_t cuenta)
{
double alto{}, ancho{}, longitud{};
for (size_t i{}; i < cuenta; i++)
{
alto += cajas[i].accAlto();
ancho += cajas[i].accAncho();
longitud += cajas[i].accLongitud();
}
return CCaja { alto / cuenta, ancho / cuenta, longitud / cuenta };
}
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja lasCajas[]{ CCaja {9.6, 8.4, 5.2}, CCaja {7.8, 6.4, 4.9}, CCaja {6.2, 4.8, 3.5} };
cout << "\n\n\nEl promedio de dimensiones de todas las cajas es:";
promedio(lasCajas, _countof(lasCajas)).mostrarCaja();
cout << endl << endl << endl;
}
344

// IH198.cpp: A specialization for a class template. A complete specialization. Una


// especialización para una plantilla de una clase. Una especialización completa.
//
// Al escribir <> al lado de la palabra clave "template", se le está indicando al
// compilador que la definición que continúa es una especialización de una plantilla.
// El tipo de dato especializado aparece entre los signos menor que y mayor
// que, después del nombre de la plantilla, CMuestras. Así:
// template<> class CMuestras<CCaja>
// Cuando una instancia de la plantilla CMuestras se haya de crear usando el pará-
// metro CCaja, el compilador usará esta especialización para crear la clase.
// La anterior especialización se conoce como una "especialización completa", ya
// que contiene solamente un parámetro simple. Si tuviera varios parámetros, y los
// incluyera a todos, también sería una especialización completa. Cuando se aplica
// a sólo una parte de los argumentos de la clase, entonces es una especialización
// parcial.
//
#include <iostream>
#include <utility>
#include <algorithm>
using namespace std;
using namespace std::rel_ops;
using std::cout;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
345

// Definición del constructor por defecto de la clase CCaja.


CCaja()
{
cout << "Se ha llamado al constructor por defecto de la clase CCaja.\n\n";
m_longitud = m_ancho = m_alto = 1.0;
}
// Definición de la función miembro volumen().
double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
// Definición de la función miembro mostrarCaja().
void mostrarCaja() const
{
cout << "La longitud de la caja es: " << m_longitud << endl << endl;
cout << "La anchura de la caja es: " << m_ancho << endl << endl;
cout << "La altura de la caja es: " << m_alto << endl << endl;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Definición de la plantilla de la clase CMuestras.
template <typename T = double, size_t TAM = 200> class CMuestras
{
public:
// Definición de los constructores de la clase CMuestras.
CMuestras(const T valores[], int cuenta);
CMuestras(const T& valor);
CMuestras(T&& valor);
CMuestras() = default;
346

bool agregar(const T& valor);


bool agregar(T&& valor);
T maX() const;
private:
T m_valores[TAM];
int m_Siguiente{};
};
// Definición del constructor de la clase CMuestras. La lista de parámetros debe
// coincidir con la definición de la plantilla de la clase CMuestras.
template <typename T, size_t TAM>
CMuestras<T, TAM>::CMuestras(const T valores[], int cuenta)
{
// Para no excederse en el tamaño.
m_Siguiente = cuenta < TAM ? cuenta : TAM;
for (int i{}; i < m_Siguiente; i++)
// Guardar el número de muestras.
m_valores[i] = valores[i];
}
// Definición del constructor de la clase CMuestras, para aceptar una muestra simple.
template <typename T, size_t TAM>
CMuestras<T, TAM>::CMuestras(const T& valor)
{
m_valores[0] = valor;
m_Siguiente = 1;
}
// Definición del constructor de la clase CMuestras, para aceptar una muestra
// temporalmente. Constructor de traslado o movimiento de datos.
template <typename T, size_t TAM>
CMuestras<T, TAM>::CMuestras(T&& valor)
{
cout << "Se ha llamado al constructor de traslado de la clase CMuestras.\n\n";
m_valores[0] = std::move(valor);
m_Siguiente = 1;
347

}
// Definición de la función agregar(). Esta función agrega una muestra a la matriz.
template <typename T, size_t TAM>
bool CMuestras<T, TAM>::agregar(const T& valor)
{
cout << "Se ha llamado a la función agregar().\n\n";

// Indica que hay espacio disponible.


bool MM{ m_Siguiente < TAM };
if (MM)
// Si MM es verdadero, guardar el dato.
m_valores[m_Siguiente++] = valor;
return MM;
}
// Definición de la función agregar(). Esta función traslada una muestra en la matriz.
template <typename T, size_t TAM>
bool CMuestras<T, TAM>::agregar(T&& valor)
{
cout << "Se ha llamado a la función agregar() para mover una muestra.\n\n";
// Indica que hay espacio disponible.
bool MM{ m_Siguiente < TAM };
if (MM)
// Si MM es verdadero, mover el dato.
m_valores[m_Siguiente++] = std::move(valor);
return MM;
}
// Definición de la función maX(). Obtiene la muestra más grande.
template <typename T, size_t TAM>
T CMuestras<T, TAM>::maX() const
{
// Fijar la primera muestra como máxima, para iniciar comparaciones.
T laMaX{ m_valores[0] };
// Chequear todas las demás.
348

for (int i{ 1 }; i < m_Siguiente; i++)


if (m_valores[i] > laMaX)
// Conservar el valor más alto.
laMaX = m_valores[i];
return laMaX;
}
//------------------------------------------------------------------------------
// Definición de la especialización para la plantilla de la clase CMuestras.
template<> class CMuestras<CCaja>
{
public:
// Definición del constructor de la clase CMuestras.
CMuestras(const CCaja cajas[], int cuenta)
{
m_Siguiente = cuenta < TAM ? cuenta : TAM;
for (int i{}; i < m_Siguiente; i++)
m_cajas[i] = cajas[i];
}
// Definición del constructor de la clase CMuestras para una muestra.
CMuestras(const CCaja& laCaja)
{
m_cajas[0] = laCaja;
m_Siguiente = 1;
}
// Definición del constructor por defecto de la clase CMuestras.
CMuestras() = default;
// Definición de la función agregar(), para agregar una caja.
bool agregar(const CCaja& laCaja)
{
bool MM{ m_Siguiente < TAM };
if (MM)
m_cajas[m_Siguiente++] = laCaja;
return MM;
349

}
// Definición de la función maX() para determinar la caja más grande.
CCaja maX() const
{
// Poner la primera caja como la más grande, para iniciar las comparaciones.
CCaja maXcaja{ m_cajas[0] };
for (int i{ 1 }; i < m_Siguiente; i++)
if (m_cajas[i].volumen() > maXcaja.volumen())
maXcaja = m_cajas[i];
return maXcaja;
}
private:
static const size_t TAM{ 6 };
CCaja m_cajas[TAM];
int m_Siguiente;
};
int main()
{
system("color FD");
cout << endl << endl << endl;
CCaja cajas[]{ CCaja {18.0, 16.2, 14.4},
CCaja {16.0, 14.4, 12.8},
CCaja {14.6, 12.8, 10.0} };
CMuestras<CCaja> misCajas{ cajas, _countof(cajas) };
cout << "\n\n\nLa caja más grande es ";
if (misCajas.maX().volumen() == cajas[0].volumen())
cout << "la primera caja.";
else
cout << "la segunda caja.";
cout << endl << endl << endl;
}
350

// IH199.cpp: A specialization for a class template. A partial specialization. Una especialización


// para una plantilla de una clase. Una especialización parcial.
//
// Es posible definir una especialización de una plantilla que abarque únicamente a
// una parte o a un rango de parámetros. Usualmente, el rango es un rango de punte-
// ros.
// Los tipos de datos pueden ser double*, CCaja* y otros.
// MMM: La definición de las clases normales deben estar al principio, antes de cual-
// quier definición de una plantilla de clase o de función.
//
#include <iostream>
#include <utility>
#include <algorithm>
using namespace std;
// using namespace std::rel_ops;
using std::cout;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición del constructor por defecto de la clase CCaja.
CCaja()
{
cout << "Se ha llamado al constructor por defecto de la clase CCaja.\n\n";
m_longitud = m_ancho = m_alto = 1.0;
}
351

// Definición de la función miembro volumen().


double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Definición de la plantilla de la clase CMuestras. Esta es una especialización
// parcial para punteros. El compilador no acepta una lista de argumentos en la
// declaración de una plantilla primaria. // class CMuestras<T*>.
template <typename T>
class CMuestras
{
public:
// Definición del constructor de la clase CMuestras. Este constructor
// nos permitirá manejar una matriz de punteros.
CMuestras(T* rMuestras[], int cuenta)
{
// m_Siguiente estará dentro de un rango, menor o igual a TAM.
m_Siguiente = (cuenta < TAM) ? cuenta : TAM;
// Guardar las muestras indicadas.
for (int i{}; i < m_Siguiente; i++)
m_rMuestras[i] = rMuestras[i];
}
// Definición de la función maX(). Obtiene la dirección de memoria de la muestra
// más grande.
T* maX() const
{
// Fijar la primera muestra como máxima, para iniciar comparaciones.
T* rLaMaX{ m_rMuestras[0] };
352

// Chequear todas las demás muestras.


for (int i{ 1 }; i < m_Siguiente; i++)
if (m_rMuestras[i] > rLaMaX)
// Conservar el valor más alto.
rLaMaX = m_rMuestras[i];
return rLaMaX;
}
private:
// Cantidad máxima de muestras en la matriz de muestras.
static const size_t TAM{ 200 };
// Matriz de punteros a cada una de las muestras.
T* m_rMuestras[TAM];
// Indicador del siguiente elemento a procesar dentro de la matriz.
int m_Siguiente{};
};
int main()
{
system("color FD");
cout << endl << endl << endl;
static const size_t TAM{ 200 };
// Crear una matriz de objetos CCaja.
CCaja cajas[]{ CCaja {19.4, 16.8, 14.4},
CCaja {18.1, 17.3, 15.7},
CCaja {16.9, 14.3, 11.8} };
// Crear una matriz de punteros a los objetos CCaja.
CCaja* rCajas[]{ cajas, cajas + 1, cajas + 2 };
CMuestras<CCaja> rMuestrasDeCajas{ rCajas, _countof(rCajas) };
cout << "\n\n\nEl volumen de la caja más grande es: "
<< rMuestrasDeCajas.maX()->volumen();
cout << endl << endl << endl;
}
353

// IH200.cpp: A specialization for a class template. A partial specialization with remaining


// variable parameter. Una especialización para una plantilla de una clase. Una
// especialización parcial con parámetro que permanece variable.
//
// Es posible definir una especialización de una plantilla que abarque únicamente a
// una parte o a un rango de parámetros. Usualmente, el rango es un rango de
// punteros.
// Los tipos de datos pueden ser double*, CCaja* y otros.
// MMM: La definición de las clases normales deben estar al principio, antes de cual-
// quier definición de una plantilla de clase o de función.
//
#include <iostream>
#include <utility>
#include <algorithm>
using namespace std;
// using namespace std::rel_ops;
using std::cout;
// Definición de la clase CCaja, con ámbito global.
class CCaja
{
public:
// Definición del constructor de la clase CCaja.
explicit CCaja(double lng, double anc = 1.0,
double alt = 1.0) : m_longitud{ lng }, m_ancho{ anc }, m_alto{ alt }
{
cout << "Se ha llamado al constructor de la clase CCaja.\n\n";
}
// Definición del constructor por defecto de la clase CCaja.
CCaja()
{
cout << "Se ha llamado al constructor por defecto de la clase CCaja.\n\n";
m_longitud = m_ancho = m_alto = 1.0;
}
354

// Definición de la función miembro volumen().


double volumen() const
{
return m_longitud * m_ancho * m_alto;
}
private:
double m_longitud;
double m_ancho;
double m_alto;
};
// Definición de la plantilla de la clase CMuestras. Esta es una especialización
// parcial para punteros. El compilador no acepta una lista de argumentos en la
// declaración de una plantilla primaria.
template <typename S>
class CMuestras
{
public:
// Definición del constructor de la clase CMuestras. Este constructor
// nos permitirá manejar una matriz de punteros.
CMuestras(CCaja* rMuestras[], S cuenta)
{
// m_Siguiente estará dentro de un rango, menor o igual a TAM.
m_Siguiente = (cuenta < TAM) ? cuenta : TAM;
// Guardar las muestras indicadas.
for (int i{}; i < m_Siguiente; i++)
m_rMuestras[i] = rMuestras[i];
}
// Definición de la función maX(). Obtiene la dirección de memoria de la muestra
// más grande.
CCaja* maX() const
{
// Fijar la primera muestra como máxima, para iniciar comparaciones.
CCaja* rLaMaX{ m_rMuestras[0] };
355

// Chequear todas las demás muestras.


for (int i{ 1 }; i < m_Siguiente; i++)
if (m_rMuestras[i] > rLaMaX)
// Conservar el valor más alto.
rLaMaX = m_rMuestras[i];
return rLaMaX;
}
private:
// Cantidad máxima de muestras en la matriz de muestras.
static const size_t TAM{ 200 };
// Matriz de punteros a cada una de las muestras.
CCaja* m_rMuestras[TAM];
// Indicador del siguiente elemento a procesar dentro de la matriz.
int m_Siguiente{};
};
int main()
{
system("color FD");
cout << endl << endl << endl;
int cuenta{ 200 };
// Crear una matriz de objetos CCaja.
CCaja cajas[]{ CCaja {19.4, 16.8, 14.4},
CCaja {18.1, 17.3, 15.7},
CCaja {16.9, 14.3, 11.8} };
// Crear una matriz de punteros a los objetos CCaja.
CCaja* rCajas[]{ cajas, cajas + 1, cajas + 2 };
CMuestras<size_t> rMuestrasDeCajas{ rCajas, _countof(rCajas) };
cout << "\n\n\nEl volumen de la caja más grande es: "
<< rMuestrasDeCajas.maX()->volumen();
cout << endl << endl << endl;
}

También podría gustarte