Você está na página 1de 58

Trabajo Práctico

Tema Central: Programación Orientada a Objetos

1- Elegir cuatro lenguajes que implementen programación orientada a objetos

2- Que características se implementan en visual Basic

3- Desarrollar dos paradigmas para compararlo.

Programación orientada a objetos

Los objetos son entidades que combinan estado, comportamiento e identidad:

• El estado está compuesto de datos, será uno o varios atributos a los que se
habrán asignado unos valores concretos (datos).
• El comportamiento está definido por los procedimientos o Método (informática)
con que puede operar dicho objeto, es decir, qué operaciones se pueden
realizar con él.
• La identidad es una propiedad de un objeto que lo diferencia del resto, dicho
con otras palabras, es su identificador (concepto análogo al de identificador de
una variable o una constante).

La programación orientada a objetos expresa un programa como un conjunto de estos


objetos, que colaboran entre ellos para realizar tareas. Esto permite hacer los
programas y módulos más fáciles de escribir, mantener, reutilizar y volver a utilizar.

De aquella forma, un objeto contiene toda la información que permite definirlo e


identificarlo frente a otros objetos pertenecientes a otras clases e incluso frente a
objetos de una misma clase, al poder tener valores bien diferenciados en sus atributos.
A su vez, los objetos disponen de mecanismos de interacción llamados métodos que
favorecen la comunicación entre ellos. Esta comunicación favorece a su vez el cambio
de estado en los propios objetos. Esta característica lleva a tratarlos como unidades
indivisibles, en las que no se separan ni deben separarse el estado y el
comportamiento.

Los métodos (comportamiento) y atributos (estado) están estrechamente relacionados


por la propiedad de conjunto. Esta propiedad destaca que una clase requiere de
métodos para poder tratar los atributos con los que cuenta. El programador debe
pensar indistintamente en ambos conceptos, sin separar ni darle mayor importancia a
ninguno de ellos. Hacerlo podría producir el hábito erróneo de crear clases
contenedoras de información por un lado y clases con métodos que manejen a las
primeras por el otro. De esta manera se estaría realizando una programación
estructurada camuflada en un lenguaje de programación orientado a objetos.

Esto difiere de la programación estructurada tradicional, en la que los datos y los


procedimientos están separados y sin relación, ya que lo único que se busca es el
procesamiento de unos datos de entrada para obtener otros de salida. La
programación estructurada anima al programador a pensar sobre todo en términos de
procedimientos o funciones, y en segundo lugar en las estructuras de datos que esos
procedimientos manejan. En la programación estructurada sólo se escriben funciones
que procesan datos. Los programadores que emplean éste nuevo paradigma, en
cambio, primero definen objetos para luego enviarles mensajes solicitándoles que
realicen sus métodos por sí mismos.

Conceptos fundamentales

• Clase: definiciones de las propiedades y comportamiento de un tipo de objeto


concreto. La instanciación es la lectura de estas definiciones y la creación de
un objeto a partir de ellas.
• Herencia: (por ejemplo, herencia de la clase D a la clase C) Es la facilidad
mediante la cual la clase D hereda en ella cada uno de los atributos y
operaciones de C, como si esos atributos y operaciones hubiesen sido
definidos por la misma D. Por lo tanto, puede usar los mismos métodos y
variables publicas declaradas en C. Los componentes registrados como
"privados" (private) también se heredan, pero como no pertenecen a la clase,
se mantienen escondidos al programador y sólo pueden ser accedidos a través
de otros métodos públicos. Esto es así para mantener hegemónico el ideal de
OOP.
• Objeto: entidad provista de un conjunto de propiedades o atributos (datos) y de
comportamiento o funcionalidad (métodos) los mismos que consecuentemente
reaccionan a eventos. Se corresponde con los objetos reales del mundo que
nos rodea, o a objetos internos del sistema (del programa). Es una instancia a
una clase.
• Método: Algoritmo asociado a un objeto (o a una clase de objetos), cuya
ejecución se desencadena tras la recepción de un "mensaje". Desde el punto
de vista del comportamiento, es lo que el objeto puede hacer. Un método
puede producir un cambio en las propiedades del objeto, o la generación de un
"evento" con un nuevo mensaje para otro objeto del sistema.
• Evento: Es un suceso en el sistema (tal como una interacción del usuario con
la máquina, o un mensaje enviado por un objeto). El sistema maneja el evento
enviando el mensaje adecuado al objeto pertinente. También se puede definir
como evento, a la reacción que puede desencadenar un objeto, es decir la
acción que genera.
• Mensaje: una comunicación dirigida a un objeto, que le ordena que ejecute uno
de sus métodos con ciertos parámetros asociados al evento que lo generó.
• Propiedad o atributo: contenedor de un tipo de datos asociados a un objeto (o a
una clase de objetos), que hace los datos visibles desde fuera del objeto y esto
se define como sus características predeterminadas, y cuyo valor puede ser
alterado por la ejecución de algún método.
• Estado interno: es una variable que se declara privada, que puede ser
únicamente accedida y alterada por un método del objeto, y que se utiliza para
indicar distintas situaciones posibles para el objeto (o clase de objetos). No es
visible al programador que maneja una instancia de la clase.
• Componentes de un objeto:atributos, identidad, relaciones y métodos.
• Representación de un objeto: un objeto se representa por medio de una tabla o
entidad que esté compuesta por sus atributos y funciones correspondientes.

En comparación con un lenguaje imperativo, una "variable", no es más que un


contenedor interno del atributo del objeto o de un estado interno, así como la "función"
es un procedimiento interno del método del objeto.

Características de POO

Hay un cierto acuerdo sobre exactamente qué características de un método de


programación o lenguaje le definen como "orientado a objetos", pero hay un consenso
general en que las características siguientes son las más importantes:

• Abstracción: Denota las características esenciales de un objeto, donde se


capturan sus comportamientos.Cada objeto en el sistema sirve como modelo
de un "agente" abstracto que puede realizar trabajo, informar y cambiar su
estado, y "comunicarse" con otros objetos en el sistema sin revelar cómo se
implementan estas características. Los procesos, las funciones o los métodos
pueden también ser abstraídos y cuando lo están, una variedad de técnicas
son requeridas para ampliar una abstracción.
• Encapsulamiento: Significa reunir a todos los elementos que pueden
considerarse pertenecientes a una misma entidad, al mismo nivel de
abstracción. Esto permite aumentar la cohesión de los componentes del
sistema. Algunos autores confunden este concepto con el principio de
ocultación, principalmente porque se suelen emplear conjuntamente.
• Principio de ocultación: Cada objeto está aislado del exterior, es un módulo
natural, y cada tipo de objeto expone una interfaz a otros objetos que
especifica cómo pueden interactuar con los objetos de la clase. El aislamiento
protege a las propiedades de un objeto contra su modificación por quien no
tenga derecho a acceder a ellas, solamente los propios métodos internos del
objeto pueden acceder a su estado. Esto asegura que otros objetos no pueden
cambiar el estado interno de un objeto de maneras inesperadas, eliminando
efectos secundarios e interacciones inesperadas. Algunos lenguajes relajan
esto, permitiendo un acceso directo a los datos internos del objeto de una
manera controlada y limitando el grado de abstracción. La aplicación entera se
reduce a un agregado o rompecabezas de objetos.
• Polimorfismo: comportamientos diferentes, asociados a objetos distintos,
pueden compartir el mismo nombre, al llamarlos por ese nombre se utilizará el
comportamiento correspondiente al objeto que se esté usando. O dicho de otro
modo, las referencias y las colecciones de objetos pueden contener objetos de
diferentes tipos, y la invocación de un comportamiento en una referencia
producirá el comportamiento correcto para el tipo real del objeto referenciado.
Cuando esto ocurre en "tiempo de ejecución", esta última característica se
llama asignación tardía o asignación dinámica. Algunos lenguajes proporcionan
medios más estáticos (en "tiempo de compilación") de polimorfismo, tales como
las plantillas y la sobrecarga de operadores de C++.
• Herencia: las clases no están aisladas, sino que se relacionan entre sí,
formando una jerarquía de clasificación. Los objetos heredan las propiedades y
el comportamiento de todas las clases a las que pertenecen. La herencia
organiza y facilita el polimorfismo y el encapsulamiento permitiendo a los
objetos ser definidos y creados como tipos especializados de objetos
preexistentes. Estos pueden compartir (y extender) su comportamiento sin
tener que volver a implementarlo. Esto suele hacerse habitualmente agrupando
los objetos en clases y estas en árboles o enrejados que reflejan un
comportamiento común. Cuando un objeto hereda de más de una clase se dice
que hay herencia múltiple.

Recolección de basura: la Recolección de basura o Garbage Collection es la técnica


por la cual el ambiente de Objetos se encarga de destruir automáticamente, y por tanto
desasignar de la memoria, los Objetos que hayan quedado sin ninguna referencia a
ellos. Esto significa que el programador no debe preocuparse por la asignación o
liberación de memoria, ya que el entorno la asignará al crear un nuevo Objeto y la
liberará cuando nadie lo esté usando. En la mayoría de los lenguajes híbridos que se
extendieron para soportar el Paradigma de Programación Orientada a Objetos como
C++ u Object Pascal, esta característica no existe y la memoria debe desasignarse
manualmente.

C++
Características

es un lenguaje de programación diseñado a mediados de los años 1980 por Bjarne


Stroustrup. La intención de su creación fue el extender al exitoso lenguaje de
programación C con mecanismos que permitan la manipulación de objetos. En ese
sentido, desde el punto de vista de los lenguajes orientados a objetos, el C++ es un
lenguaje híbrido.

Posteriormente se añadieron facilidades de programación genérica, que se sumó a los


otros dos paradigmas que ya estaban admitidos (programación estructurada y la
programación orientada a objetos). Por esto se suele decir que el C++ es un lenguaje
de programación multiparadigma.
Una particularidad del C++ es la posibilidad de redefinir los operadores (sobrecarga de
operadores), y de poder crear nuevos tipos que se comporten como tipos
fundamentales.

Al usar la directiva #include estamos diciéndole al compilador que busque


determinadas cosas en un archivo que se llama iostream. Para evitar redefinir cosas
ya hechas al ponerles igual nombre, se creó algo llamado espacios de nombres o
namespace en el singular del inglés. En este caso hay un espacio de nombres llamado
std, que es donde se incluyen las definiciones, entre muchas otras cosas del objeto
cout, que representa el flujo de salida estándar (típicamente la pantalla o una ventana
de texto), y todo esto es exactamente lo que decimos al añadir la sentencia using
namespace std.

La definición de funciones es igual que en C, salvo por la característica de que si main


no va a recoger argumentos, no tenemos por qué ponérselos, a diferencia de C, donde
había que ponerlos explícitamente, aunque no se fueran a usar. Queda solo comentar
que el símbolo << se conoce como operador de inserción, y grosso modo está
enviando a cout lo que queremos mostrar por pantalla para que lo pinte, en este caso
la cadena "¡Hola mundo!" . El mismo operador << se puede usar varias veces en la
misma sentencia, de forma que gracias a esta característica podemos poner un
carácter endl al final, que es el equivalente del \n en C o \n\r, según el sistema en que
se esté programando.

Tipos de datos

C++ tiene los siguientes tipos fundamentales:

• Carácteres: char (también es un entero), wchar_t


• Enteros: short int, int, long int, long long int
• Números en coma floante: float, double, long double
• Booleanos: bool
• Vacío: void

El modificador unsigned se puede aplicar a enteros para obtener números sin signo
(por omisión los enteros contienen signo), con lo que se consigue un rango mayor de
números naturales.

Tamaños Asociados

Según la máquina y el compilador que se Tamaños de tipos primitivos bajo i386 (GCC)
utilice los tipos primitivos pueden ocupar un Tipo Número de Bits
determinado tamaño en memoria. La char 8
siguiente lista ilustra el número de bits que
short 16
ocupan los distintos tipos primitivos la
arquitectura x86. int 32
float 32
Otras arquitecturas pueden requerir distintos double 64
tamaños de tipos de datos primitivos. C++ no dice nada acerca de cuál es el número
de bits en un byte, ni del tamaño de estos tipos; más bien, ofrece solamente las
siguientes "garantías de tipos":

• Un tipo char tiene el tamaño mínimo en bytes asignable por la máquina, y todos
los bits de este espacio deben ser "accesibles".
• El tamaño reconocido de char es de 1. Es decir, sizeof(char) siempre devuelve
1.
• Un tipo short tiene al menos el mismo tamaño que un tipo char.
• Un tipo long tiene al menos el doble tamaño en bytes que un tipo short.
• Un tipo int tiene un tamaño entre el de short y el de long, ambos inclusive,
preferentemente el tamaño de un apuntador de memoria de la máquina.
• Un tipo unsigned tiene el mismo tamaño que su versión signed.

wchar_t

Para la versión del estándar que se publicó en 1999, se decidió añadir el tipo de dato
wchar_t, que permite el uso de caracteres UNICODE, a diferencia del tradicional char,
que contempla simplemente al código de caracteres ASCII extendido. A su vez, se ha
definido para la mayoría de las funciones y clases, tanto de C como de C++, una
versión para trabajar con wchar_t, donde usualmente se prefija el carácter w al nombre
de la función (en ocasiones el carácter es un infijo). Por ejemplo:

