Capitulo 03 C++ - Estructuras
Capitulo 03 C++ - Estructuras
Capitulo 03 C++ - Estructuras
Sumario
Estructuras de datos
Estructuras básicas en C, C++
Variables
Matrices o Arreglos
Matrices estáticas
Acceso a los miembros de una matriz de datos:
Matrices dinámicas
Estructuras compuestas (struct, union, class)
Acceso a los miembros de una estructura
Estructuras anidadas
Herencia
Estructura de campos de bits
union: Sintaxis general
class: sintaxis
struct vs. class
Estructuras de datos
Las estructuras de datos se emplean con el objetivo principal de organizar los datos contenidos
dentro de la memoria del ordenador. Así, nuestra primera experiencia con estructuras comienza
desde el momento mismo en que usamos en nuestros programas variables de tipos primitivos
(char, short, int, float, etc). A la memoria del ordenador se le puede considerar como un gran
bloque compuesto por una serie de BYTES dispuestos secuencialmente uno detrás de otro. por
ejemplo, si un ordenador posee una memoria de 128MB (128 megas) entonces se le puede leer
o escribir desde el BYTE 0 hasta el BYTE 128MB - 1 ( 0000000H .. 7FFFFFFH )
La idea de ver la memoria como un serie de bytes es buena, sin embargo no es suficiente ya que
en la misma podemos guardar números, cadenas de caracteres, funciones, objetos, etc. de tal
manera que surge la necesidad de establecer los mecanismos adecuados para dar cuenta de la
forma, tamaño y objetivo de los datos almacenados. Según el tipo de microprocesador, estos
tienen la capacidad para manipular o direccionar estructuras compuestas por uno, dos, cuatro,
etc, bytes; de donde se derivan los tipos que comunmente se conocen como: BYTE, WORD,
DWORD, QWORD y TWORD.
En C,C++ existe una serie de estructuras básicas o tipos primitivos, los cuales pueden ser
usados por el programador para declarar variables, y también son el fundamento sobre el cual se
crean estructuras complejas. El tamaño de los tipos primitivos no es estándar ya que los mismos
dependen de factores tales como:
Nota: en el lenguaje C,C++ existe el operador sizeof(), con el cual se puede obtener el tamaño
(número de bytes) ocupados por un tipo específico. Por ejemplo, sizeof(int) regresa el número
de bytes ocupados por los datos de tipo int.
Variables
Ejemplos:
De acuerdo con la tabla anterior y según las instrucciones anteriores, con la primera, o sea (
char c;), se le está indicando al ordenador que reserve en la memoria un espacio de tipo char (8
bits) y que el mismo será identificado bajo el nombre de c. La segunda instrucción ( int i;) le
indica al ordenador que reserve en la memoria un espacio de tipo int (16 bits) y que el mismo
será identificado bajo el nombre de i. Luego, la instrucción ( float f;) le indica al ordenador que
reserve en la memoria un espacio de tipo float (32 bits) y que el mismo será identificado bajo el
nombre de f. Por último, se le indica al compilador que reserve espacio para otras tres variables
enteras identificadas como: x, y, z. Así, se puede dar cuenta cómo los tipos primitivos sirven
con el propósito de estructurar los datos dentro de la memoria y con la idea de referirnos a los
mismos mediante nombres usamos identificadores de variables. .
Matrices o Arreglos
Una Matriz (en inglés, array, también denominado arreglo) es una estructura usada para
agrupar bajo un mismo nombre
listas de datos de un mismo tipo.
El tipo de matriz puede ser cualquiera, sin embargo cada componente tiene que ser del mismo
tipo. En C estándar solamente da soporte para matrices estáticas, mientras que con C++ se
pueden crear matrices dinámicas pudiendo usar la librería estándar de plantillas (STL).
Matrices estáticas
Una matriz estática es una estructura cuyo tamaño es determinado en tiempo de compilación, es
decir, una vez establecido el tamaño de la matriz ésta no podrá cambiarse durante el tiempo de
ejecución. En C, C++ para declarar un arreglo estático de datos se emplea la sintaxis:
donde,
tipo se refiere al tipo de datos que contendrá la matriz. El tipo puede ser
cualquiera de los tipos estándar (char, int, float, etc.) o un tipo definido por el
usuario. Es más, el tipo de la matriz puede ser de una estructura creada con:
struct, union y class.
lista de inicialización es opcional y se usa para establecer valores para cada uno
de los componentes de la matriz. Si la matriz es declarada con un tamaño
especifíco, el número de valores inicializados no podrá ser mayor a dicho tamaño.
Ejemplos:
int intA[5];
long longA[5] = { 1, 2, 3, 4, 5 };
char charA[] = { 'a', 'b', 'c' };
En orden de acceder a los miembros de una matriz se debe indicar el nombre de la matriz
seguido de dos corchetes, dentro de los cuales se debe especificar el índice del elemento
deseado. Se debe aclarar que los índices son números o expresiones enteras y que en C, C++
estos tienen un rango permitido de 0 a T-1 ( T = tamaño de la matriz ).
cout << longA[0]; // muestra por pantalla el elemento 0 de longA, que es longA[0].
Matrices dinámicas
Una matriz dinámica es una estructura compleja y, ya que C estándar no da el soporte para
operar con estos tipos de estructuras, le corresponde al programador crear los algoritmos
necesarios para su implementación. Crear lista dinámicas de datos en C estándar no es una tarea
para programadores inexpertos, ya que para lograr tal objetivo se necesita tener conocimentos
solidos acerca de los punteros y el comportamiento de los mismos. Los usuarios de C++ pueden
auxiliarse de la librería estándar de plantillas, conocidas por sus siglas en ingles como STL.
struct: esta orden se emplea para agrupar variables de tipos iguales o diferentes
en un solo registro, con la misma se pueden crear estructuras anónimas,
estructuras con nombre, y un tipo especial de estructura conocida como bit-fields (
banderas o campos de bits ).
union: una union es similar a una struct, salvo que en una estructura creada con
union los campos o variables comparten una dirección de almacenamiento común.
class: una clase es una estructura en donde se agrupan variables y funciones, la
misma es usada en Programación Orientada al Objeto. Las clases no son
soportadas por el C estándar.
Nota: tanto las estructuras como las uniones y las clases pueden ser anónimas, pero lo
más
recomendable es darle a las mismas un nombre. Si una estructura, union o clase posee
nombre, esta pueden ser empleadas para declarar variables de la misma y, lo más
importante,
puede ser usada para el paso de parámetros a funciones.
De acuerdo con la sintaxis general de la orden struct es posible crear estructuras de datos
anónimas. Solamente hay que tener en cuenta que en una declaración anónima se debe definir al
menos una variable al final de la declaración. Por ejemplo, con el siguiente fragmento de
código:
se declara y define la variable estructurada p1, misma que se compone por los miembros a y b;
ambos del tipo int. Ahora bien, la sintaxis mostrada arriba no es tan común ni conveniente, ya
que con la misma solamente se esta creando una variable estructurada pero no un nuevo tipo. Es
decir, si desearamos tener otra variable que tuviera las mismas caracteristicas que posee la
variable p1, necesitariamos escribir exactamente la misma instrucción, salvo que cambiando el
nombre de la variable. Por ejemplo:
Por supuesto, en una misma línea de instrucción podemos definir más de una variable. Ejemplo:
Entonces, para crear nuevos tipos con struct deberemos de modificar la sintaxis mostrada en
los ejemplos anteriores.
Ejemplos:
En el fragmento de código anterior se declara la estructura identificada como pareja, misma que
se compone de los miembros a y b, ambos de tipo int. En el mismo ejemplo, se define la
variable p1; la cual es una variable estructurada de tipo pareja.
Una vez que una estructura con nombre ha sido creada, la misma puede ser usada para declarar
cualquier número de variables. Por ejemplo, en el siguiente fragmento de código se crea la
estructura tiempo compuesta por los miembros hora, minuto y segundo; todos del tipo int. En el
mismo ejemplo, se declaran las variables t1 y t2.
Nota: en C++ puede obviarse la palabra struct a la hora de declarar variables. Así, en C++ la
línea de instrución struct tiempo t1, t2; ( del ejemplo anterior) puede escibirse como: tiempo
t1, t2;
En orden de poder leer o escribir uno de los miembros de una variable estructurada, se debe
usar el operador de acceso ( . ); o sea, el nombre de la variable seguida por un punto seguido por
el nombre del miembro o componente deseado de la estructura. Por ejemplo, para acceder a los
miembros de la variable t1 (mostrada arriba) podemos hacerlo de la siguiente manera:
t1.hora = 12;
t1.minuto = 0;
t1.segundo = 0;
Estructuras anidadas
Los miembros de una estructura pueden ser ellos mismos, otra estructura previamente
identificada, o bien una estructura anónima. Por ejemplo, en el siguiente fragmento de código,
se crean las estructuras pareja y pareja2. Obsérvese cómo dentro de los miembros de pareja2, se
declara el miembro X, que a su vez es una estructura del tipo pareja. Luego, las variables
declaradas a raíz de la estructura pareja2 poseerán los miembros variables a y b heredados de
pareja, y c.
Herencia
Por ejemplo, en la definición de las estructuras pareja y pareja2 del ejemplo anterior, se dice
que pareja2 hereda por composición todos los miembros de pareja. Ahora, en el siguiente
ejemplo se usa la sintaxis para que la estructura pareja2 herede por extensión los miembros de
pareja:
// solo C++
struct pareja { int a, b ; };
struct pareja2 : pareja { int c; } P3;
Con esta forma de herencia, la estructura pareja2 hereda de pareja los miembros a y b, y
además agrega un miembro c. Y a diferencia del ejemplo anterior, para acceder a alguno de sus
miembros heredados, basta con utilizar el mecanismo de acceso (el punto).
// solo C++
cout << P3.a << P3.b ;
Antes de ver un ejemplo del uso de struct para crear estructuras de campos de bits,
consideremos el caso en donde se tiene una variable del tipo short (16 bits) y que para la misma
se desea que los bits tengan significados específicos. Digamos que el primer bit servirá para
controlar alguna condición; los siguientes cuatro bits, o sea del segundo al quinto bit,
controlarán otra condición; el bit 6 tendrá otra función; y el resto, o sea del séptimo al
decimosexto bit se emplearán para controlar otra condición. De tal manera que si queremos, por
ejemplo, saber si el primer bit de la variable tiene almacenado un 1 o un 0, podemos emplear la
siguiente sintaxis:
int X = 123;
int r = X & 1;
la cosa parece sencilla, pero ahora consideremos el caso en el cual deseamos saber cual es el
valor contenido por el grupo de bits ( segundo al quinto ), entonces nos daremos cuenta que no
basta con una prueba mediante AND ( X & 1 ), sino que hay que realizar otros pasos.
Precisamente, para problemas como el planteado arriba es por los que los lenguajes C y C++
soportan las estructuras de campos de bits. Por ejemplo, la estructura
struct campo_de_bit {
int bit_1 : 1;
int bits_2_a_5 : 4;
int bit_6 : 1;
int bits_7_a_16 : 10;
} bit_var;
El mecanismo de estructuras de campos de bits soportado por C,C++ es una herramienta útil y
poderosa, sobre todo en programación de bajo nivel; ya que mediante el mismo es posible aislar
y dar nombres a todos y cada uno de los bits de un dato, y también crear en un mismo campo
grupos de bits (como se mostró arriba). Ahora bien, no hay que olvidar que la estructura mínima
de información en un sistema de memoria de un PC es el bit, y que para aislar a cada uno de
estos se puede emplear el operador AND ( &, en C ), pero mediante el mecanismo mencionado
el compilador genera los algoritmos necesarios para llevar a cabo el aislamiento de los bits. Por
ejemplo, para escribir y leer bit identificado como bit_1 de la variable bit_var del ejemplo
anterior, podemos emplear las siguientes instrucciones:
bit_var.bit_1 = 1;
printf("%i\n", bit_var.bit_1 );
Nota: acerca de las estructuras de campos de bits hay que aclarar que, aunque cada uno de los
campos de la estructura pueden declararse como enteros con signo o enteros sin signo, la misma
no tendrá una longitud mayor a un entero largo.
De la misma manera que con la orden struct, con la orden union se pueden crear
estructuras con nombre y estructuras sin nombre.
En el ejemplo anterior se declara la variable u1, la cual es una estructura tipo union. El espacio
de almacenamiento para la variable a es compartido por la variable b, en consecuencia, al
escribir sobre cualquiera de estas dos variables se altera el contenido de ambas.
En el ejemplo anterior se declara la variable u1, la cual es una estructura tipo union. El espacio
de almacenamiento para la variable a es compartido por la variable b. Es decir, el compilador
reservará espacio en la memoria para la variable de mayor tamaño (que para éste caso es b ).
Ahora bién, suponiendo que en su equipo el tipo long ocupa 32 bits y que el tipo short ocupa
16 bits, entonces la variable a ocupará solamente los 16 bits menos significativos, mientras que
la variable b ocupará todo el espacio, o sea los 32 bits; Observe que en la sintaxis se ha
especificado el nombre ux, mismo que puede ser empleado para declarar cualquier número de
variables de la union. Por ejemplo, a continuación se declaran las variables u2 y u3 del tipo
union ux creado en el ejemplo anterior.
class: sintaxis
<classkey> <classname> [<:baselist>] { <member list> } [lista de variables] ;
Las clases son algo así como "super" estructuras capaces de agrupar no solo datos miembros
sino también funciones miembros. En el lenguaje común a los datos miembros de una clase se
les conoce como atributos; mientras que a las funciones miembros de una clase se les llama
métodos. Normalmente, los métodos se emplean para leer o escribir los atributos. Es decir, la
norma general es no permitir el acceso directo a los atributos de una clase, con la idea de
aumentar la seguridad de los datos.
En seguida se mostrará el código para crear la clase Pareja, misma que poseerá los atributos a y
b, y los métodos setA(), setB(); getA(), getB(), y mostrar();
class Pareja {
int a, b;
public:
void setA(int n) { a = n; }
void setB(int n) { b = n; }
int getA() { return a; }
int getB() { return b; }
void mostrar() {
cout << "a = " << a << "; b = " << b << endl;
}
} p1;
Nota: por omisión, los miembros de una clase son privados, lo cual significa que los objetos
instanciados de dicha clase no tendrán acceso a los mismos. Así, en el ejemplo anterior se está
creando la clase Pareja, y al mismo tiempo el objeto p1. Luego, para leer o escribir los atributos
de p1 se debe hacer a traves de los métodos definidos con tal objetivo. Por ejemplo, con el
siguiente fragmento de código se establecen respectivamente a 100 y a 200 los atributos a y b; y
posteriormente se despliegan por medio del método mostrar().
p1.setA(100);
p1.setB(200);
p1.mostrar();
Los miembros de una struct son públicos por defecto, mientras que los miembros
de una class son privados por defecto.
Los parámetros-argumentos struct se pasan normalmente por copia, los
parámetros-argumentos class se pasan normalmente por referencia.
#include <iostream>
int a, b;
// constructor base
Par() { a = b = 0; }
// destructor base
~Par() { cout << "hecho..." << endl; }
void setA(int n) { a = n; }
void setB(int n) { b = n; }
void mostrar() {
cout << "a = " << a << ", b = " << b << "; suma = " << a+b << endl;
}
};
// prueba
void test00() {
ParHijo p1(100, 200); // p1 es instancia de ParHijo
p1.mostrar(); // se envía mensaje al método mostrar() de p1
}
// funcion principal
int main()
{
test00();
cin.get();
return 0;
}
Esta página se editó por última vez el 1 may 2020 a las 06:51.
El texto está disponible bajo la Licencia Creative Commons Atribución-CompartirIgual 3.0; pueden aplicarse términos
adicionales. Véase Términos de uso para más detalles.