Alcides Iván Meza - 200 Ejercicios de Visual C++ 2019
Alcides Iván Meza - 200 Ejercicios de Visual C++ 2019
Alcides Iván Meza - 200 Ejercicios de Visual C++ 2019
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
#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
int main()
{
int numero1{ 123 }, numero2{ 456 }, numero3{ 789 };
cout << endl << endl << endl;
cout << numero1;
cout << numero1 << numero2;
cout << numero1 << numero2 << numero3;
}
int main()
{
cout << "\n\n\n"; // Tres líneas en blanco.
#include <iostream>
#include <iomanip> // Para utilizar setw(n).
int main()
{
cout << "\n\n\n";
cout << setw(6) << numero1 << setw(6) << numero2 << setw(6) << numero3;
// 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>
int main()
{
cout << "\n\n\n";
cout << setw(6) << numero1 << setw(6) << numero2 << setw(6) << numero3;
int main()
{
cout << endl << endl << endl;
c = a - b; // operación binaria
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;
// 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;
}
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;
}
// 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
// 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
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;
}
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 << "El valor de estilo0 es: " << estilo0 << endl;
cout << "El valor de estilo1 es: " << estilo1 << endl << endl;
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 << "El valor de estilo1 es: " << estilo1 << endl << endl;
estilo1 = 65535U;
//
#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;
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;
}
cout << "Valor externo de la variable automática cuenta1: " << cuenta1 << endl << endl;
24
/* 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
// 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
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 << "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.";
// 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
<< 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;
}
#include <iostream>
int main()
{
system("color FD");
int miNum{ 0 };
miEtiq: cout << "Esta es mi etiqueta. Aquí se reinicia el procedimiento." << endl << endl;
if (miNum == 0)
miNum += 5;
else
return 0;
cout << "Este es el numero: " << miNum << endl << endl;
goto miEtiq;
// IH41.cpp: Loop with if and goto. Los ciclos con las sentencias if y goto.
//
#include <iostream>
int main()
{
system("color FD");
int i{ 1 };
int suma{};
loop:
suma += i;
cout << "Suma = " << suma << endl << endl
<< "i = " << i;
}
41
#include <iostream>
int main()
{
system("color FD");
int i{ 1 };
int suma{};
cout << "La suma de los primeros 10 dígitos es: " << suma
<< endl << endl << "i es ahora: " << i;
// IH43.cpp: Using multiple counters. El uso de contadores múltiples. For loop. Ciclo for.
//
#include <iostream>
#include <iomanip>
int main()
{
system("color FD");
}
43
cout << endl << endl << "El promedio de los datos es: " << suma / i;
#include <iostream>
int main()
{
system("color FD");
int valor{};
int producto{ 1 };
producto *= valor;
}
// IH46.cpp: Using other types in loops. El uso de otros tipos de datos en los ciclos.
//
#include <iostream>
#include <iomanip>
int main()
{
system("color FD");
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.
}
++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;
}
cout << endl << endl << "La suma de los " << i << " números es: " << suma;
cout << endl << endl << "El promedio de los datos es: " << suma / i;
//
#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
}
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;
}
*/
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
// 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
}
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;
}
// "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;
}
// 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;
}
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;
}
//----------------------------------------------------------------------
65
//----------------------------------------------------------
//----------------------------------------------------------
cout << "\n\n\nEl número de bytes de una variable tipo long es: " << long_size;
*/
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
// 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
// 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;
}
// 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
// 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
// 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.";
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
// 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;
}
#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";
}
//
// 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
// 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
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
//
// 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
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
// 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;
}
// 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
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;
}
{
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;
}
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;
}
//
#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
// 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
}
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
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;
}
{
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
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;
}
};
*/
// 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;
}
// 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;
}
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
// 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
// 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;
}
{
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
#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
// 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
{
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;
}
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
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
//
#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
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
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
// 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
{
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;
}
}
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
// 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
// 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
{
// 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
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;
}
// 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
// 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
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;
}
{
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
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
// 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
// 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
}
// 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.
// 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
// 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 << "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;
}
}
// 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
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().
*/
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
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
// 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
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
{
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
// 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
*/
// 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
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
{
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
}
// 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
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
#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
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
}
// 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
#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
// 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
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
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
// 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
// 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
#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
//-------------------------------------------------------------------------
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
}
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
/* 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
// 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
//-----------------------------------------------------------------------
// 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
}
// 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
{
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
}
// 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";
}
// 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