• strcpy - wstrcpy
• std::string - std::wstring
• std::cout - std::wcout

Cabe resaltar que en C se define wchar_t como:

typedef unsigned short wchar_t;

Mientras que en C++ es en sí mismo un tipo de dato.

La Palabra Clave "void"

La palabra clave void define en C++ el concepto de no existencia o no atribución de un


tipo en una variable o declaración. Como tal, puede ser usada para destacar que una
función no recibe parámetros, como en:

int funcion (void);

, aunque la tendencia actual es la de no colocar la palabra "void".

Además se utiliza para determinar que una función no retorna un valor, como en:

void funcion (int parametro);

Cabe destacar que void no es un tipo. Una función como la declarada anteriormente
no puede retornar un valor por medio de return: la palabra clave va sola. No es posible
una declaración del tipo:

void t;

En este sentido, void se comporta de forma ligeramente diferente a como lo hace en


C, especialmente en cuanto a su significado en declaraciones y prototipos de
funciones.
Sin embargo, la forma especial void * puede utilizarse como un ajuste que convierte
cualquier variable a una "variable sin tipo", la cual puede solo ser accedida y utilizada
bajo una operación de cast. Por ejemplo:

void *memoria;

Indica que memoria es un puntero a alguna parte, donde se guarda información de


algún tipo. El programador es responsable de definir estos "algún", eliminando toda
ambigüedad. Una ventaja de la declaración "void *" es que puede representar a la vez
varios tipos de datos, dependiendo de la operación de cast escogida. La memoria que
hemos apuntado en alguna parte, en el ejemplo anterior, bien podría almacenar un
entero, un flotante, una cadena de texto o un programa, o combinaciones de éstos. Es
responsabilidad del programador recordar qué tipo de datos hay y garantizar el acceso
adecuado.

Principios

Todo programa en C++ debe tener la función main() (a no ser que se especifique en
tiempo de compilación otro punto de entrada, que en realidad es la función que tiene el
main())

int main()
{}

La función main debe tener uno de los siguientes prototipos:


int main()
int main(int argc, char** argv)

La primera es la forma por omisión de un programa que no recibe parámetros ni


argumentos. La segunda forma tiene dos parámetros: argc, un número que describe el
número de argumentos del programa (incluyendo el nombre del programa mismo), y
argv, un puntero a un array de punteros, de argc elementos, donde el elemento argv[i]
representa el i-ésimo argumento entregado al programa.

El tipo de retorno de main es int. Al finalizar la función main, debe incluirse el valor de
retorno (por ejemplo, return 0;, aunque el estándar prevé solamente dos posibles
valores de retorno: EXIT_SUCCESS y EXIT_ERROR, definidas en el archivo cstddef),
o salir por medio de la función exit. Alternativamente puede dejarse en blanco, en cuyo
caso el compilador es responsable de agregar la salida adecuada.

El Concepto de Clase

Los objetos en C++ son abstraídos mediante una Clase. Según el paradigma de la
programación orientada a objetos un objeto consta de:

1. Métodos o funciones
2. Atributos o Variables Miembro

Un ejemplo de clase que podemos tomar es la clase perro. Cada perro comparte unas
características (atributos). Su número de patas, el color de su pelaje o su tamaño son
algunos de sus atributos. Las funciones que lo hagan ladrar, cambiar su
comportamiento... esas son las funciones de la clase.

Este es otro ejemplo de una clase:

class Punto
{
//por omisión los miembros son 'private' para que sólo se puedan modificar desde la
propia clase.
private:
// Variable miembro privada
int id;
protected:
// Variables miembro protegidas
int x;
int y;
public:
// Constructor
Punto();
// Destructor
~Punto();
// Funciones miembro o métodos
int ObtenerX();
int ObtenerY();
};

Constructores

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:

// Coordenadas del punto

float x;
float y;
// Constructor

Punto() : x ( 0 ), y ( 0 ) // Inicializamos las variables "x" e "y"


{}

};

// Main para demostrar el funcionamiento de la clase

# include <iostream> // Esto nos permite utilizar "cout"

using namespace std;

int main()
{
Punto MiPunto; // creamos un elemento de la clase Punto llamado MiPunto

cout << "Coordenada X:" << MiPunto.x << endl; // mostramos el valor acumulado
en la variable x
cout << "Coordenada Y:" << MiPunto.y << endl; // mostramos el valor acumulado
en la variable y
return 0;
}

Si compilamos y ejecutamos el anterior programa, obtenemos una salida que debe ser
similar a la siguiente:

Coordenada X:0 Coordenada Y:0

Existen varios tipos de constructores en C++:

1. Constructor predeterminado. Es el constructor que no recibe ningún parámetro


en la función. Si no se definiera ningún constructor, el sistema proporcionaría
uno predeterminado. Es necesario para la construcción de estructuras y
contenedores de la STL.
2. Constructor de copia. Es un constructor que recibe un objeto de la misma
clase, y realiza una copia de los atributos del mismo. Al igual que el
predeterminado, si no se define, el sistema proporciona uno.
3. Constructor de conversión. Este constructor, recibe como único parámetro, un
objeto o variable de otro tipo distinto al suyo propio. Es decir, convierte un
objeto de un tipo determinado a otro objeto del tipo que estamos generando.

Destructores

Los destructores son funciones miembro especiales llamadas automáticamente en la


ejecución del programa, y por tanto no tienen por qué ser llamadas explícitamente por
el programador. Su cometido es liberar los recursos computacionales que el objeto de
dicha clase haya adquirido en tiempo de ejecución al expirar este.

Los destructores son invocados automáticamente al alcanzar el flujo del programa el


fin del ámbito en el que está declarado el objeto.
Existen dos tipos de destructores pueden ser públicos o privados, según si se
declaran:

• si es publico se llama desde cualquier parte del programa para destruir el


objeto.

• si es privado no se permite la destrucción del objeto por el usuario.

Funciones Miembro

Función miembro es aquella que está declarada en ámbito de clase. Son similares a
las funciones habituales, con la salvedad de que el compilador realizara el proceso de
Decoración de nombre (Name Mangling en inglés): Cambiara el nombre de la función
añadiendo un identificador de la clase en la que está declarada, pudiendo incluir
caracteres especiales o identificadores numéricos. Además, las funciones miembro
reciben implícitamente un parámetro adicional: El puntero this, que referencia al objeto
que ejecuta la función.

Las funciones miembro se invocan accediendo primero al objeto al cual refieren, con la
sintaxis: myobject.mymemberfunction(), esto es un claro ejemplo de una función
miembro.

Plantillas

Las plantillas son el mecanismo de C++ para implantar el paradigma de la


programación genérica. Permiten que una clase o función trabaje con tipos de datos
abstractos, especificándose más adelante cuales son los que se quieren usar. Por
ejemplo, es posible construir un vector genérico que pueda contener cualquier tipo de
estructura de datos. De esta forma se pueden declarar objetos de la clase de este
vector que contengan enteros, flotantes, polígonos, figuras, fichas de personal, etc.

La declaración de una plantilla se realiza anteponiendo la declaración template


<typename A,....> a la declaración de la estructura (clase, estructura o función)
deseado.

Por ejemplo:

template <typename T>


T max(T x, T y)
{
if (x > y)
return x;
else
return y;
}

La función max() es un ejemplo de programación genérica, y dados dos parámetros de


un tipo T (que puede ser int, long, float, double, etc.) devolverá el mayor de ellos
(usando el operador >). Al ejecutar la función con parámetros de un cierto tipo, el
compilador intentará "calzar" la plantilla a ese tipo de datos, o bien generará un
mensaje de error si fracasa en ese proceso.

Especialización
El siguiente ejemplo:

template <typename A> int myfunction(A a);

crea una plantilla bajo la cual pueden ser definidas en el código de cabecera
cualesquiera funciones especializadas para un tipo de datos como int myfunction(int),
int myfunction(std::string), int myfunction(bool), etcétera:

int myfunction (int a) {


return a + 5;
};

int myfunction (std::string a) {


return -a.size();
};

int myfunction (bool a) {


return (a & rand());
};

Cada una de estas funciones tiene su propia definición (cuerpo). Cada cuerpo
diferente, no equivalente ("no convertible") corresponde a una especialización. Si una
de estas funciones no fuera definida, el compilador tratará de aplicar las conversiones
de tipos de datos que le fuesen permitidas para "calzar" una de las plantillas, o
generará un mensaje de error si fracasa en ese proceso.

Todas las definiciones habilitadas de una plantilla deben estar disponibles al momento
de la compilación, por lo cual no es posible actualmente "compilar" una plantilla como
archivo de objeto, sino simplemente compilar especializaciones de la plantilla. Por lo
tanto, las plantillas se distribuyen junto con el código fuente de la aplicación. En otras
palabras, no es posible compilar la plantilla std::vector< > a código objeto, pero sí es
posible, por ejemplo, compilar un tipo de datos std::vector<std::string>.

Clases Abstractas

En C++ es posible definir clases abstractas. Una clase abstracta, o clase base
abstracta (ABC), es una que está diseñada sólo como clase padre de las cuales se
deben derivar clases hijas. Una clase abstracta se usa para representar aquellas
entidades o métodos que después se implementarán en las clases derivadas, pero la
clase abstracta en sí no contiene ninguna implementación -- solamente representa los
métodos que se deben implementar. Por ello, no es posible instanciar una clase
abstracta, pero sí una clase concreta que implemente los métodos definidos en ella.

Las clases abstractas son útiles para definir interfaces, es decir, un conjunto de
métodos que definen el comportamiento de un módulo determinado. Estas definiciones
pueden utilizarse sin tener en cuenta la implementación que se hará de ellos.

En C++ los métodos de las clases abstractas se definen como funciones virtuales
puras.

class Abstracta
{
public:
virtual int metodo() = 0;
};

class ConcretaA : public Abstracta


{
public:
int metodo()
{
//haz algo
return foo () + 2;
}
};

class ConcretaB : public Abstracta


{
public:
int metodo()
{
//otra implementación
return baz () - 5;
}
};

En el ejemplo, la clase ConcretaA es una implementación de la clase Abstracta, y la


clase ConcretaB es otra implementación. Debe notarse que el = 0 es la notación que
emplea C++ para definir funciones virtuales puras.

Espacios de Nombres

Una adición a las características de C son los espacios de nombre (namespace en


inglés), los cuales pueden describirse como áreas virtuales bajo las cuales ciertos
nombres de variable o tipos tienen validez. Esto permite evitar las ocurrencias de
conflictos entre nombres de funciones, variables o clases.

El ejemplo más conocido en C++ es el espacio de nombres std::, el cual almacena


todas las definiciones nuevas en C++ que difieren de C (algunas estructuras y
funciones), así como las funcionalidades propias de C++ (streams) y los componentes
de la biblioteca STL.

Por ejemplo:

# include <iostream>
// Las funciones en esta cabecera existen dentro del espacio de nombres std::

namespace mi_paquete{
int mi_valor;
};

