Tipos de Programación en C
Tipos de Programación en C
Tipos de Programación en C
Constructores [editar]
Véase también: Constructor (informática)
Son unos métodos especiales que se ejecutan automáticamente al crear un objeto de la
clase. En su declaración no se especifica el tipo de dato que devuelven, y poseen el mismo
nombre que la clase a la que pertenecen. Al igual que otros métodos, puede haber varios
constructores sobrecargados, aunque no pueden existir constructores virtuales.
Como característica especial a la hora de implementar un constructor, justo después de la
declaración de los parámetros, se encuentra lo que se llama "lista de inicializadores". Su
objetivo es llamar a los constructores de los atributos que conforman el objeto a construir.
Cabe destacar que no es necesario declarar un constructor al igual que un destructor, pues el
compilador lo puede hacer, aunque no es la mejor forma de programar.
Tomando el ejemplo de la Clase Punto, si deseamos que cada vez que se cree un objeto de
esta clase las coordenadas del punto sean igual a cero podemos agregar un constructor
como se muestra a continuación:
class Punto
{
public:
float x;
float y;
// Constructor
};
int main()
{
Punto MiPunto; // creamos un elemento de la clase Punto
llamado MiPunto
namespace mi_paquete{
int mi_valor;
};
int main()
{
int mi_valor= 3;
mi_paquete::mi_valor= 4;
}
Como puede verse, las invocaciones directas a mi_valor darán acceso solamente a la
variable descrita localmente; para acceder a la variable del espacio de nombres mi_paquete
es necesario acceder específicamente el espacio de nombres. Un atajo recomendado para
programas sencillos es la directiva using namespace, que permite acceder a los nombres
de variables del paquete deseado en forma directa, siempre y cuando no se produzca alguna
ambigüedad o conflicto de nombres.
Excepciones [editar]
C++ permite la existencia de excepciones, las cuales son una metodología de flujo de
ejecución basada en la prueba del código deseado (try) seguida por la intercepción de
ciertas condiciones bajo un flujo de programa adicional (catch). La declaración de estas
condiciones se hace "arrojando" (throw) sentencias especiales que son capturadas por el
flujo catch correspondiente.
Por ejemplo:
# include <iostream>
try {
std::cout<< x.at(99)<<std::endl;
}
catch (std::exception& X) {
std::cerr<< X.what()<<std::endl;
}
return 0;
}
En el ejemplo anterior, se hace el intento de acceder al caracter número 99 de la cadena
"Hola Mundo", el cual no existe. El tipo de datos std::string arroja en estos casos, en la
llamada a la función "at", una excepción, del tipo std::out_of_range, derivado de
std::exception. El bloque catch "atrapará" la excepción arrojada como una variable X,
para la cual el método what() muestra un mensaje con la causa del error (en nuestro caso,
un mensaje similar a "Index Out of Range").
Es buena idea al crear nuevas excepciones derivarlas de std::exception ya que es el
bloque catch que muchos programadores colocan por omisión.
Si una excepción se propagara sin ser atrapada por un bloque catch, y llegara hasta el punto
de terminación del programa, se produce la terminación abrupta de éste ("abort").
Herencia [editar]
Existen varios tipos de herencia entre clases en el lenguaje de programación C++. Estos
son:
Herencia simple [editar]
La herencia en C++ es un mecanismo de abstracción creado para poder facilitar y mejorar
el diseño de las clases de un programa. Con ella se pueden crear nuevas clases a partir de
clases ya hechas, siempre y cuando tengan un tipo de relación especial.
En la herencia, las clases derivadas "heredan" los datos y las funciones miembro de las
clases base, pudiendo las clases derivadas redefinir estos comportamientos (polimorfismo)
y añadir comportamientos nuevos propios de las clases derivadas. Para no romper el
principio de encapsulamiento (ocultar datos cuyo conocimiento no es necesario para el uso
de las clases), se proporciona un nuevo modo de visibilidad de los datos/funciones:
"protected". Cualquier cosa que tenga visibilidad protected se comportará como pública en
la clase Base y en las que componen la jerarquía de herencia, y como privada en las clases
que NO sean de la jerarquía de la herencia.
Antes de utilizar la herencia, nos tenemos que hacer una pregunta, y si tiene sentido,
podemos intentar usar esta jerarquía: Si la frase <claseB> ES-UN <claseA> tiene sentido,
entonces estamos ante un posible caso de herencia donde clase A será la clase base y clase
B la derivada.
Ejemplo: clases Barco, Acorazado, Carguero, etc. un Acorazado ES-UN Barco, un
Carguero ES-UN Barco, un Trasatlántico ES-UN Barco, etc.
En este ejemplo tendríamos las cosas generales de un Barco (en C++)
class Barco {
protected:
char* nombre;
float peso;
public:
//Constructores y demás funciones básicas de barco
};
y ahora las características de las clases derivadas, podrían (a la vez que heredan las de
barco) añadir cosas propias del subtipo de barco que vamos a crear, por ejemplo:
class Carguero: public Barco { // Esta es la manera de especificar que
hereda de Barco
private:
float carga;
//El resto de cosas
};
class Empleado {
Persona jefe;
int sueldo;
Cobrar();
...
};
Ejemplo: f>>hola;
2-Si es un fichero binario(.dat);
nombre_variable_fichero.read((char*)&nombre_variable,sizeof(tipo_variab
le));
Ejemplo:
f.read((char*)&e,sizeof(int));
Escribir un fichero:
1-Si es fichero de texto(.txt):
nombrevariable<<"texto"; donde "texto" puede ser también una variable
de cualquier tipo primitivo, o un string.
Ejemplo: f<<HOLA;
2-Si es un fichero binario(.dat);
nombre_variable_fichero.write((char*)&nombre_variable,sizeof(tipo_varia
ble));
Ejemplo:
f.write((char*)&e,sizeof(int));
Pueden abrirse pasando al constructor los parámetros relativos a la ubicación del fichero y
el modo de apertura:
sstreams [editar]
Se destacan dos clases, ostringstream e istringstream. Todo lo anteriormente dicho es
aplicable a estas clases. Tratan a una cadena como si de un flujo de datos se tratase.
ostringstream permite elaborar una cadena de texto insertando datos cual flujo, e
istringstream puede extraer la información contenida en una cadena (pasada como
parámetro en su constructor) con el operador >>. Ejemplos:
ostringstream s;
s << nombre << "," << edad << "," << estatura << "," << punto(5,6) <<
endl;
cout << s.str();
istringstream s(cadena);
s >> nombre >> edad >> estatura >> p;
Contenedores [editar]
Son clases plantillas especiales utilizadas para almacenar tipos de datos genéricos, sean
cuales sean. Según la naturaleza del almacenado, disponemos de varios tipos:
• Vectores: Se definen por
• vector<tipo_de_dato> nombre_del_vector;
Equivalen a los array de cualquier lenguaje, con diversas salvedades. Tienen tamaño
dinámico, con lo que se puede insertar elementos aún si el vector está lleno. A
diferencia de los vectores clásicos a bajo nivel de C, también pueden lanzar
excepciones si se accede a un elemento cuyo rango está fuera del vector en cuestión,
usando, en vez del operador [], el método at().
• Colas dobles: son parecidas a los vectores, pero tienen mejor eficiencia para agregar
o eliminar elementos en las "puntas".
• Listas.
• Adaptadores de secuencia.
• Contenedores asociativos: map y multimap, que permiten asociar una "clave" con
un "valor".
• Contenedores asociativos: set y multiset, que ofrecen solamente la condición de
"pertenencia", sin la necesidad de garantizar un ordenamiento particular de los
elementos que contienen.
Iteradores [editar]
Pueden considerarse como una generalización de la clase de "puntero". Un iterador es un
tipo de dato que permite el recorrido y la búsqueda de elementos en los contenedores.
Como las estructuras de datos (contenedores) son clases genéricas, y los operadores
(algoritmos) que deben operar sobre ellas son también genéricos (funciones genéricas),
Stepanov y sus colaboradores tuvieron que desarrollar el concepto de iterador como
elemento o nexo de conexión entre ambos. El nuevo concepto resulta ser una especie de
punteros que señalan a los diversos miembros del contenedor (punteros genéricos que como
tales no existen en el lenguaje).
Algoritmos [editar]
Combinando la utilización de templates y un estilo específico para denotar tipos y
variables, la STL ofrece una serie de funciones que representan operaciones comunes, y
cuyo objetivo es "parametrizar" las operaciones en que estas funciones se ven involucradas
de modo que su lectura, comprensión y mantenimiento, sean más fáciles de realizar.
Un ejemplo es la función copy, la cual simplemente copia variables desde un lugar a otro.
Más estrictamente, copia los contenidos cuyas ubicaciones están delimitadas por dos
iteradores, al espacio indicado por un tercer iterador. La sintaxis es:
copy (inicio_origen, fin_origen, inicio_destino);
De este modo, todos los datos que están entre inicio_origen y fin_origen, excluyendo el
dato ubicado en este último, son copiados a un lugar descrito o apuntado por inicio_destino.
Entre las funciones más conocidas están swap (variable1, variable2), que
simplemente intercambia los valores de variable1 y variable2; max (variable1,
variable2) y su símil min (variable1, variable2), que retornan el máximo o mínimo
entre dos valores; find (inicio, fin, valor) que busca valor en el espacio de variables
entre inicio y fin; etcétera.
Los algoritmos son muy variados, algunos incluso tienen versiones específicas para operar
con ciertos iteradores o contenedores, y proveen un nivel de abstracción extra que permite
obtener un código más "limpio", que "describe" lo que se está haciendo, en vez de hacerlo
paso a paso explícitamente.
C++0x [editar]
Artículo principal: C++0x
C++0x es el nombre no oficial del nuevo estandar para el lenguaje de programación C++.
Se planea que reemplace el estandar actual (ISO/IEC 14882), que fue publicado en 1998 y
actualizado en 2003; estas versiones preliminares son conocidas como C++98 y C++03. El
nuevo estandar incluirá adiciones al núcleo del lenguaje y extenderá la biblioteca estandar.
El más reciente bosquejo publicado, fue en junio de 2009.
Diferencias de tipos respecto a C [editar]
En C++, cualquier tipo de datos que sea declarado completo (fully qualified, en inglés) se
convierte en un tipo de datos único. Las condiciones para que un tipo de datos T sea
declarado completo son a grandes rasgos las siguientes:
• Es posible al momento de compilación conocer el espacio asociado al tipo de datos
(es decir, el compilador debe conocer el resultado de sizeof(T)).
• T Tiene al menos un constructor, y un destructor, bien declarados.
• Si T es un tipo compuesto, o es una clase derivada, o es la especificación de una
plantilla, o cualquier combinación de las anteriores, entonces las dos condiciones
establecidas previamente deben aplicar para cada tipo de dato constituyente.
En general, esto significa que cualquier tipo de datos definido haciendo uso de las
cabeceras completas, es un tipo de datos completo.
En particular, y, a diferencia de lo que ocurría en C, los tipos definidos por medio de
struct o enum son tipos completos. Como tales, ahora son sujetos a sobrecarga,
conversiones implícitas, etcétera.
Los tipos enumerados, entonces, ya no son simplemente alias para tipos enteros, sino que
son tipos de datos únicos en C++. El tipo de datos bool, igualmente, pasa a ser un tipo de
datos único, mientras que en C funcionaba en algunos casos como un alias para alguna
clase de dato de tipo entero.