Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Fundamentos POO PDF
Fundamentos POO PDF
FUNDAMENTOS POO
INTRODUCCIÓN:
Objetivos:
General
Específicos
Comprender las nociones de la abstracción de datos a través del estudio de los Tipos
de Datos Abstractos (TDA) más utilizados.
Introducción
Objetivo General
Objetivos específicos
Pensar en objetos es pensar como lo hacemos en la vida real. Si usted mira a su alrededor
lo que ve son objetos, personas, animales, libros, computadores, muebles, edificios, etc. La
Programación Orientada a Objetos (POO) es un estilo de programación que emplea los
objetos como componentes fundamentales para la construcción de programas. Es decir,
realiza un modelado de objetos reales, sus características y comportamiento.
Objetos
- Objetos físicos:
Carros
Barcos
Personas
Animales, etc.
Mensajes
Clases
La definición de objeto nos lleva a una de los conceptos fundamentales de la POO que es
el de clase. Una clase se puede considerar como un patrón para construir objetos, es decir,
son tipos de datos definidos por el programador, quien determina las estructuras de datos
y las operaciones asociadas con ese tipo.
En C++, un objeto es sólo un tipo de variable de una clase determinada. Cada vez que se
construye un objeto de una clase, se crea una instancia de esa clase. Así como a una
instancia del tipo predefinido double se le llama variable, una instancia de una clase es el
objeto. Por consiguiente, una clase define 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.
Es importante diferenciar entre objetos y clases, una clase es una colección de objetos
similares y un objeto es una instancia de una definición de una clase. La clase es
simplemente una declaración, no tiene asociado ningún objeto, de modo que no puede
recibir mensajes ni procesarlos, esto únicamente lo hacen los objetos.
Herencia
La herencia produce relaciones jerárquicas entre las clases, pues existen clases hijas y
padres. Dependiendo del tipo de herencia existen clases que reciben características de una
sola clase (herencia simple) y otras clases que reciben atributos de más de una clase base
(herencia múltiple).
El UML es un lenguaje gráfico utilizado para representar los programas orientados a objetos
de manera visual. Representa las relaciones entre clases bases y clases derivadas, así
como entre objetos.
Polimorfismo
Es una característica de la POO que describe el hecho de que una función puede tener un
comportamiento diferente dependiendo del tipo de objeto sobre el cual esté actuando. Por
ejemplo, se puede definir la función multiplicar como la multiplicación entre dos números
escalares, así como la multiplicación entre dos matrices.
9.2 CLASES
Así como un plano permite la construcción de varias casas, a partir de una clase se pueden
crear varios objetos. Por consiguiente, los objetos son las instancias de las clases, cada
que se construye un objeto de una clase, se está creando una instancia de dicha clase. Por
esta razón, se utilizan indistintamente los términos objeto e instancia de una clase.
Generalmente, las clases se denominan con sustantivos abstractos, como por ejemplo
Animal, Persona, Factura, entre otros.
Ejemplo:
Podemos tener, por ejemplo, una clase denominada Gato con determinadas propiedades y
comportamientos. Una vez creada dicha clase podemos crear dos instancias una llamada
"Garfield" y otra llamada “Mimi” que tendrán ciertas propiedades específicas y los
comportamientos de dicha clase. Los atributos de la clase Gato podrían ser: sexo, color,
raza y nivel de cansancio. Por su parte, las operaciones de Gato podrían ser: paseo, quieto
y muerte.
Abstracción:
La abstracción en POO surge del hecho de que cada clase del programa es un modelo de
un "agente" abstracto que puede realizar funciones, informar y cambiar su estado, y
"comunicarse" con otras clases 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:
Polimorfismo:
Esta propiedad se refiere al hecho de que una misma función puede tener comportamientos
diferentes y estar asociada a objetos distintos.
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 re-
implementar su comportamiento. 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.
Persistencia:
La definición de una clase consta de dos partes: una declaración y una implementación. La
declaración es la lista de los miembros de la clase. La implementación o cuerpo define las
funciones de la clase. A continuación, se describen cada uno de estos pasos.
Las clases están conformadas por un conjunto de características y de funciones, las cuales
son equivalentes a las funciones en los lenguajes estructurados. Se diferencian de ellos en
que es posible acceder a las variables de la clase de forma implícita. A los atributos se les
conoce como miembros dato y son las características que describen el objeto. Las
funciones son conocidas en C++ como función miembro o como métodos en otros lenguajes
de programación. Dichas funciones pueden estar relacionadas entre sí, modificar el estado
del objeto o invocar funcionalidades de otros objetos, entre otras cosas.
Una clase se puede definir con struct, union o class pero la forma que caracteriza la POO
es utilizar la palabra reservada class. La sintaxis de una clase es:
class nombre_clase
{
membro_dato1;
miembro_dato2;
...
funcion_miembro1();
funcion_miembro2();
...
};
Como se puede ver una clase es sintácticamente igual a una estructura, con la única
diferencia de que en el tipo class todos los miembros son por defecto privados mientras que
en el tipo struct son por defecto públicos. Suponga que usted va a desarrollar un programa
para manejas el inventario de su empresa. Para esto definiremos una clase de tipo elemento
cuyos miembros datos podrían ser: nombre, marca y cantidad; sus funciones podrían ser
simplemente aumentar cantidad y disminuir cantidad.
class elemento {
char nom[20];
char marca[20];
int cantidad;
Una de las características fundamentales de una clase es ocultar tanta información como
sea posible. Por consiguiente, es necesario imponer ciertas restricciones en el modo en que
se puede manipular una clase y de cómo se pueden utilizar los datos y el código dentro de
una clase.
Dentro de una declaración de clase, cada una de estas palabras se puede utilizar para
preceder a una o más declaraciones de los miembros de una clase:
Acceso público. Los miembros públicos son accesibles por cualquier parte del
programa.
Acceso protegido. Los miembros protegidos significan que sólo se puede acceder a
ellos por “funciones miembro” dentro de la misma clase y por funciones miembro de
clases derivadas de esta clase.
Acceso privado. Los miembros privados sólo pueden ser utilizados por las “funciones
miembro” de la clase y las funciones amigas de la clase.
Una clase puede contener partes públicas y partes privadas. Por defecto, todos los
miembros definidos en la clase son privados. Para hacer las partes de una clase públicas
(esto es, accesibles desde cualquier parte del programa) deben declararse después de la
palabra reservada public. Todas las variables o funciones definidas después de public son
accesibles a las restantes funciones del programa. Dado que una característica clave de la
POO es la ocultación de datos, debe tenerse presente que, aunque se pueden tener
variables públicas, desde un punto de vista conceptual se debe tratar de limitar o eliminar
su uso. En su lugar, deben hacerse todos los datos privados y controlar el acceso a ellos a
través de funciones públicas. Considere la clase de tipo artículo que se declara en el
siguiente ejemplo. En ella se tiene dos miembros dato privados y una función miembro
pública.
class articulo
{
private:
float precio;
char nombre[30];
Por defecto u omisión todo lo declarado dentro de una clase es privado y sólo se puede
acceder a ello con las funciones miembro declaradas en el interior de la clase o con
funciones amigas.
Los miembros que se declaran en la sección protegida de una clase sólo pueden ser
accedidos por funciones miembro declaradas dentro de la clase, por funciones amigas o
por funciones miembro de clases derivadas.
A los miembros que se declaran en la región pública de una clase se puede acceder a
través de cualquier objeto de la clase de igual modo que se accede a los miembros de una
estructura en C++.
En el siguiente ejemplo se declara una clase con más miembros que la clase del ejemplo
anterior. Nótese que hay miembros datos cuyo especificador de acceso está sin definir, por
lo cual serán tomados al compilar como miembros dato privados. De igual forma se declara
un miembro dato público. En cuanto a las funciones todas las funciones de la clase son
públicas.
class alfa
{
int x; //miembros dato privados
float y;
char z;
public:
double k; //miembro dato público
void fijar(int,float,char); //funciones miembro públicas
void visualizar();
};
Miembros dato
La lista de miembros dato de una clase puede comprender cualquier tipo válido en C++.
Puede contener tipos primarios, estructuras e incluso apuntadores a cualquier tipo válido.
Los miembros dato pueden ser incluso clases. En cualquier caso, sólo las instancias de
clases definidas o declaradas previamente pueden ser miembros.
Además de los especificadores de acceso utilizados para definir los miembros de una clase.
Un miembro dato de una clase se puede declarar estático (static). Para un miembro dato,
la designación static significa que existe sólo una instancia de ese miembro. Un miembro
dato estático es compartido por todos los objetos de una clase. A un miembro dato static se
le asigna una zona fija de almacenamiento en tiempo de compilación, al igual que una
variable global, pero el identificador de la variable está dentro de ámbito utilizando
solamente el operador (::) con el nombre de la clase.
Los miembros datos se asignan generalmente con la misma clase de almacenamiento. Para
declarar o inicializar un miembro static se utiliza la misma notación que una variable global.
class ejemplo
{
public:
static int valor; //declarar miembro estático
};
void main()
{
ejemplo e1,e2;
e1.valor=1;
e2.valor=10;
}
class ejemplo
{
private:
static int valor; //declarar miembro estático
void main()
{
ejemplo e1;
e1.valor=1; //error: acceso no válido
}
Para acceder a un miembro dato private static se necesita utilizar el operador (::). Otros
medios son:
Funciones miembro
Una operación de una clase, es un servicio que la clase proporciona a los clientes de la
clase. Por lo general, los objetos no realizan sus operaciones de manera autónoma, sino
que requieren de la recepción de un mensaje que es enviado por un objeto cliente.
Las funciones miembro implementan las operaciones permitidas sobre los tipos de datos
de una clase. Para declarar una función miembro hay que situar su prototipo en el cuerpo
de la clase. No hay que definir la función dentro de la clase; dicha definición puede estar
fuera de la clase e incluso en un archivo independiente, aunque también pueden ser
definidas en línea dentro de la clase.
Las funciones miembro son necesarias para acceder a los datos privados. En general, son
públicas; si no lo fueran, ninguna otra función podría llamarlas. Se pueden declarar para
devolver valores con tipos incluyendo objetos de clases, apuntadores o referencias. Pueden
ser declaradas también para aceptar cualquier número y tipo de argumentos. Los
argumentos por defecto están permitidos, así como la notación de puntos suspensivos.
Una vez se ha declarado la clase es necesario definir cada una de las funciones miembro
que ésta contiene. La definición de funciones miembro es muy similar a la definición
ordinaria de función. Tienen una cabecera y un cuerpo y pueden tener tipos y argumentos.
Una de las principales características de las funciones miembro es que éstas por pertenecer
a la clase pueden tener acceso a las componentes privadas de la clase.
En la primera opción la función de define en línea (inline). Por cada llamada a esta
función, el compilador genera (vuelve a copiar) las diferentes instrucciones de la función,
por consiguiente, no existe una única copia de la función dentro del código, sino que
cada vez que se realiza una llamada a la función se crea una copia de ella en lugar de
invocarla. La ventaja de este tipo de funciones es que el programa se ejecuta más
rápidamente, el inconveniente es que se requiere mayor espacio en memoria para su
ejecución.
class ejemplo
{
int x,y;
public:
void ejemplo::f()
{
cout<<"x= "<<x<<" y= "<<y<<endl;
}
9.3 OBJETOS
La comunicación con el objeto se realiza a través del paso de mensajes. El envío a una
instancia de una clase produce la ejecución de una función miembro. El paso de mensajes
es el término utilizado para referirnos a la invocación o llamada de una función miembro de
un objeto.
De igual forma, haciendo uso del tercer ejemplo de la sección 2.2.1, se definió una clase de
tipo alfa. Una vez definida esta clase, ésta puede ser usada dentro del main() como se
presenta en el siguiente ejemplo. Nótese que en primer lugar se define un objeto del tipo
alfa. Posteriormente se hace uso de las funciones miembros, las cuales se definieron como
públicas y no se produce ningún error. Sin embargo, no se puede acceder a los miembros
privados.
int main()
{
alfa obj; //declaración de un objeto
Los operadores new y delete se pueden utilizar para crear y destruir objetos de una clase,
así como dentro de funciones constructoras y destructoras.
Un objeto de una determinada clase se crea cuando la ejecución del programa entra en el
ámbito en que está definida y se destruye cuando se llega al final del ámbito. Esto es válido
tanto para objetos globales como locales, ya que los objetos globales se crean al comenzar
el programa y se destruyen al salir de él. Sin embargo, se puede crear un objeto también
mediante el operador new, que asigna la memoria necesaria para alojar el objeto y devuelve
su dirección, en forma de puntero, al objeto en cuestión.
class cadena
{
char *datos;
public:cadena(int);
~cadena();
};
cadena::cadena(int lon)
{
datos=new char[lon];
}
cadena::~cadena()
9.3.2 CONSTRUCTORES
Un constructor es una función especial que sirve para construir o inicializar objetos. En C++
la inicialización de objetos no se puede realizar en el momento en que son declarados; sin
embargo, tiene una característica muy importante y es disponer de una función llamada
constructor que permite inicializar objetos en el momento en que se crean.
Un constructor es una función que sirve para construir un nuevo objeto y asignar valores a
sus miembros dato. Se caracteriza por:
Un constructor del objeto se llama cuando se crea el objeto implícitamente: nunca se llama
explícitamente a las funciones constructoras. Esto significa que se llama cuando se ejecuta
la declaración del objeto. También, para objetos locales, el constructor se llama cada vez
que la declaración del objeto se encuentra. En objetos globales, el constructor se llama
cuando se arranca el programa.
Se pueden declarar en una clase constructores múltiples, mientras tomen parte diferentes
tipos o número de argumentos. El compilador es entonces capaz de determinar
automáticamente a qué constructor llamar en cada caso, examinando los argumentos. Los
argumentos por defecto se pueden especificar en la declaración del constructor. Los
miembros dato se inicializarán a esos valores por defecto, si ningún otro se especifica.
Ejemplo
Un constructor que crea un nuevo objeto a partir de uno existente se llama constructor
copiador o de copia. El constructor de copias tiene sólo un argumento: una referencia
constante a un objeto de la misma clase. Un constructor copiador de una clase complejo
es:
9.3.3 DESTRUCTORES
Un destructor es una función miembro con igual nombre que la clase, pero precedido por el
carácter ~. Una clase sólo tiene una función destructora que, no tiene argumentos y no
devuelve ningún tipo. Un destructor realiza la operación opuesta de un constructor,
limpiando el almacenamiento asignado a los objetos cuando se crean. C++ permite sólo un
destructor por clase. El compilador llama automáticamente a un destructor del objeto
cuando el objeto sale fuera del ámbito. Si un destructor no se define en una clase, se creará
por defecto un destructor que no hace nada. Normalmente los destructores se declaran
public.
cadena::cadena() : cad(NULL) {}
cadena::~cadena() {
delete[] cad; // Libera la memoria reservada a cad
}
C++ proporciona las herramientas necesarias que permiten definir funciones y operadores
que utilizan el mismo nombre o símbolo que una función u operador incorporado. Esto se
conoce como sobrecarga de funciones o sobrecarga de operadores. Estas funciones y
operadores se dicen que están sobrecargados y se pueden utilizar para redefinir una
función u operador definido.
Sobrecarga de funciones
Dos funciones pueden tener el mismo nombre, pero tener una implementación y
argumentos de entrada y variables de retorno diferentes. Esta característica se conoce
Considere el mismo ejemplo del numeral anterior. Ahora vamos a sobrecargar la función
constructora de la clase cadena.
class cadena {
public:
cadena(); // Constructor por defecto
cadena(char *c); // Constructor desde cadena c
cadena(int n); // Constructor de cadena de n caracteres
cadena(const cadena &); // Constructor copia
~cadena(); // Destructor
cadena::cadena() : cad(NULL) {}
cadena::cadena(char *c) {
cad = new char[strlen(c)+1];// Reserva memoria para cadena
strcpy(cad, c); // Almacena la cadena
}
cadena::cadena(int n) {
cad = new char[n+1]; // Reserva memoria para n caracteres
cad[0] = 0; // Cadena vacía
}
Sobrecarga de operadores
Operadores que se pueden sobrecargar: +, -, *, /, %, ^, &, |, _, ', =, <, >, <=, >=, ++, --, <<,
>>, ==, 0, %%, ||, +=, -=, *=, /=, %=, &=, |=, <<=, >>=, [ ], ( ), ->, ->*, new, delete
Si un operador tiene formatos unitarios y binarios (tales como + y &) ambos formatos
pueden ser sobrecargados. Un operador unitario tiene un parámetro, mientras que un
operador binario tiene dos. ¿El único operador ternario, ?:, no se puede sobrecargar.
Existen una serie de operadores que no se pueden sobrecargar: ., ::, ?:, sizeof
La clave para la sobrecarga de un operador es una función incorporada a C++ que permite
al programador sustituir una función definida por el usuario para uno de los operadores
existentes.
Las funciones operador deben ser miembro o amigas de la clase que se está utilizando.
Las funciones operador tienen la misma sintaxis que cualquier otra, excepto que el nombre
de la función es operator op, donde op es el operador que se sobrecarga.
Una función amiga es una función no miembro de una clase que puede tener acceso a las
partes privadas de una clase; se debe declarar como amiga de la clase mediante la palabra
reservada friend.
class cosa
{
int datos;
public:
friend void cargar (cosa& t, int x);
};
Como la función cargar se declara amiga de la clase cosa puede acceder al miembro
privado datos.
Las razones fundamentales para utilizar funciones amigas es que algunas funciones
necesitan acceso privilegiado a más de una clase. Una segunda razón es que las funciones
amigas pasan todos sus argumentos a través de la lista de argumentos y cada valor de
argumento se somete a la conversión de asignación.
Por último, consideremos el siguiente ejemplo en el cual se hace uso de las funciones
amiga. Se puede ver que la clase miclase tiene una función amiga llamada factor que tiene
#include <iostream>
using namespace std;
class miclase{
int n,d;
public:
miclase(int i, int j){n=i;d=j;}
friend int factor(miclase ob);
};
int factor(miclase ob)
{
if (!(ob.n%ob.d))
return 1;
else
return 0;
}
void main()
{
miclase obj1(10,2), obj2(3,2);
if(factor(obj1))
cout << "es factor";
else
cout << "no es factor";
}
No sólo puede ser una función, amiga de una clase, también una clase completa puede ser
amiga de otra clase. En este caso todas las funciones de la clase amiga pueden acceder a
las partes privadas de la otra clase.
Una clase amiga puede ser declarada antes de que pueda ser designada como amiga.
class animales;
class hombre
{
public:
friend class animales;
};
class animales
};
Resumen
Cualquier dato o función miembro de tipo public puede ser accedida y utilizada por otras
clases. Las funciones o datos de tipo private solo son accesibles a clases amigas o
miembros de la misma clase.
Un constructor es una función miembro especial con el mismo nombre de la clase que se
encarga de crearla
Actividades complementarias
1. Crear una clase llamada empleado que contenga como dato miembro el nombre y
el número de empleado, y como funciones miembro Leerdatos() y Verdatos() que
lean los datos del teclado y los visualice en pantalla, respectivamente.
2. Cree una clase llamada complejo para realizar aritmética con números complejos.
Los números complejos tienen la forma: a + b – i, en donde a es la parte real y b es
la parte imaginaria.
Utilice variables double para representar los datos de tipo private de la clase, es
decir la parte real e imaginaria. Cree un constructor que permita inicializar un objeto
de esta clase cuando se declare. Proporcione funciones miembro de tipo public para
realizar lo siguiente:
Suma de dos números complejos: las partes reales se suman juntas y las partes
imaginarias se suman juntas.
Lecturas recomendadas