int main()
{
int mi_valor= 3;
mi_paquete::mi_valor= 4;

std::cout<< mi_valor<< '\n'; // imprime '3'


std::cout<< mi_paquete::mi_valor<< '\n'; // imprime '4'
return 0;

Como puede verse, las invocaciones directas a mi_valor darán acceso solamente a la
variable descrita localmente; para acceder a la variable del paquete 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

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>

// Muestra el uso de try y catch

int main()
{
std::string x = "Hola Mundo";

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 propaga 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
Existen varios tipos de herencia entre clases en el lenguaje de programación C++.
Estos son:

Herencia Simple

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 Acorazado: public Barco {


private:
int numeroArmas;
int Soldados;
// Elresto de cosas
};
Por último, hay que mencionar que existen 3 clases de herencia que se diferencian en
el modo de manejar la visibilidad de los componentes de la clase resultante:

• Herencia publica (class Derivada: public Base ) : Con este tipo de herencia se
respetan los comportamientos originales de las visibilidades de la clase Base
en la clase Derivada.
• Herencia privada (clase Derivada: private Base) : Con este tipo de herencia
todo componente de la clase Base, será privado en la clase Derivada (ojo!
siempre será privado aunque ese dato fuese público en la clase Base)
• Herencia protegida (clase Derivada: protected Base) : Con este tipo de
herencia, todo componente publico y protegido de la clase Base, será protegido
en la clase Derivada, y los componentes privados, siguen siendo privados.

Herencia Múltiple

La herencia múltiple es el mecanismo que permite al programador hacer clases


derivadas a partir, no de una sola clase base, sino de varias. Para entender esto
mejor, pongamos un ejemplo: Cuando ves a quien te atiende en una tienda, como
persona que es, podrás suponer que puede hablar, comer, andar, pero, por otro lado,
como empleado que es, también podrás suponer que tiene un jefe, que puede cobrarte
dinero por la compra, que puede devolverte el cambio, etc. Si esto lo trasladamos a la
programación sería herencia múltiple (clase empleado_tienda):

class Persona {
...
Hablar();
Caminar();
...
};

class Empleado {
Persona jefe;
int sueldo;
Cobrar();
...
};

class empleado_tienda: public Persona, Empleado {


...
AlmacenarStock();
ComprobarExistencias();
...
};

Por tanto, es posible utilizar más de una clase para que otra herede sus
características.

Sobrecarga de Operadores

La sobrecarga de operadores es una forma de hacer polimorfismo. Es posible definir el


comportamiento de un operador del lenguaje para que trabaje con tipos de datos
definidos por el usuario. No todos los operadores de C++ son factibles de sobrecargar,
y, entre aquellos que pueden ser sobrecargados, se deben cumplir condiciones
especiales. En particular, los operadores sizeof y :: no son sobrecargables.
No es posible en C++ crear un operador nuevo.

Los comportamientos de los operadores sobrecargados se implementan de la misma


manera que una función, salvo que esta tendrá un nombre especial: Tipo de dato de
devolución operator<token del operador>(parámetros)

Los siguientes operadores pueden ser sobrecargados:

• Operadores Unarios
o Operador * (de indirección)
o Operador -> (de indirección)
o Operador +
o Operador -
o Operador ++
o Operador --
• Operadores Binarios
o Operador ==
o Operador +
o Operador -
o Operador *
o Operador /
o Operador %
o Operador <<
o Operador >>
o Operador &
o Operador ^
o Operador |
o Operador []
o Operador ()
• Operadores de Asignación
o Operador =
o Operador +=
o Operador -=
o Operador *=
o Operador /=
o Operador %=
o Operador <<=
o Operador >>=
o Operador &=
o Operador ^=
o Operador |=

Dado que estos operadores son definidos para un tipo de datos definido por el usuario,
éste es libre de asignarles cualquiera semántica que desee. Sin embargo, se
considera de primera importancia que las semánticas sean tan parecidas al
comportamiento natural de los operadores como para que el uso de los operadores
sobrecargados sea intuitivo. Por ejemplo, el uso del operador unario - debiera cambiar
el "signo" de un "valor".

Los operadores sobrecargados no dejan de ser funciones, por lo que pueden devolver
un valor, si este valor es del tipo de datos con el que trabaja el operador, permite el
encadenamiento de sentencias. Por ejemplo, si tenemos 3 variables A,B y C de un tipo
T y sobrecargamos el operador = para que trabaje con el tipo de datos T, hay dos
opciones: si el operador no devuelve nada una sentencia como "A=B=C;" (sin las
comillas) daría error, pero si se devuelve un tipo de datos T al implementar el
operador, permitiría concatenar cuantos elementos se quisieran, permitiendo algo
como "A=B=C=D=...;"

Biblioteca Estándar de Plantillas (STL, Standard Template Library)

Los lenguajes de programación suelen tener una serie de bibliotecas de funciones


integradas para la manipulación de datos a nivel más básico. En C++, además de
poder usar las bibliotecas de C, se puede usar la nativa STL (Standard Template
Library), propia del lenguaje. Proporciona una serie plantillas (templates) que permiten
efectuar operaciones sobre el almacenado de datos, procesado y flujos de
entrada/salida.

Biblioteca de entrada y salida

Las clases basic_ostream y basic_stream, y los objetos cout y cin, proporcionan la


entrada y salida estándar de datos (teclado/pantalla). También está disponible cerr,
similar a cout, usado para la salida estándar de errores. Estas clases tienen
sobrecargados los operadores << y >>, respectivamente, con el objeto de ser útiles en
la inserción/extracción de datos a dichos flujos. Son operadores inteligentes, ya que
son capaces de adaptarse al tipo de datos que reciben, aunque tendremos que definir
el comportamiento de dicha entrada/salida para clases/tipos de datos definidos por el
usuario. Por ejemplo:

ostream& operator<<(ostream& fs,const Punto& punto)


{
fs << punto.x << "," << punto.y;
return fs;
}

De esta forma, para mostrar un punto, solo habría que realizar la siguiente expresión:

//...
Punto p(4,5);
//...
cout << "Las coordenadas son: " << p << endl;
//...

Es posible formatear la entrada/salida, indicando el número de dígitos decimales a


mostrar, si los textos se pasarán a minúsculas o mayúsculas, si los números recibidos
están en formato octal o hexadecimal, etc.

fstreams

Tipo de flujo para el manejo de ficheros. La definición previa de ostreams/istreams es


aplicable a este apartado. Existen tres clases (ficheros de lectura, de escritura o de
lectura/escritura): ifstream,ofstream y fstream.
Como abrir un fichero:
(nombre_variable_fichero).open("nombre_fichero.dat/txt",ios::in); para abrirlo en modo
lectura. (nombrevariablefichero).open("nombre_fichero.dat/txt",ios::out); para abrirlo en
modo escritura.

Ejemplo: f.open("datos.txt",ios::in);

Como cerrar el fichero: nombre_variable_fichero.close();

Ejemplo: f.close();

Leer un fichero:

1-Si es fichero de texto(.txt):


nombrevariable>>"texto";

Ejemplo: f>>HOLA;
2-Si es un fichero binario(.dat);
nombre_variable_fichero.read((char*)&nombre_variable,sizeof(tipo_variable));
Ejemplo:
f.read((char*)&e,sizeof(int));

Escribir un fichero:

1-Si es fichero de texto(.txt):


nombrevariable<<"texto";

Ejemplo: f<<HOLA;
2-Si es un fichero binario(.dat);
nombre_variable_fichero.write((char*)&nombre_variable,sizeof(tipo_variable));
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

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

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

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

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 e fin_origen, exclusive 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.

Diferencias de Tipos respecto de C

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.

Compiladores

Uno de los compiladores libres de C++ es el de GNU, el compilador G++ (parte del
proyecto Gcc, que engloba varios compiladores para distintos lenguajes),
C #.
Caracteristicas

(pronunciado si sharp en inglés) es un lenguaje de programación orientado a objetos


desarrollado y estandarizado por Microsoft como parte de su plataforma .NET, que
después fue aprobado como un estándar por la ECMA e ISO.

Su sintaxis básica deriva de C/C++ y utiliza el modelo de objetos de la plataforma.NET


el cual es similar al de Java aunque incluye mejoras derivadas de otros lenguajes
(entre ellos Delphi).

La creación del nombre del lenguaje, C♯, proviene de dibujar dos signos positivos
encima de los dos signos positivos de "C++", queriendo dar una imagen de salto
evolutivo del mismo modo que ocurrió con el paso de C a C++.

C♯, como parte de la plataforma.NET, está normalizado por ECMA desde diciembre de
2001 (ECMA-334 "Especificación del lenguaje C♯"). El 7 de noviembre de 2005 salió la
versión 2.0 del lenguaje que incluía mejoras tales como tipos genéricos, métodos
anónimos, iteradores, tipos parciales y tipos anulables. El 19 de noviembre de 2007
salió la versión 3.0 de C# destacando entre las mejoras los tipos implícitos, tipos
anónimos y LINQ (Language Integrated Query -consulta integrada en el lenguaje).

Aunque C♯ forma parte de la plataforma.NET, ésta es una interfaz de programación de


aplicaciones (API); mientras que C♯ es un lenguaje de programación independiente
diseñado para generar programas sobre dicha plataforma. Ya existe un compilador
implementado que provee el marco de DotGNU - Mono que genera programas para
distintas plataformas como Win32, UNIX y Linux.

Tipos de datos

C♯ contiene dos categorías generales de tipos de datos integrados: tipos de valor y


tipos de referencia. El término tipo de valor indica que esos tipos contienen
directamente sus valores.

C♯ define ocho tipos de enteros, a saber:

Tipo de datos de enteros


Ancho en
Tipo Rango Significado
bits
byte 8 De 0 a 255 Entero sin signo de
8 bits
Entero con signo de
sbyte 8 De -128 a 127
8 bits
short 16 De -32.768 a 32.767 Entero corto
Entero corto sin
ushort 16 De 0 a 65.535
signo
int 32 De -2.147.483.648 a 2.147.483.647 Entero medio
Entero medio sin
uint 32 De 0 a 4.294.967.295
signo
De -9.223.372.036.854.775.808 a
long 64 Entero largo
9.223.372.036.854.775.807
Entero largo sin
ulong 64 De 0 a 18.446.744.073.709.551.615
signo

Los tipos de punto flotante pueden representar números con componentes


fraccionales. Existen dos clases de tipos de punto flotante; float y double. El tipo double
es el más utilizado porque muchas funciones matemáticas de la biblioteca de clases de
C♯ usan valores double. Quizá, el tipo flotante más interesante de C♯ es decimal,
dirigido al uso de cálculos monetarios. La aritmética de punto flotante normal está
sujeta a una variedad de errores de redondeo cuando se aplica a valores decimales. El
tipo decimal elimina estos errores y puede representar hasta 28 lugares decimales.

Tipo de datos de punto flotante


Tipo Ancho en bits Rango Significado
float 32 De 1,5E-45 a 3,4E+38 Punto flotante corto
double 64 De 5E-324 a 1,7E+308 Punto flotante largo
decimal 128 De 1E-28 a 7,9E+28 Punto flotante monetario

Los caracteres en C♯ no son cantidades de 8 bits como en otros muchos lenguajes de


programación. Por el contrario, C♯ usa un tipo de caracteres de 16 bits llamado
Unicode al cual se le llama char. No existen conversiones automáticas de tipo entero a
char.

Tipo de datos de carácteres


Tipo Ancho en bits Rango Significado
char 16 De 0 a 65,535 (código Unicode) Carácter
Tipo de datos lógicos
Ancho en
Tipo Rango Significado
bits
true or false, no se usa 1 ó 0 ya que no hay conversión
bool 1 true or false
definida

No existe una conversión definida entre bool y los valores enteros (1 no se convierte a
verdadero ni 0 se convierte a falso).

Constantes
Las constantes en C♯ se denominan literales. Todas las constantes tienen un tipo de
dato, en caso de ser una constante entera se usa la de menor tamaño que pueda
alojarla, empezando por int. En caso de punto flotante se considera como un double.
Sin embargo se puede especificar explícitamente el tipo de dato que una constante
deberá usar, por medio de los sufijos:

Sufijo Tipo de dato Ejemplo


L long 12L
UL ulong 68687UL
F float 10,19F
M decimal 9,95M

En ocasiones, resulta más sencillo usar un sistema numérico basado en 16 en lugar de


10, para tal caso C♯ permite especificar constantes enteras en formato hexadecimal, y
se hace empezando con 0x. Por ejemplo: 0xFF equivale a 255 en decimal.

C♯ tiene carácteres denominados secuencias de escape para facilitar la escritura con el


teclado de símbolos que carecen de representación visual. Estos son:

Secuencia de escape Descripción


\a Alerta (timbre)
\b Retroceso
\f Avance de página
\n Nueva línea
\r Retorno de carro
\t Tabulador horizontal
\v Tabulador vertical
\0 Nulo
\' Comilla sencilla
\" Comilla doble
\\ Diagonal invertida

C♯, al igual que C++, es compatible con el tipo de constante cadena de caracteres.
Dentro de la cadena de caracteres se pueden usar secuencias de escape. Una cadena
de caracteres puede iniciarse con el símbolo @ seguido por una cadena entre comillas,
en tal caso, las secuencias de escape no tienen efecto y además la cadena puede
ocupar dos o más líneas.

Variables

Toda variable se debe declarar antes de ser utilizada. La forma en que se declara una
variable en C♯ es la siguiente:

tipo nombre_variable;

Para asignar un valor a una variable:


nombre_variable = valor

Las conversiones de tipo de variables en C♯ se representan en la siguiente tabla en


donde la fila es el origen y la columna el destino. Los significados de las letras son: A
(Conversión automática o implícita), E (Conversión explícita), I (Conversión
incompatible).

Conversiones de tipo de datos


byte sbyte short ushort int uint long ulong float double decimal char bool
byte E A A A A A A E E E E I
sbyte E A E A E A A E E E E I
short E E E A A A A E E E E I
ushort E E E A A A A E E E E I
int E E E E E A A E E E E I
uint E E E E E A A E E E E I
long E E E E E E E E E E E I
ulong E E E E E E E E E E E I
float E E E E E E E E A E I I
double E E E E E E E E E E I I
decimal E E E E E E E E E E I I
char E E E A A A A A A A A I
bool I I I I I I I I I I I I

• Toda conversión implícita no ocasiona pérdida de información, truncamientos o


redondeos.
• Es posible (mas no siempre ocurre) que en una conversión explícita haya
pérdida de información, truncamientos o redondeos.
• En toda conversión implícita el tipo de dato destino es mayor que el tipo de dato
origen.
• La conversión explícita se realiza de la siguiente forma: (tipo-destino) expresion.

Además de realizarse dentro de una asignación, las conversiones de tipos también


tienen lugar dentro de una expresión, pues en cada operación ambos operandos deben
de ser del mismo tipo. Si la conversión es del tipo implícito se efectúa el siguiente
algoritmo en dicho orden:

1. Si un operando es decimal, el otro operando se transforma a decimal.


2. Si un operando es double, el otro operando se transforma a double.
3. Si un operando es float, el otro operando se transforma a float.
4. Si un operando es ulong, el otro operando se transforma a ulong.
5. Si un operando es long, el otro operando se transforma a long.
6. Si un operando es uint, y si el otro operando es de tipo sbyte, short o int, los dos
se transforman a long.
7. Si un operando es uint, el otro operando se transforma a uint.
8. Si ninguno de los casos anteriores, los dos operandos se transforman a int.

Operadores
C♯ tiene cuatro clases generales de operadores: aritméticos, a nivel de bit, relacionales
y lógicos.

Operadores
Operador Significado Tipo
+ Suma Aritmético
- Resta Aritmético
* Producto Aritmético
/ División Aritmético
% Módulo (residuo entero) Aritmético
++ Incremento Aritmético
-- Decremento Aritmético
== Igual que Relacional
!= Distinto que Relacional
> Mayor que Relacional
< Menor que Relacional
>= Mayor o igual que Relacional
<= Menor o igual que Relacional
& AND Lógico y a nivel de bits
| OR Lógico y a nivel de bits
^ XOR y de nivel de bits Lógico
|| OR de cortocircuito Lógico
&& AND de cortocircuito Lógico
! NOT Lógico
~ Complemento a uno A nivel de bits
<< Desplazamiento a la izquierda A nivel de bits
>> Desplazamiento a la derecha A nivel de bits

• Los operadores aritméticos funcionan igual que en C y C++.


• El resultado de los operadores relacionales y lógicos es un valor bool.
• Los operadores de cortocircuito evalúan el segundo operando solo cuando es
necesario.
• Los operadores a nivel de bit no se pueden aplicar a tipos bool, float, double o
decimal.

Instrucciones de control

• La instrucción if-else es básicamente igual que en C, C++ y Java.


• La diferencia de la instrucción switch con la versión de C, C++ y Java es que
todo cuerpo perteneciente a un case debe de toparse con un break o un goto
antes de toparse con otro case, a menos que dicho cuerpo esté vacío.
• La instrucción for es básicamente igual que en C, C++ y Java.
• La instrucción while es básicamente igual que en C, C++ y Java.
• La instrucción do-while es básicamente igual que en C, C++ y Java.
• La instrucción foreach realiza un ciclo a través de los elementos de una
colección (grupo de objetos). El formato de esta instrucción es: foreach(tipo
variable in coleccion) instruccion;. En este ciclo se recorre la colección y la
variable recibe un respectivo elemento de dicha colección en cada iteración.
• Al igual que en C y C++, la instrucción break permite forzar la salida de un ciclo
omitiendo el código restante en el cuerpo del ciclo.
• Al igual que en C y C++, la instrucción continue permite forzar la repetición
temprana de un ciclo omitiendo el código restante en el cuerpo del ciclo.
• La instrucción return es básicamente igual que en C, C++. Se utiliza para
devolver un valor y salir de un método.
• La instrucción goto se sigue utilizando en C♯ a pesar de toda la polémica que
esto conlleva.

Métodos

Todo método debe de ser parte de una clase, no existen métodos globales.

• De forma predeterminada, los parámetros se pasan por valor (se copia dicho
valor).
• El modificador ref fuerza a pasar los parámetros por referencia en vez de
pasarlos por valor.
• El modificador out es similar al modificador ref con una excepción: sólo se
puede utilizar para pasar un valor fuera de un método. El método debe de
asignar un valor al parámetro antes de que el método finalice.
• Cuando ref y out modifican un parámetro de referencia, la propia referencia se
pasa por referencia.
• El modificador params sirve para definir un número variable de argumentos los
cuales se implementan como una matriz. Ejemplo: public int maxVal(params
int[] nums){...}, esta función se podría llamar así: maxVal(23,3,a,-12);.
• Un método debe tener como máximo un único parámetro params y éste debe
de ser el último.
• Un método puede devolver cualquier tipo de datos, incluyendo tipos de clase.
• Ya que en C# las matrices se implementan como objetos, un método también
puede devolver una matriz (algo que se diferencia de C++ en que las matrices
no son válidas como tipos de valores devueltos).
• C♯ implementa sobrecarga de métodos, dos o más métodos pueden tener el
mismo nombre siempre y cuando se diferencien por sus parámetros.
• El método Main es un método especial al cual se refiere el punto de partida del
programa. Tiene la siguiente sintaxis: public static int Main(string[] args){...}.

Clases y objetos

Varios puntos a tener en cuenta en C♯ con respecto a clases y objetos son los
siguientes:

• Una variable de objeto de cierta clase no almacena los valores del objeto sino
su referencia (al igual que Java).
• El operador de asignación no copia los valores de un objeto, sino su referencia
a él (al igual que Java).
• Un constructor tiene el mismo nombre que su clase y es sintácticamente similar
a un método.
• Un constructor no devuelve ningún valor.
• Al igual que los métodos, los constructores también pueden ser sobrecargados.
• Si no se especifica un constructor en una clase, se usa uno por defecto que
consiste en asignar a todas las variables el valor de 0, null o false según
corresponda.
• Para crear un nuevo objeto se utiliza la siguiente sintaxis: variable = new
nombre_clase();.
• Un destructor se declara como un constructor, aunque va precedido por un
signo de tilde ~.
• Se emplea una desasignación de memoria de objetos no referenciados
(recolección de basura), y cuando esto ocurre se ejecuta el destructor de dicha
clase.
• El destructor de una clase no se llama cuando un objeto sale del ámbito.
• Todos los destructores se llamarán antes de que finalice un programa.
• La palabra clave this es un apuntador al mismo objeto en el cual se usa.
• La palabra clave static hace que un miembro pertenezca a una clase en vez de
pertener a objetos de dicha clase. Se puede tener acceso a dicho miembro
antes de que se cree cualquier objeto de su clase y sin referencias a un objeto.
• Un método static no tiene una referencia this.
• Un método static puede llamar sólo a otros métodos static.
• Un método static sólo debe tener acceso directamente a datos static.
• Un constructor static se usa para inicializar atributos que se aplican a una clase
en lugar de aplicarse a una instancia.
• C♯ permite la sobrecarga de operadores con la palabra clave operator.

Matrices

En C♯ las matrices se implementan como objetos.

• Para crear una matriz se utiliza el siguiente formato: tipo[] nombre_matriz = new
tipo[tamaño];
• Se puede crear una matriz inicializada así: tipo[] nombre_matriz = { val1 , val2 ,
val3 , ... , valN };
• Los índices de las matrices comienzan en 0.
• Para crear una matriz bidimensional se utiliza el siguiente formato: tipo[,]
nombre_matriz = new tipo[filas,columnas]
• Para referirse a un elemento de una matriz bidimensional no se usa la forma
matriz[fila][columna] (la cual usa C++), si no matriz[fila,columna].
• Ya que C♯ implementa matrices como objetos, cada matriz tiene asociada una
propiedad Length que contiene el número de elementos que puede alojar cada
matriz.

Cadenas de carácteres

El tipo de dato cadena se llama string.

• Realmente la palabra clave string es un alias de la clase System.String de la


plataforma .NET.
• En C♯ las cadenas son objetos y no una matriz de caracteres, aun así, se
puede obtener un carácter arbitrario de una cadena por medio de su índice
(mas no modificarlo).
• La forma más común de construir una cadena es por medio de una literal o
constante: string str = "Una cadena";
• El operador == determina si dos referencias hacen referencia al mismo objeto,
pero al usar dicho operador con dos operandos tipo string se prueba la igualdad
del contenido de las cadenas y no su referencia. Sin embargo, con el resto de
los operadores relacionales, como < o >= se comparan las referencias.
• Se pueden concatenar (unir) dos cadenas mediante el operador +.
• Las cadenas son inmutables, una vez creadas no se pueden modificar, solo se
pueden copiar total o parcialmente.
• Las cadenas se pueden usar en las instrucciones switch.

Métodos comunes de control de cadenas


Método Descripción
static string
Devuelve una copia de str.
Copy(string str)
Devuelve menor que cero si la cadena que llama es menor que
int CompareTo(string
str, mayor que cero si la cadena que llama es mayor que str, y
str)
cero si las cadenas son iguales.
Busca en la cadena que llama la subcadena especificada por
str.
int IndexOf(string str)
Devuelve el índice de la primera coincidencia, o -1 en caso de
error.
Busca en la cadena que llama la subcadena especificada por
int LastIndexOf(string
str. Devuelve el índice de la última coincidencia, o -1 en caso de
str)
error.
string ToLower Devuelve una versión en minúsculas de la cadena que llama.
string ToUpper Devuelve una versión en mayúsculas de la cadena que llama.

Compiladores

En la actualidad existen los siguientes compiladores para el lenguaje C♯:

• Microsoft.NET framework SDK incluye un compilador de C♯, pero no un IDE.


• Microsoft Visual Studio .NET, IDE por excelencia de este lenguaje, versión
2002, 2003, 2005, 2008 y 2010(beta).
• #develop, es un IDE libre para C♯ bajo licencia LGPL, muy similar a Microsoft
Visual C♯.
• Mono, es una implementación GPL de todo el entorno .NET desarrollado por
Novell. Como parte de esta implementación se incluye un compilador de C♯.
• Delphi 2006, de Borland Software Corporation.
• dotGNU Portable.NET, de la Free Software Foundation.

Metas del diseño del lenguaje

El estándar ECMA lista las siguientes metas en el diseño para C♯:

• Lenguaje de programación orientado a objetos simple, moderno y de propósito


general.
• Inclusión de principios de ingeniería de software tales como revisión estricta de
los tipos de datos, revisión de límites de vectores, detección de intentos de usar
variables no inicializadas, y recolección de basura automática.
• Capacidad para desarrollar componentes de software que se puedan usar en
ambientes distribuidos.
• Portabilidad del código fuente
• Fácil migración del programador al nuevo lenguaje, especialmente para
programadores familiarizados con C y C++.
• Soporte para internacionalización
• Adecuación para escribir aplicaciones de cualquier tamaño: desde las más
grandes y sofisticadas como sistemas operativos hasta las más pequeñas
funciones.
• Aplicaciones económicas en cuanto a memoria y procesado.

Java
Caracteristicas

Se necesitaba algún tipo de lenguaje de programación que funcionara en diferentes


tipos de ordenadores, con lo que se necesitó crear una máquina "virtual" para que
interpretara el código de este lenguaje.

Java en 1995 dió un salto al mundo de Internet con lo que representó un salto muy
importante dentro de este lenguaje de la programación exclusivamente enfocado a
trabajar con Objetos muy similar a C++

Java es un lenguaje orientado a objetos, eso implica que su concepción es muy


próxima a la forma de pensar humana. También posee otras características muy
importantes:

Es un lenguaje que es compilado, generando ficheros de clases compilados, pero


estas clases compiladas, son en realidad interpretadas por la máquina virtual de java.
Siendo la máquina virtual de java la que mantiene el control sobre las clases que se
estén ejec

Es un lenguaje seguro: La máquina virtual, al ejecutar el código java, realiza


comprobaciones de seguridad, además el propio lenguaje carece de características
inseguras, como por ejemplo los punteros.

Tipos de datos Java

Tipos de datos compuestos

El concepto de clase como tipo de datos compuesto de campos, métodos y eventos es


similar en Java y C#, introduce el concepto de estructura como tipo de datos
compuesto asignado por pila que no admite la herencia. En la mayoría de los otros
aspectos, las estructuras son muy similares a las clases. Las estructuras proporcionan
una manera ligera de agrupar campos y métodos relacionados para el uso en los
bucles de pequeñas dimensiones y otros escenarios donde el rendimiento es crucial.

C# permite crear un método de destructor al que se llama antes de que se destruyan


las instancias de una clase. En Java, se puede utilizar un método finalize para
contener código que limpia los recursos antes de que se recolecten los elementos no
utilizados del objeto. En C#, el que realiza esta función es el destructor de clase. El
destructor se parece a un constructor sin los argumentos y con un carácter de tilde
delante (~).
Tipos de datos integrados

C# proporciona todos los tipos de datos que están disponibles en Java y agrega
compatibilidad para los números sin signo y un nuevo tipo de punto flotante de 128 bits
de alta precisión.

Para cada tipo de datos primitivo en Java, la biblioteca de clases principal proporciona
una clase contenedora, que lo representa como un objeto de Java. Por ejemplo, la
clase Int32 contiene el tipo de datos int y la clase Double contiene el tipo de datos
double.

Por otro lado, todos los tipos de datos primitivos en C# son objetos en el espacio de
nombres System. Para cada tipo de datos, se proporciona un nombre corto o alias. Por
ejemplo, int es el nombre corto correspondiente a System.Int32 y double es la forma
abreviada de System.Double.

En la tabla siguiente se proporciona la lista de tipos de datos de C# y sus alias. Como


puede ver, los primeros ocho de estos tipos corresponden a los tipos primitivos
disponibles en Java. Sin embargo, tenga en cuenta que el tipo boolean de Java se
denomina bool en C#.

Nombre Clase
Tipo Ancho Intervalo (bits)
corto .NET
byte Byte Entero sin signo 8 0 a 255
sbyte SByte Entero con signo 8 -128 a 127
-2.147.483.648 a
int Int32 Entero con signo 32
2.147.483.647
uint UInt32 Entero sin signo 32 0 a 4294967295
short Int16 Entero con signo 16 -32.768 a 32.767
ushort UInt16 Entero sin signo 16 0 a 65535
-922337203685477508 a
long Int64 Entero con signo 64
922337203685477507
ulong UInt64 Entero sin signo 64 0 a 18446744073709551615
Tipo de punto flotante de
float Single 32 -3,402823e38 a 3,402823e38
precisión simple
Tipo de punto flotante de -1,79769313486232e308 a
double Double 64
precisión doble 1,79769313486232e308
Símbolos Unicode utilizados
char Char Un carácter Unicode 16
en el texto
bool Boolean Tipo Boolean lógico 8 True o false
Tipo base de todos los otros
object Object
tipos
string String Una secuencia de caracteres
Tipo preciso fraccionario o
integral, que puede
decimal Decimal representar números 128 ±1.0 × 10e−28 a ±7.9 × 10e28
decimales con 29 dígitos
significativos

Dado que C# representa todos los tipos de datos primitivos como objetos, es posible
llamar a un método de objeto de un tipo de datos primitivo. Por ejemplo:
C#
static void Main()
{
int i = 10;
object o = i;
System.Console.WriteLine(o.ToString());
}

Constantes

Java y C# proporcionan la capacidad para declarar una variable cuyo valor se


especifica en tiempo de compilación y no se puede cambiar en tiempo de ejecución.
Java utiliza el modificador de campo final para declarar este tipo de variable, mientras
que C# utiliza la palabra clave const. Además de const, C# proporciona la palabra
clave readonly para declarar variables a las que se puede asignar un valor una vez en
tiempo de ejecución, ya sea en la instrucción de declaración o en otra parte del
constructor. Después de la inicialización, el valor de una variable readonly no puede
cambiar. Un escenario en el que las variables readonly son útiles es cuando los
módulos que se han compilado independientemente tienen que compartir datos como
un número de versión. Si el módulo A se actualiza y se vuelve a compilar con un
nuevo número de versión, el módulo B se puede inicializar con ese nuevo valor
constante sin tener que volver a compilarlo.

Enumeraciones

Las enumeraciones se utilizan para agrupar constantes con nombres en forma similar
a la forma en que se utilizan en C y C++; no están disponibles en Java. En el ejemplo
siguiente se define una enumeración Color sencilla.

C#
public enum Color
{
Green, //defaults to 0
Orange, //defaults to 1
Red, //defaults to 2
Blue //defaults to 3
}

También se pueden asignar valores integrales a las enumeraciones, tal como se


muestra en la siguiente declaración de enumeración:

C#
public enum Color2
{
Green = 10,
Orange = 20,
Red = 30,
Blue = 40
}

En el siguiente ejemplo de código se llama al método GetNames del tipo Enum para
mostrar las constantes disponibles para una enumeración. Luego, asigna un valor a
una enumeración y muestra el valor.
C#
class TestEnums
{
static void Main()
{
System.Console.WriteLine("Possible color choices: ");

//Enum.GetNames returns a string array of named constants for the enum.


foreach(string s in System.Enum.GetNames(typeof(Color)))
{
System.Console.WriteLine(s);
}

Color favorite = Color.Blue;

System.Console.WriteLine("Favorite Color is {0}", favorite);


System.Console.WriteLine("Favorite Color value is {0}", (int) favorite);
}
}

Cadenas

Los tipos de cadena en Java y C# denotan un comportamiento similar con leves


diferencias. Ambos tipos de cadena son inmutables, lo que significa que los valores de
las cadenas no se pueden cambiar una vez que se han creado las cadenas. En ambos
casos, los métodos que parecen modificar el contenido real de una cadena crean en
realidad una nueva cadena que se devolverá como resultado, dejando la cadena
original sin cambios. El proceso de comparación de los valores de cadena es diferente
en C# y Java. Para comparar los valores de cadena en Java, los desarrolladores
deben llamar al método equals de un tipo string, mientras que el operador == compara
los tipos de referencia de forma predeterminada. En C#, los desarrolladores pueden
utilizar los operadores == o != para comparar directamente valores de cadena. Aunque
una cadena es un tipo de referencia en C#, los operadores == y != compararán, en
forma predeterminada, los valores de las cadenas en lugar de las referencias.

Operadores

Este lenguaje presenta un conjunto de operadores con los que escribir distintas
expresiones. Además, se pueden utilizar los paréntesis para agrupar subexpresiones.

Operadores Aritméticos

Son los operadores que permiten realizar la suma, resta, producto, división y módulo:
+, -, *, / y % [obligatorio implementar al menos uno]. Se aplican sobre datos enteros,
proporcionando un resultado entero.

También existen los operadores más y menos unarios: +, - [implementación opcional].

Operadores de Relación

Son los operadores que permiten realizar las comparaciones de igual, distinto, menor,
mayor, menor o igual, mayor o igual: ==, !=, <, >, <= y >= [obligatorio implementar al
menos uno de los operadores]. Se aplican sobre datos enteros y proporcionan un
resultado lógico.

Operadores Lógicos

Representan las operaciones de conjunción, disyunción y negación: &&, || y !


[obligatorio implementar al menos uno]. Se aplican sobre datos lógicos y devuelven un
resultado lógico.

Operadores de Incremento y Decremento

Permiten auto-incrementar (++) o auto-decrementar (--) el valor de un identificador


entero (ambos pueden actuar como prefijos o como sufijos) [algunos grupos tienen
que implementar al menos uno de los cuatro]. Se aplican sobre identificadores enteros
y devuelven un resultado entero modificando también el valor del identificador.
Ejemplo:

a = j++; /* si j valía 5, ahora a == 5 y j == 6 */


a = ++j; /* si j valía 5, ahora a == 6 y j == 6 */

Operadores de asignación

Permiten realizar asignaciones simples o realizando simultáneamente una operación:


=, +=, -=, *=, /= y %= [todos los grupos tienen que implementar la asignación simple
(=) y algunos grupos deberán implementar uno de los otros operadores]. Ejemplo:

n += m; /* es equivalente a n = n + m; */

Operador Condicional

El operador condicional (condición ? exp1: exp2) permite obtener un valor basándose


en una condición [implementación obligatoria solo para algunos grupos]. Si la
condición es cierta, devuelve exp1; en caso contrario, devuelve exp2. Ejemplo:

max = (a > b) ? a : b; // Si a > b entonces max == a; en otro caso, max == b

Operador de Acceso a Miembros de una Clase

El operador punto (.) permite utilizar los miembros de una clase. Ejemplo:

c.a= c.m(); /* accede al atributo a y al método m de la clase c */

Operador de Acceso a Elementos de un Vector

El operador corchete ([ ]) permite utilizar los elementos de un vector [implementación


obligatoria sólo para algunos grupos]. Ejemplo:

a= v[i+1]; // accede al elemento i+1 del vector v y copia su valor en a

Tipos de clase

Hasta ahora sólo se ha utilizado la palabra clave public para calificar el nombre de las
clases que hemos visto, pero hay tres modificadores más. Los tipos de clases que
podemos definir son:

abstract

Una clase abstract tiene al menos un método abstracto. Una clase abstracta no se
instancia, sino que se utiliza como clase base para la herencia.

final

Una clase final se declara como la clase que termina una cadena de herencia. No se
puede heredar de una clase final. Por ejemplo, la clase Math es una clase final.

public

Las clases public son accesibles desde otras clases, bien sea directamente o por
herencia. Son accesibles dentro del mismo paquete en el que se han declarado. Para
acceder desde otros paquetes, primero tienen que ser importadas.

synchronizable

Este modificador especifica que todos los métodos definidos en la clase son
sincronizados, es decir, que no se puede acceder al mismo tiempo a ellos desde
distintos threads; el sistema se encarga de colocar los flags necesarios para evitarlo.
Este mecanismo hace que desde threads diferentes se puedan modificar las mismas
variables sin que haya problemas de que se sobreescriban.

ADA
Caracteristicas

Legibilidad
Los programas profesionales se leen muchas más veces de las que se escriben,
por tanto, conviene evitar una notación que permita escribir el programa
fácilmente, pero que sea difícil leerlo excepto, quizás, por el autor original y no
mucho tiempo después de escribirlo.
Tipado fuerte
Esto asegura que todo objeto tenga un conjunto de valores que esté claramente
definido e impide la confusión entre conceptos lógicamente distintos. Como
consecuencia, el compilador detecta más errores que en otros lenguajes.
Construcción de grandes programas
Se necesitan mecanismos de encapsulado para compilar separadamente y para
gestionar bibliotecas de cara a crear programas transportables y mantenibles de
cualquier tamaño.
Manejo de excepciones
Los programas reales raramente son totalmente correctos. Es necesario
proporcionar medios para que el programa se pueda construir en capas y por
partes, de tal forma que se puedan limitar las consecuencias de los errores que se
presenten en cualquiera de las partes.
Abstracción de datos
Se puede obtener mayor transportabilidad y mejor mantenimiento si se pueden
separar los detalles de la representación de los datos y las especificaciones de las
operaciones lógicas sobre los mismos.
Procesamiento paralelo
Para muchas aplicaciones es importante que el programa se pueda implementar
como una serie de actividades paralelas. Dotando al lenguaje de estos
mecanismos, se evita tener que añadirlos por medio de llamadas al sistema
operativo, con lo que se consigue mayor transportabilidad y fiabilidad.
Unidades genéricas
En muchos casos, la lógica de parte de un programa es independiente de los tipos
de los valores que estén siendo manipulados. Para ello, se necesita un mecanismo
que permita la creación de piezas de programa similares a partir de un único
original. Esto es especialmente útil para la creación de bibliotecas.

Tipos de datos Ada

Una de las principales contribuciones de los lenguajes de alto nivel es que el


programador no tiene que preocuparse de cómo se representan físicamente los datos
en el computador. De esta idea surge el concepto de tipo de datos. Una extensión del
mismo es el tipo abstracto de datos. Su implementación es de nuevo desconocida
para el programador, esta vez no porque desconozca la arquitectura del computador
subyacente, sino porque es encapsulado en un módulo que no permite el acceso
directo a los detalles de su implementación. En su lugar, se proporciona al
programador operaciones sobre el tipo que son invocaciones a entradas del módulo
que lo encapsula.

Por ejemplo, consideremos la utilización de un tipo abstracto de datos que represente


a un número complejo:

package Números_complejos is type TComplejo is record Real, Imag: Float; end


record; I: constant TComplejo := (0.0, 1.0); function "+" (X, Y: TComplejo) return
TComplejo; function "-" (X, Y: TComplejo) return TComplejo; function "*" (X, Y:
TComplejo) return TComplejo; function "/" (X, Y: TComplejo) return TComplejo; end
Números_complejos;

De este modo, el usuario debe conocer los detalles de la implementación y sabe que
se utiliza una representación cartesiana. Además, el usuario está obligado a hacer uso
de la representación.

Para impedir el uso del conocimiento de la representación con vistas, por ejemplo, a
poder cambiar ésta posteriormente, se puede hacer uso de los tipos privados
definiéndolos mediante la palabra reservada private:

package Números_complejos is -- Parte visible. type TComplejo is private; -- Tipo


privado. I: constant TComplejo; -- No se puede asignar valor todavía. function "+"
(X, Y: TComplejo) return TComplejo; function "-" (X, Y: TComplejo) return
TComplejo; function "*" (X, Y: TComplejo) return TComplejo; function "/" (X, Y:
TComplejo) return TComplejo; function Construir_complejo (R, I: Float) return
TComplejo; function Parte_imaginaria (X: TComplejo) return Float; function
Parte_real (X: TComplejo) return Float; private -- Parte oculta. type TComplejo is
record Real, Imag: Float; end record; I: constant TComplejo := (0.0, 1.0); end
Números_complejos;

Ahora, se ha definido TComplejo como tipo privado y se resguardan los detalles de su


implementación en la parte no visible del paquete después de la palabra reservada
private y hasta el fin de la especificación del paquete. En la parte visible (desde el
comienzo de la especificación hasta private), se da la información disponible fuera del
paquete.

Las únicas operaciones disponibles son la asignación, la igualdad y la desigualdad,


aparte de las añadidas en el paquete.

Nótese que el valor de I no se puede dar pues no se conocen todavía los detalles de la
implementación, se declara como constante y se le asigna después un valor en la
parte privada.

Las funciones Construir_complejo, Parte_imaginaria y Parte_real son ahora


necesarias pues el usuario ya no conoce la estructura del tipo TComplejo y se necesita
realizar dicha interfaz para poder manejar objetos del tipo privado.

El cuerpo se podría implementar de la siguiente manera:

package body Números_complejos is function "+" (X, Y: Tcomplejo) return


TComplejo is begin return (X.Real + Y.Real, X.Imag + Y.Imag); end "+"; -- ... "-", "* y
"/" similarmente. function Construir_complejo (R, I: Float) return TComplejo is
begin return (R, I); end Construir_complejo; function Parte_real (X: TComplejo)
return Real is begin return X.Real; end Parte_real; -- ... Parte_imaginaria
análogamente. end Números_complejos;

Y podría ser utilizado transparentemente, por ejemplo, dentro de un bloque como:

declare use Números_complejos; C1, C2: TComplejo; R1, R2: Float; begin C1 :=
Construir_complejo (1.5, -6.0); C2 := C1 + I; R := Parte_real (C2) + 8.0; end;

Si ahora se quisiera cambiar la implementación del tipo TComplejo y representarlo en


forma polar, no sería necesario cambiar la parte visible de la especificación, por lo que
todas las unidades que utilicen dicho paquete no tienen la necesidad de actualizarse.
La interfaz exportada no ha cambiado y, por tanto, los programas que la utilizarán
pueden seguir haciéndolo. Por ejemplo, ahora se podría representar en la parte
privada de la especificación del paquete como:

-- ... private Pi: constant := 3.1416; type TComplejo is record R: Float; Theta: Float
range 0.0 .. 2*Pi; end recod; I: constant TComplejo := (1.0, 0.5*Pi); end
Números_complejos;

Lo único que se necesitaría sería reescribir el cuerpo del paquete y recompilarlo.

Operadores

Comprobación de pertenencia (in, not in)

Además existe la comprobación de pertenencia ( , in; , not in) que


técnicamente no es un operador y no se puede sobrecargar. Su precedencia es la
misma que la de los operadores relacionales. Se puede utilizar con rangos o con
subtipos.

-- Supongamos que X es de tipo Integer


if X in Positive then -- Positive es un subtipo de Integer
...
if X not in 4 .. 6 then
...
end if;
end if;

declare
type Dia_Semana is
(Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo);
subtype Dia_Laborable is Dia_Semana range Lunes .. Viernes;
Hoy : Dia_Semana := Obtener_Dia;
begin

if Hoy in Dia_Laborable then -- Dia_Laborable es un subtipo de Dia_Semana


Ir_Al_Trabajo;
if X not in Lunes .. Miercoles then
Pensar_En_El_Fin_De_Semana;
end if;
end if;
end;

Operadores lógicos de corto-circuito

Para los operadores lógicos existen versiones para minimizar las evaluaciones (short-
circuit). Es decir, se evalúa primero el operando de la izquierda y después, sólo si es
necesario para determinar el resultado, el de la derecha:

• Conjunción: and then


• Disyunción inclusiva: or else

Instrucciones de control

Ada distingue entre declaraciones, que introducen identificadores nuevos, y


sentencias, que no lo hacen. El fragmento de texto más simple que incluye
declaraciones y sentencias es un bloque.

Definición
bloque ::=
[ identificador : ]
[ declare
parte_declarativa ]
begin
sentencias
[ exception
manejador_de_excepción
{ manejador_de_excepción } ]
end [ identificador ] ;
Ejemplo
-- Supuestas X e Y declaradas con anterioridad de tipo Float:
declare
Temp: Float; -- Esta variable sólo es visible dentro del bloque.
begin -- Se intercambian 2 variables.
Temp := X;
X := Y;
Y := Temp;
end;

Destacar que un bloque es una sentencia, cuando se ejecuta, se elaboran las


declaraciones contenidas en su parte declarativa (entre declare y begin) y después se
ejecutan las sentencias del cuerpo (entre begin y end).

El ámbito de las variables declaradas en un bloque finaliza cuando termina dicho


bloque.

Sentencia de selección (if)

Flujo de control if-then-else

Definición
sentencia_selección ::=
if condición then secuencia_de_sentencias
[ { elsif condición then secuencia_de_sentencias } ]
[ else secuencia_de_sentencias ]
end if ;

with Ada.Text_Io,Ada.Integer_Text_Io; use Ada.Text_Io,Ada.Integer_Text_Io;

procedure comptar_LA is cp,c:character; n:integer; --declaramos las variables

begin
put("Escribe una frase"); --pedimos y recojemos texto
put (c);
n:=0; --inicializamos n a cero
cp:=' ';get(c); --si cp es espacio seguir leyendo
while c/='.' loop --mientras c sea diferente del PUNTO entrar en bucle if cp='L'and c='A'
then n:=n+1;end if; -- si cp es L y c es A incrementar n y finalizar el if

cp:=c; Get(c); --igualamos cp a c para proceder y c sigue al proxima caracter


end loop; --FINALIZAMOS BUCLE
Put("El numero de palabras de la frase es"); --enseñamos en pantalla variable n
put(n);

end comptar_LA;

Ejemplo
if Hoy = DOM then -- Si hoy es domingo.
Mañana := LUN;
elsif Hoy /= SAB then -- Si no es domingo ni sábado.
Laborable := True;
else -- Cualquier otro caso.
Mañana := TDía'Succ(Hoy);
end if;

Sentencia de selección por casos (case)

Flujo de control de la estructura case

[editar] Definición
sentencia_selección_por_casos ::=
case expresión is
alternativa_caso { alternativa_caso }
end case ;

alternativa_caso ::=
when elección { | elección } => secuencia_de_sentencias
elección ::= expresión_simple | rango_discreto | others

Ejemplo
case Hoy is
when MIE..VIE => Entrenar_duro; -- Rango.
when MAR | SAB => Entrenar_poco; -- Varias elecciones.
when DOM => Competir; -- Una elección.
when others => Descansar; -- Debe ser única y la última alternativa.
end case;
Bucles

Sentencia de bucle simple (loop)

Definición
sentencia_bucle_simple ::=
[ identificador_bucle : ] loop
secuencia_de_sentencias
end loop [ identificador_bucle ] ;

Ejemplo
Vida: loop -- El bucle dura indefinidamente.
Trabajar;
Comer;
Dormir;
end loop Vida;

Este bucle sólo se puede abandonar si alguno de los procedimientos levanta una
excepción.

El bucle simple a menudo se acompaña de una sentencia exit para abandonar el bucle
cuando se cumple una condición.

loop
Alimentar_Caldera;
Monitorizar_Sensor;
exit when Temperatura_Ideal;
end loop;

Sentencia de bucle iterativo (for)

Definición
sentencia_bucle_iterativo ::=
[ identificador_bucle : ] for parámetros_for loop
secuencia_de_sentencias
end loop [ identificador_bucle ] ;

parámetros_for ::= identificador in [ reverse ] rango_discreto

Ejemplo
for I in 1..N loop -- I se itera desde 1 hasta N.
V(I) := 0;
end loop;

Sentencia de bucle condicional (while)

Definición
sentencia_bucle_iterativo ::=
[ identificador_bucle : ] while condición loop
secuencia_de_sentencias
end loop [ identificador_bucle ] ;

Ejemplo
I := 1;
while I > N loop -- Se hace el bucle mientras se cumpla la condición.
V(I) := 0;
I := I + 1;
end loop;

Otras sentencias de control (goto, exit, return, abort)

Sentencia goto

Antes de nada, decir que la utilización de sentencias goto se desaconseja totalmente.


Ada posee estructuras de control adecuadas para evitar su uso. Si se soporta su
utilización es por si se quiere traducir de otro lenguaje a Ada automáticamente.

Se especifica una etiqueta entre los símbolos << y >>, por ejemplo, <<Salto>>.

Se realiza el salto a dicha etiqueta con la sentencia goto Salto; que transferiría el
control a la siguiente sentencia después de la etiqueta.

No puede usarse para transferir control fuera de un subprograma.

Sentencia exit

Termina el bucle nombrado en la sentencia o, si no aparece, el bucle más próximo que


la contiene. Su notación sintáctica es:

sentencia_exit ::= exit [ nombre_bucle ] [ when condición ] ;

Sentencia return

Termina la ejecución del subprograma más próximo que la contiene, tanto en


procedimientos como en funciones donde, además, se utiliza para devolver el
resultado de dicha función. Su notación sintáctica es:

sentencia_return ::= return [ expresión ] ;

Sentencia abort

Se utiliza sólo para tareas. Implica la terminación incondicional de las tareas que se
especifiquen. Su notación sintáctica es:

sentencia_abort ::= abort nombre_tarea { , nombre_tarea } ;

Cadena de caracteres

En Ada hay varios tipos de cadenas de caracteres; presentamos aqui algunas


de ellas.

Cadenas de tamaño fijo:


-----------------------
<identificador>:string(1..<longitud>); -- El valor inicial no tiene porque ser
-- 1, pero en general lo usaremos asi.

Declara la variable indicada como una cadena de caracteres de la longitud


especificada.

Ejemplo:

cadena: string(1..20);
cad: string(1..5);

OPERADORES Y FUNCIONES:

:= Ejemplos: s:="hola "; -- 20 caracteres


=, /= Ejemplo: if s="hola " then
<, > Comparación lexicográfica (carácter a carácter, según el código
ASCII. Las dos cadenas deben tener la misma longitud)
Ejemplos:
"adios"<"hola " se evalúa a true ("hola "<"adios" es false)
"casa "<"casanova" se evalúa a true
"CASA"<"casa" se evalúa a true
"ZAPATO"<"avion " se evalúa a true
"00000"<"casa " se evalúa a true
"00000"<"CASA " se evalúa a true
"1 "<"15" se evalúa a true
"1 "<"015" se evalúa a FALSE

PROCEDIMIENTOS:

procedure Move (Source : in String;


Target : out String;
Drop : in Truncation := Error;
Justify : in Alignment := Left;
Pad : in Character := Space);

Drop: que hacer si no cabe. Posibilidades: Error, Left, Right


Justify: donde la pone?. Posibilidades: Left, Right, Center
Pad: con que rellena. Posibilidades: cualquier carácter.

Ejemplos: Move("Jose Luis", cadena); -- "Jose Luis"


Move(cadena,cad,Drop=>Right); -- "Jose "
Move("CPS",cad,Drop=>Right,Pad=>'+'); -- "CPS++"

cad(indice) (Indexación de cadenas)


Permite acceder a los caracteres que componen la cadena cad
comenzando en la posición 1 y acabando en la posición última según
el tamaño con que se definió.
Ejemplo:
c:char;
s:string(1..10);
begin
s:="hola ";
put(s(3)); -- escribe el caracter 'l'
c:=s(1); -- le asigna a c el caracter 'h'
writeln(s(11)); -- da error en ejecución, 11>10
end;

También se puede usar para asignar valores a caracteres específicos


de una cadena.
Ejemplo:
s:string(1..5);
begin
s:="000000";
s(3):='1';
s(5):='1';
put(s); -- escribiria "001010"
s(1):="12"; -- da error al compilar. Debe ser un caracter

cad(indice1..indice2) (Trozos de cadenas -rebanadas-)


Permite acceder a grupos de caracteres que componen la cadena cad
comenzando en la posición indice1 y acabando en la posición indice2
Ejemplo:
s:string(1..10);
begin
s:="hola ";
put(s(1..3)); -- escribe 'hol'
end;

La indexación también se puede usar para asignar valores a una cadena,


carácter a carácter.
Ejemplo:
s:string(1..5);
begin
s:="000000";
s(3..5):="111";
put(s); -- escribiria "001110"
s(4..6):="123"; -- da error al compilar. Debe ser un rango
-- admisible.
s(4..5):="123"; -- da error al compilar. Deben ser del mismo
-- tamaño.
end;

Cadenas de tamaño variable (acotado):


-------------------------------------

with Ada.Strings.Bounded;
...
procedure .... is

package <identificador> is new Generic_Bounded_Length(<tam>);


...

define el tipo Bounded_String capaz de albergar cadenas de caracteres de como


máximo tam caracteres.

Ejemplo:
package pCad20 is new Generic_Bounded_Length(20);
use pCad20;
laCad: Bounded_String;
...
laCad:= To_Bounded_String("Jose Luis");

Concatenación: &

Ejemplo: laCad:= laCad & To_Bounded_String(" Perez");


laCad:= Append(laCad, ' ' & "Pi");

Indexación de elementos:

function Element (Source : in Bounded_String;


Index : in Positive) return Character;

Ejemplo:

put(Element(laCad, 3)); -- Escribira ´s´

Selección de trozos (rebanadas):

function Slice (Source : in Bounded_String;


Low : in Positive;
High : in Natural) return String;

Ejemplo:

cad(2..4):= Slice(laCad,2,4); -- Notar que la primera era de tamaño fijo.

Otras interesantes con relación a las anteriores:

procedure Replace_Element (Source : in out Bounded_String;


Index : in Positive;
By : in Character);
procedure Replace_Slice (Source : in out Bounded_String;
Low : in Positive;
High : in Natural;
By : in String;
Drop : in Truncation := Error);
Lenguajes Java Ada C++ C#

Caracteristicas Orientado a Orientado a Orientado a objetos Orientado a objetos


objetos objetos

Tipo de dato Compuesto abstractos enteros, caracter enteros


,integrado numero en coma de punto flotante
,variables flotante, booleanos caracter
constantes vacio logicos
constantes
variables
Principios Todo programa en
C++ debe tener la
función main() (a no
ser que se
especifique en
tiempo de
compilación otro
punto de entrada,
que en realidad es
la función que tiene
el main())

Concepto de
clase

Instrucción de If,for,etc If,case,loop,for,


control While,goto,exit,ret
urn,
abort

metodos Astract,final
Public
sincronozable

Cadena de
caracteres

Compiladores micrsoft net


framework sdk,
microsoft visual
studio .net,
#develop, mono,
novell, delphi,

operadores
2_ Visual Basic
Visual basic es un programa basado en objetos, pero no orientado a ojetos, la
diferencia esta en que visual basic utiliza objetos con propiedades y metodos pero
carece de los mecanismos de herencia y polimorfismo propios de los verdaderos
lenguajes orientados a objetos como java y c++.

Los siguientes temas describen la implementación del polimorfismo en Visual Basic y


cómo puede utilizarlo en sus programas.

• Cómo proporciona Visual Basic el polimorfismo La mayoría de los lenguajes


orientados a objetos proporcionan polimorfismo mediante la herencia; Visual
Basic utiliza la técnica de interfaz múltiple del Modelo de objetos componentes
(COM).
• Crear e implementar una interfaz Un amplio ejemplo de código muestra cómo
puede crear una interfaz abstracta Animal e implementarla para las clases
Tiranosaurio y Pulga.
• Implementar propiedades Las interfaces que implementa pueden tener tanto
propiedades como métodos, aunque hay algunas diferencias en la forma de
implementar las propiedades.
• Tiempo muerto para una breve descripción de objetos e interfaces Explica los
términos objeto e interfaz, presenta el concepto de consultar una interfaz y
describe otros orígenes de interfaces para implementar.
• Múltiples facetas (e interfaces) de la reutilización del código Además de
implementar interfaces abstractas, puede volver a utilizar el código si
implementa la interfaz de una clase normal y después delega de forma
selectiva en una instancia oculta de la clase.

El polimorfismo proviene del hecho de que puede llamar al método Morder de un


objeto que pertenezca a cualquier clase derivada de Animal sin conocer la clase a la
que pertenece el objeto.

Proporcionar polimorfismo mediante interfaces

Visual Basic no utiliza la herencia para proporcionar polimorfismo. Visual Basic


proporciona el polimorfismo mediante múltiples interfaces ActiveX. En el Modelo de
objetos componentes (COM) que forma la infraestructura de la especificación ActiveX,
las múltiples interfaces permiten que los sistemas de componentes software
evolucionen sin afectar al código existente.

Una interfaz es un conjunto de propiedades y métodos relacionados. Gran parte de la


especificación ActiveX se ocupa de la implementación de interfaces estándar para
obtener servicios del sistema o para proporcionar funcionalidad a otros programas.
En Visual Basic podría crear una interfaz Animal e implementarla en las clases Pulga y
Tiranosaurio. Después podría invocar el método Morder de cualquiera de los dos tipos
de objeto sin conocer su tipo.
Herencia en Visual Basic

Visual Basic admite el concepto de herencia, es decir, la posibilidad de definir clases


que sirven de base para clases derivadas. Las clases derivadas heredan, y pueden
extender, las propiedades, métodos y eventos de la clase base. Las clases derivadas
también pueden reemplazar métodos heredados con nuevas implementaciones. De
manera predeterminada, se pueden heredar todas las clases creadas con Visual
Basic.

La herencia permite escribir y depurar una clase una vez, y después volver a utilizar
ese código una y otra vez como base de nuevas clases. La herencia también permite
utilizar el polimorfismo basado en la herencia, la posibilidad de definir clases que
pueden utilizarse de forma intercambiable mediante código cliente en tiempo de
ejecución, pero con funcionalidad diferente, incluyo con métodos o propiedades
denominados de manera idéntica.

Visual Basic (Visual Studio) constituye un IDE (entorno de desarrollo integrado o en


inglés Integrated Development Enviroment) que ha sido empaquetado como un
programa de aplicación, es decir, consiste en un editor de código (programa donde se
escribe el código fuente), un depurador (programa que corrige errores en el código
fuente para que pueda ser bien compilado), un compilador (programa que traduce el
código fuente a lenguaje de máquina), y un constructor de interfaz gráfica o GUI (es
una forma de programar en la que no es necesario escribir el código para la parte
gráfica del programa, sino que se puede hacer de forma visual).

Compilador

El compilador de Visual Basic x.0 genera ejecutables que requieren una DLL para que
funcionen, en algunos casos llamada MSVBVMxy.DLL (acrónimo de "MicroSoft Visual
Basic Virtual Machine x.y", siendo x.y la versión) y en otros VBRUNXXX.DLL ("Visual
Basic Runtime X.XX"), que provee todas las funciones implementadas en el lenguaje.
Además existen un gran número de bibliotecas (DLL) que facilitan el acceso a muchas
funciones del sistema operativo y la integración con otras aplicaciones. Sin embargo
esto sólo es una limitación en sistemas obsoletos, ya que las bibliotecas necesarias, la
mayoria de componentes y controles adicionales inclusive, para ejecutar programas en
Visual Basic 5.0 y 6.0 vienen de serie en todas las versiones de Windows desde
Windows XP.

Entorno de desarrollo

Su entorno de desarrollo es muy similar al de otros lenguajes e IDE's. - Se compone


principalmente de su barra de herramientas y menus que se pueden personalizar con
prácticamente la completa totalidad de los comandos del IDE a necesidad.

• El espacio de trabajo donde se muestran todas las ventanas del proyecto, las
vistas de codigo de modulos y objetos, y las vistas de diseño de formularios y
componentes.
• El Cuadro de herramientas (por defecto a la izquierda) contiene los controles
con los que componer las ventanas de nuestra aplicacion. Por defecto
disponemos los controles básicos:
o (PictureBox) Caja de Imagen
o (Label) Etiqueta
o (TextBox) Caja de texto
o (Frame) Marco
o (CommandButton) Boton de comando
o (CheckBox) Casilla de verificación
o (OptionButton) Boton de opción
o (ComboBox) Lista desplegable
o (ListBox) Lista
o (HScrollBar) Barra de desplazamiento horizontal
o (VScrollBar) Barra de desplazamiento vertical
o (Timer) Temporizador
o (DriveListBox) Lista de unidades de disco
o (DirListBox) Lista de directorios
o (FileListBox) Lista de archivos
o (Shape) Figura
o (Line) Linea
o (Image) Imagen
o (Data) Conexion a origen de datos
o (OLE) Contenedor de documentos embebidos compatibles con Object
Linking and Embedding

Se pueden agregar todo tipo de controles de terceros, una gran cantidad de ellos de
serie con la instalacion de Visual Basic 6.0, que vienen embebidos dentro de archivos
de extension *.OCX.

• El panel lateral derecho contiene dos vistas principales:


o El Explorador de Proyectos, que muestra todos los elementos que
componen nuestro proyecto o grupos de proyectos (formularios, interfaz
de controles, módulos de codigo, modulos de clase, etc...)
o El Panel de propiedades, donde se muestran todos los atributos de los
controles de nuestros formularios o la información de modulos clase y
formularios entre otros muchos.

La Ventana de inmediato (por defecto en la parte inferior aunque puede no


estar visible. Utilizar Ctrl+G mostrar la ventana). Esta ventana resulta una
herramienta muy util a la hora de depurar codigo o incluso de hacer pruebas
rapidas ya que permite imprimir mensajes de texto desde nuestro codigo y
ejecutar sentencias simples de codigo (solo sentencias que se puedan
representar en una sola linea, no permite bloques) que puede ser desde código
de nuestra propia aplicación, por ejemplo consultar el valor de una variable o
llamar a un metodo declarado en el modulo que se esta depurando, asi como
ejecutar código al vuelo

Características :

• Es un lenguaje RAD.
• Posee una curva de aprendizaje muy rápida.
• Integra el diseño e implementación de formularios de Windows.
• Permite usar con suma facilidad la plataforma de los sistemas Windows dado
que tiene acceso practicamente total a la API de Windows incluidas librerías
actuales.
• El código en Visual Basic es fácilmente migrable a otros lenguajes.
• Es un lenguaje muy extendido por lo que resulta fácil encontrar información,
documentación y fuentes para los proyectos.
• Fácilmente extensible mediante librerías DLL y componentes ActiveX de otros
lenguajes.
• Posibilidad de añadir soporte para ejecución de scripts, VBScript o JScript, en
las aplicaciones mediante Microsoft Script Control.
• Acceso a la API multimedia de DirectX (versiones 7 y 8). También esta
disponible, de forma no oficial, un componente para trabajar con OpenGL 1.1:
VBOpenGL type library
• Existe una versión integrada en las aplicaciones de Office, versiones tanto
Windows como Mac, que permite programar macros para extender y
automatizar funcionalidades en documentos como por ejemplo una hoja de
calculo de EXCEL o una base de datos ACCESS (VBA)
• Es un entorno perfecto para realizar pequeños prototipos rápidos de ideas.

• No es multiplataforma (Sin embargo se pueden usar emuladores e interpretes


para correrlos en otras plataformas).
• Por defecto permite la programación sin declaración de variables. (que puede
ser sencillamente corregida escribiendo la frase Option Explicit en el
encabezado de cada modulo de codigo, en cuyo caso será menester declarar
todas las variables a utilizar, lo que a la postre genera código más estable y
seguro).
• No permite programación a bajo nivel ni incrustar secciones de código en ASM
(aunque es posible ejecutar codigo ASM mediante pequeños hacks como
este).
• Sólo soporta librerías dinámicas (DLL) que usen la convención de llamadas
_stdcall y componentes y librerías ActiveX.
• Es un lenguaje basado en objetos pero no implementa por completo la filosofía
de Orientación a Objetos
• No permite la sobrecarga de operadores ni métodos.
• No permite nombres de espacio
• No soporta el punteros a memoria salvo en algunas acciones concretas, como
por ejemplo pasar la dirección de memoria de una función como argumento
(operador AddressOf).
• No soporta tratamiento de procesos como parte del lenguaje.
• No incluye operadores de desplazamiento de bits como parte del lenguaje.
• No permite el manejo de memoria dinámica, punteros, etc. como parte del
lenguaje.
• No soporta el tratamiento de excepciones. Su tratamiento de errores se basa
en la captura de mensajes y desvió del flujo de ejecución de la forma
tradicional del BASIC (On Error Goto <etiqueta/numero linea>).
• No controla todos los errores de conversion de tipos dado que en muchas
ocasiones hace conversiones al vuelo (sobre todo al usar variables de tipo
Variant).
• Aunque existen opciones avanzadas en el compilador para desactivar los
controladores de desbordamiento de enteros o las comprobaciones de límites
en matrices entre otros (presumiblemente para optimizar y lograr algo de
rendimiento) no es seguro del todo dado que hay mas posibilidades de generar
una excepcion grave no controlada por el interprete (y por consiguiente el
programador) o un memory leak haciendo el programa altamente inestable e
impredecible.
• No tiene instrucciones de preprocesamiento.
• El tratamiento de mensajes de Windows es básico e indirecto.
• La gran gama de controles incorporados son, sin embargo en algunos casos,
muy generales, lo que lleva a tener que reprogramar nuevos controles para una
necesidad concreta de la aplicación. Esto cambia radicalmente en Visual
Basic .NET donde es posible reprogramar y mejorar o reutilizar los controles
existentes.
• El depurador no es demasiado flexible ni cómodo en ciertas situaciones.
• Los controles personalizados no mejoran la potencia de la API de Windows, y
en determinados casos acudir a ésta será el único modo de conseguir el
control personalizado deseado.
• No soporta correctamente la programacion multihilo haciendo muy inestable su
ejecucion inclusive desde el propio entorno de desarrollo.
• Su fuerte dependencia de librerías y componentes ActiveX que requieren de
privilegios de administrador para poder instalar las aplicaciones (existen
opciones de terceras aplicaciones para generar ejecutables independientes
que embeben las dependencias en el propio código del ejecutable, por
ejemplo: Molebox o Thinstall/VMware Thinapp).

programacion estructurada

La programación estructurada es un método (un paradigma de programación) de


programacion basado sobre el concepto de la unidad y del alcance (la gama de la
visión de los datos de una declaración ejecutable del código). Un programa
estructurado se compone de unas o más unidades o módulos — escrito por el usuario,
o sacado de una libreria; cada módulo se compone de unos o más procedimientos,
también llamado una función, una rutina, un subprograma, o un método, dependiendo
del lenguaje de programación. Es posible que un programa estructural tenga niveles
múltiples o alcances, con los procedimientos definidos dentro de otros procedimientos.
Cada alcance puede contener las variables que no se pueden considerar en alcances
externos.

El término no se debe confundir con la programación tipo imperativa. Una lenguaje


estructurado (no imperativa) es LOGO, que especifica secuencias de pasos para
realizarse pero no tiene un estado interno.

La programación estructurada ofrece muchas ventajas sobre la programación


secuencial: El código de programación estructurada es más fácil de leer y más
conservable; El código procesal es más flexible; La programación estructurada permite
la práctica más fácil del buen diseño del programa.

Programación estructurada

El creciente empleo de los computadores ha conducido a buscar un abaratamiento del


desarrollo de software, paralelo a la reducción del costo del hardware obtenido gracias
a los avances tecnológicos. Los altos costos del mantenimiento de las aplicaciones en
producción normal también han urgido la necesidad de mejorar la productividad del
personal de programación.

funcionamiento

La programación estructurada (en adelante simplemente PE ), es un estilo de


programación con el cual el programador elabora programas, cuya estructura es la
más clara posible, mediante el uso de tres estructuras básicas de control lógico, a
saber :

SECUENCIA.
SELECCIÓN.

ITERACIÓN.

Un programa estructurado se compone de funciones,segmentos, módulos y/o


subrutinas, cada una con una sola entrada y una sola salida. Cada uno de estos
módulos (aún en el mismo programa completo), se denomina programa apropiado
cuando, además de estar compuesto sólamente por las tres estructuras básicas, tiene
sólo una entrada y una salida y en ejecución no tiene partes por las cuales nunca pasa
ni tiene ciclos infinitos.

La PE tiene un teorema estructural o teorema fundamental, el cual afirma que


cualquier programa, no importa el tipo de trabajo que ejecute, puede ser elaborado
utilizando únicamente las tres estructuras básicas ( secuencia, selección, iteración ).

DEFINICIÓN DE LAS ESTRUCTURAS BÁSICAS DE CONTROL LÓGICO

1.- SECUENCIA

Indica que las instrucciones de un programa se ejecutan una después de la otra, en el


mismo orden en el cual aparecen en el programa. Se representa gráficamente como
una caja después de otra, ambas con una sola entrada y una única salida.

Las cajas A y B pueden ser definidas para ejecutar desde una simple instrucción hasta
un módulo o programa completo, siempre y cuando que estos también sean
programas apropiados.

2.- SELECCIÓN

También conocida como la estructura SI-CIERTO-FALSO, plantea la selección entre


dos alternativas con base en el resultado de la evaluación de una condición o
predicado; equivale a la instrucción IF de todos los lenguajes de programación y se
representa gráficamente de la siguiente manera :

En el diagrama de flujo anterior, C es una condición que se evalúa; A es la acción que


se ejecuta cuando la evaluación de este predicado resulta verdadera y B es la acción
ejecutada cuando indica falso. La estructura también tiene una sola entrada y una sola
salida; y las funciones A y B también pueden ser cualquier estructura básica o
conjunto de estructuras.

3.- ITERACIÓN

También llamada la estructura HACER-MIENTRAS-QUE, corresponde a la ejecución


repetida de una instrucción mientras que se cumple una determinada condición. El
diagrama de flujo para esta estructura es el siguiente :

Aquí el bloque A se ejecuta repetidamente mientras que la condición C se cumpla o


sea cierta. También tiene una sola entrada y una sola salida; igualmente A puede ser
cualquier estructura básica o conjunto de estructuras.

VENTAJAS DE LA PROGRAMACIÓN ESTRUCTURADA


Los programas son más fáciles de entender. Un programa estructurado puede ser
leído en secuencia, de arriba hacia abajo, sin necesidad de estar saltando de un sitio a
otro en la lógica, lo cual es típico de otros estilos de programación. La estructura del
programa es más clara puesto que las instrucciones están más ligadas o relacionadas
entre si, por lo que es más fácil comprender lo que hace cada función. Reducción del
esfuerzo en las pruebas. El programa se puede tener listo para producción normal en
un tiempo menor del tradicional; por otro lado, el seguimiento de las fallas o
depuración (debugging) se facilita debido a la lógica más visible, de tal forma que los
errores se pueden detectar y corregir más fácilmente.

Reducción de los costos de mantenimiento.

Programas más sencillos y más rápidos.

Aumento en la productividad del programador.

Se facilita la utilización de las otras técnicas para el mejoramiento de la productividad


en programación.

Los programas quedan mejor documentados internamente.

programacion funcional

Entre las características del paradigma funcional destacamos:

• Programación declarativa
• Definición y evaluación de funciones
• Uso de la recursión
• Funciones como datos primitivos

Definición y evaluación de funciones

Cualquier lenguaje funcional permite la definición de nuevas funciones como forma de


abstracción, y la evaluación de expresiones como forma de computación.

Ya hemos visto cómo definir y evaluar funciones en Scheme:

(define (cuadrado x)
(* x x))

(+ (cuadrado 3) (cuadrado 2))

Estamos definiendo una función con un argumento formal (x) que tiene como cuerpo la
expresión (* x x) y le estamos dando el nombre de cuadrado. Después evaluamos una
expresión en la que llamamos a la función recién definida y a la función primitiva '+'.
Uso de la recursión

Otro elemento común a todos los lenguajes funcionales es el uso de la recursión para
expresar funciones que en otros lenguajes se expresan con iteraciones. Muchas veces
es más sencillo y natural utilizar una recursión en la definición de una función.
Veremos hoy algún ejemplo sencillo. En clases posteriores analizaremos más a fondo
la recursión en Scheme.

Factorial

Un primer ejemplo es la típica función factorial que calcula el factorial de un número.

Matemáticamente, el factorial de un número se puede expresar con la siguiente


formulación:

Esta expresión tiene una traducción directa en Scheme:

(define (factorial x)
(if (= x 0)
1
(* x (factorial (- x 1)))))

>(factorial 8)
40320
>(factorial 30)
265252859812191058636308480000000

Sumatorio

Otro ejemplo de función recursiva es el sumatorio desde un número inicial hasta un


límite. Matemáticamente se expresa con la siguiente fórmula:

En Scheme:

(define (sumatorio min max)


(if (> min max)
0
(+ min (sumatorio (+ 1 min) max))))

> (sumatorio 3 12)


75

Por último un ejemplo utilizando símbolos y las funciones que vimos en una clase
anterior empty?, first, bf y equal?.

Recordemos qué hace cada una de estas funciones:

• (empty? palabra): comprueba si palabra es una cadena vacía


• (first palabra): devuelve el primer carácter de un símbolo (como símbolo a su
vez)
• (bf palabra): devuelve el resto de un símbolo, después de haber quitado su
primer carácter
• (equal? pal1 pal2): comprueba si dos símbolos son iguales

La función (elemento? x palabra) devuelve #t o #f dependiendo de si x es un símbolo


que está dentro de palabra:

(define (elemento? x palabra)


(cond
((empty? palabra) #f)
((equal? x (first palabra)) #t)
(else (elemento? x (bf palabra)))))
> (elemento? 'a 'hola)
#t
> (elemento? 'e 'cocodrilo)
#f

Funciones como tipo de datos primitivo

Otra característica fundamental de los lenguajes funcionales es que las funciones son
consideradas un tipo de dato primitivo.

Un tipo de dato primitivo es aquel que:

• puede ser el valor de una variable (en terminología de programación funcional:


nombrado por un identificador)
• un argumento de una función
• el valor que devuelve una función
• componente de una estructura de datos mayor

Por ejemplo, los números, los caracteres o las cadenas son datos primitivos en la
mayor parte de lenguajes de programación. Sin embargo, resulta poco frecuente que
podamos hacer todas estas cosas con una función o un procedimiento. Es una de las
características más sorprendentes de los lenguajes funcionales:

• Una variable puede tener como valor un procedimiento


• Podemos definir una función que toma como argumentos otras funciones
• Podemos devolver un procedimiento como resultado de una llamada a otro
procedimiento
• Podemos construir estructuras de datos que contengan procedimientos como
elementos (listas de procedimientos, por ejemplo)

Vamos a ver un ejemplo de cada una de estas características en Scheme:


Una función puede ser nombrada con un identificador.

Ya hemos visto algún ejemplo curioso de esto:

(define suma +)
suma
>(suma 1 2 3)
6
(define + -)
(+ 4 2)
2
(define + suma)

Podemos comprobar en el ejemplo que los símbolos 'suma', '+' o '-' no son más que
identificadores ligados a procedimientos.

En Scheme cuando definimos una función con un nombre, la relación entre la función
y el nombre es la misma que la relación entre una variable y un valor. El nombre de la
función es el identificador que está ligado al procedimiento.

Una función puede ser el argumento de otra función.

Por ejemplo, podemos defnir la siguiente función aplicar que toma como argumento
una función f 2 argumentos y realiza la llamada a f con los 2 argumentos:

(define (aplicar f x y)
(f x y))
> (aplicar + 2 3)
5
> (aplicar * 2 3)
6
(aplicar word 'hola 'adios)
holaadios

En el siguiente ejemplo definimos una función que toma dos procedimientos unarios
(de un argumento) f y g y los aplica a un número:

(define (aplicar-2 f g x)
(f (g x)))
(define (5+ x)
(+ 5 x))
(define (doble x)
(* 2 x))
> (aplicar-2 5+ doble 8)
21

Una función puede ser el valor devuelto por otra función.

Vamos a definir una función que devuelve otra función. La siguiente función hacer-
suma1 define en su interior la función suma1 y la devuelve.

(define (hacer-suma1)
(define (suma1 x)
(+ x 1))
suma1)
(define f (hacer-suma1))
> (f 3)
4

Vemos que el resultado de la llamada a hacer-suma1 se guarda en la variable f y


después comprobamos que realmente es una función que suma 1.

Podemos complicar un poco más el ejemplo definiendo un parámetro con el número


que queremos sumar en la función que devolvemos. Así, la función hacer-sumak
construye otra función que suma el parámetro k al argumento x que se le pasa:

(define (hacer-sumak k)
(define (sumak x)
(+ x k))
sumak)
(define g (hacer-sumak 8))
> (g 5)
13

Por último, una función puede ser parte de una estructura de datos mayor.

Veamos un ejemplo que a primera vista puede parecer algo complicado (ya que
mezclamos recursión y funciones como tipos de datos primitivos). Se trata de construir
una función que recorra una lista de funciones unarias (se aplican a un único
argumento) y las vaya aplicando a un argumento.

Vamos a verlo paso a paso. Vamos a ver en primer lugar qué es eso de una lista de
funciones unarias.

(define (2+ x)
(+ 2 x))
(define (10* x)
(* 10 x))
(define (cuadrado x)
(* x x))

(define lista-funcs (list cuadrado 2+ 10*))

La lista lista-funcs es una lista que contiene funciones (no identificadores). Vamos a
comprobarlo. Si miramos su primer elemento el intérprete nos dice lo siguiente:

> (car lista-funcs)


#procedure:cuadrado

Podemos probar qué ese primer elemento es un procedimiento aplicándolo a un


número y viendo que devuelve su cuadrado:

>((car lista-funcs) 3)
9

Podemos ahora hacer la función aplica-funcs que recorre la lista de funciones y las
aplica a un número:
(define (aplica-funcs lista x)
(if (empty? (cdr lista))
((car lista) x)
((car lista) (aplica-funcs (cdr lista) x))))

El caso base de la recursión es el caso en que la lista de funciones tiene un único


argumento (eso es lo que significa (empty? (cdr lista))). En ese caso se devuelve el
resultado de aplicar esa única función al número x.

En el caso general, aplicamos la primera función al resultado de la llamada recursiva


de aplicar el resto de funciones al número x.

Lambda en Scheme

Los tipos de datos primitivos se pueden manejar directamente en los lenguajes de


programación, sin darles nombre (asignarlos a variables). Por ejemplo, cuando
queremos llamar a una función cualquiera (como cuadrado) con el número "4" como
argumento, escribimos directamente "4" y de esa forma nos referimos a ese número:
"4":

(cuadrado 4)

No hace falta hacer:

(define cuatro 4)
(cuadrado cuatro)

Hemos dicho que las funciones son en Scheme tipos de datos primitivos. ¿Cumplen
entonces la propiedad anterior? ¿Es posible usar una función sin darle un nombre? La
respuesta es que sí, utilizando la forma especial lambda.

La sintaxis de lambda es:

(lambda (<arg1> ... <argn>) <cuerpo>)

La forma especial lambda construye una función sin nombre y la devuelve como
resultado. Para definir una función son necesarios dos elementos: sus argumentos y
su cuerpo. Estos dos elementos son los que se definen a continuación de la palabra
lambda.

Así, por ejemplo, en la siguiente expresión:

(lambda (x)
(* x x))

se define una función que tiene un único argumento (x) y que tiene como cuerpo la
expresión (* x x). Es la función cuadrado.

Si ejecutamos esa expresión en el intérprete de Scheme veremos que el resultado de


su evaluación es un procedimiento:

>(lambda (x) (* x x))


#procedure:3:2
Con ese procedimiento que nos devuelve Scheme (el procedimiento llamado con el
críptico nombre de #procedure:3:2) podemos hacer bastantes cosas. Algunos
ejemplos:

1. Podemos darle un nombre y obtener una función con nombre que después
podremos usar:
2. > (define cuadrado (lambda (x) (* x x)))
3. > (cuadrado 3)
4. 9
5. Pasarlo como argumento de otra función que admite otras funciones como
parámetro:
6. >(define (aplicar-2 f g x)
7. (f (g x)))
8.
9. >(define (suma-5 x)
10. (+ 5 x))
11.
12. >(aplicar-2 suma-5 (lambda (x) (* x x)) 3)
13. 14
14. Podemos evaluarlo inmediatamente poniendo un paréntesis de apertura
(recordad lo que comentamos la primera semana de clase de que los
paréntesis de apertura "(" servían para lanzar funciones o formas especiales
que hay a continuación de ellos):
15. >((lambda (x) (* x x)) 3)
16. 9

Você também pode gostar