Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Tutorial 1
Introducción a la
Programación Orientada a
Objetos
C++. Tutorial 1 Introducción POO
1. Introducción. ......................................................................................................................... 3
2. Conceptos orientados a objetos. .......................................................................................... 4
2.1. Abstracción de datos. .................................................................................................... 4
2.2. Objeto............................................................................................................................ 4
2.3. Atributos........................................................................................................................ 5
2.4. Mensajes y métodos. .................................................................................................... 5
2.5. Encapsulación. ............................................................................................................... 5
2.6. Clases............................................................................................................................. 5
2.7. Herencia de clases. ........................................................................................................ 5
2.8. Polimorfismo. ................................................................................................................ 6
3. E/S por consola de C++.......................................................................................................... 8
4. Comentarios en C++. ............................................................................................................. 9
5. Definición de clases. ............................................................................................................ 10
6. Introducción a la sobrecarga de funciones. ........................................................................ 13
7. Funciones constructoras y destructoras. ............................................................................ 14
8. Constructores con parámetros. .......................................................................................... 16
9. Punteros a objeto. ............................................................................................................... 18
10. Clases, estructuras y uniones en C++. ............................................................................. 19
11. Funciones insertadas. ...................................................................................................... 20
12. Inserción automática....................................................................................................... 21
13. Asignación de objetos. .................................................................................................... 22
14. Paso de objetos a funciones............................................................................................ 23
15. Objetos devueltos por funciones. ................................................................................... 24
16. Funciones amigas. ........................................................................................................... 25
17. Arrays de objetos ............................................................................................................ 26
18. Uso de punteros a objetos. ............................................................................................. 28
19. El puntero "THIS"............................................................................................................. 29
21. Asignación dinámica de memoria en C++. ...................................................................... 30
22. Referencias. ..................................................................................................................... 33
22.1. Paso de referencias a objetos. ................................................................................ 34
22.2. Devolución de referencias....................................................................................... 36
22.3. Referencias independientes. ................................................................................... 38
1. Introducción.
2.2. Objeto.
Se corresponde con una entidad del mundo real. Tiene dos partes: una estructura de
datos y unas operaciones que manejan esa estructura, llamadas métodos. Un objeto
contiene atributos, a los que llamaremos variables de instancia. El objeto tiene una
parte visible y una parte oculta. En la parte visible, llamada interfaz o protocolo del
objeto, sólo vemos los nombres de los mensajes que el objeto entiende. En la parte
oculta están la implementación de esos métodos y los atributos.
2.3. Atributos.
Dos objetos tendrán, en principio, distinto valor en sus atributos. No se accede a los
atributos directamente, sino con los métodos del protocolo del objeto. Un atributo
puede tomar un valor o un conjunto de valores. Los atributos son también objetos.
Un objeto tiene una identidad: aunque cambien los valores de sus atributos, el objeto
sigue siendo siempre el mismo.
2.5. Encapsulación.
Es el mecanismo que agrupa el código y los datos que maneja y los mantiene protegidos
frente a cualquier interferencia y mal uso. Un objeto es por tanto, el dispositivo que
soporta encapsulación. Un objeto oculta parte de su información a otros objetos. Sólo
se puede acceder a un objeto a través de los mensajes de su propia interfaz. Los
atributos y métodos están integrados en un objeto de tal forma que un objeto sólo
puede modificar sus propios atributos con sus propios métodos. Por esto, los atributos
y los métodos están almacenados físicamente en un mismo módulo de programación,
es decir, encapsulados.
2.6. Clases.
Todos los objetos con los mismos atributos y que entienden los mismos mensajes se
agrupan en una clase. Las instancias o ejemplares de una clase son los objetos.
Una clase es también un objeto, y por tanto debe ser instancia, a su vez, de una clase a
la que llamaremos metaclase. También tiene variables y métodos que se llaman
variables de clase y métodos de clase. Las variables de clase son compartidas por todos
los objetos de una clase. Entre los métodos de clase destacan los de creación de
instancias de una clase.
Además, a una clase puede añadirse nuevos métodos y nuevos atributos, o anular los
métodos de su clase base, o redefinir dichos métodos.
La relación de herencia es ascendente: una clase hereda de su clase base que a su vez
también hereda de su clase base y así sucesivamente.
Para visualizar la relación de herencia utilizaremos grafos o árboles de herencia.
C++ tiene herencia múltiple, esto quiere decir que una clase puede heredar atributos y
métodos de más de una clase base.
2.8. Polimorfismo.
Un mismo mensaje puede ser válido para más de una clase porque su implementación
puede ser distinta. En realidad podremos tener varios métodos para un mismo mensaje.
Cuando ocurre esto decimos que hay polimorfismo o sobrecarga de funciones. Lo
mismo ocurre con el nombre de los atributos. El polimorfismo se puede aplicar tanto a
funciones como a operadores, en el segundo caso hablaremos de sobrecarga de
operadores.
Todos los lenguajes de POO, incluyendo C++, comparten las tres características
siguientes: encapsulación, polimorfismo y herencia.
Ejemplo:
#include <iostream>
class rectangulo
{
private:
int ancho, largo;
public:
void intro(int a, int b); int area ();
int perimetro ();
};
4. Comentarios en C++.
5. Definición de clases.
Las funciones y variables declaradas dentro de una declaración de clase, se dice que son
miembros de esa clase. Por omisión, todas las funciones y variables declaradas en una
clase son privadas para esa clase. Esto significa, que sólo son accesibles por otros
miembros de esa clase. Todas las funciones y variables declaradas tras el especificador
public son públicas y por tanto, accesibles tanto por los miembros de la clase como por
cualquier otra parte del programa que contiene la clase.
Las funciones declaradas como parte de una clase se llaman funciones miembro. Para
definir una función miembro, se debe enlazar el nombre tipo de la clase de la que es
parte la función miembro con el nombre de la función. Esto se hace precediendo el
nombre de la función con el nombre de clase seguido de dos símbolos de dos puntos.
El formato general para definir una función miembro es el siguiente:
tipo nom_clase::nom_función(lista-parámetros)
{
<instrucciones>;
}
Para crear un objeto de una determinada clase se utiliza el nombre de dicha clase como
un especificador de tipo:
nom_clase lista_de_objetos;
Una vez creado un objeto de clase, el programa puede referenciar sus miembros
públicos usando el operador punto de la misma forma en que se accede a los miembros
de la estructura.
Ejemplo:
#include <iostream>
class op_aritméticas
{
private:
int op1, op2;
public:
intro (int a, int b);
int suma();
int resta();
float multiplica();
float divide();
};
int op_aritméticas::suma()
{
return (op1 + op2);
}
int op_aritméticas::resta()
{
return (op1 - op2);
}
float op_aritméticas::multiplica()
{
return (op1 * op2);
}
float op_aritméticas::divide()
{
return (op1 / op2);
}
main()
{
int a, b;
op_aritméticas ob;
cout << "Operador 1: ";
cin >> a;
cout << "Operador 2: ";
cin >> b;
ob.intro (a,b);
cout << a << " + " << b << " = " << ob.suma();
cout << a << " - " << b << " = " << ob.resta();
cout << a << " * " << b << " = " << ob.multiplica();
cout << a << " / " << b << " = " << ob.divide();
}
#include <iostream>
class triangulo
{
private:
int base, altura;
public:
intro (int a, int b);
float área();
};
int main()
{
int a, b;
triangulo ar;
cout << "Base: ";
cin >> a;
cout << "Altura: ";
cin >> b; ar.intro(a,b);
cout << "El área de " << a << "y " << b << "es: " << ar.area();
return 0;
}
Prácticamente cada objeto que se crea va a necesitar algún tipo de inicialización. Para
tratar esta situación, C++ permite incluir una función constructora en una declaración
de clase. A un constructor de clase se le llama cada vez que se crea un objeto de esa
clase. De esta manera, cualquier inicialización que sea necesaria en un objeto la puede
realizar automáticamente la función constructora.
Una función constructora tiene el mismo nombre que la clase de la que es parte y no
tiene tipo devuelto.
Ejemplo:
#include <iostream>
class pila
{
private:
int p[10];
int tope;
public:
pila();
PUSH (int n);
POP ();
};
pila::pila()
{
int i;
tope = 0;
for (i=0; i<10; i++)
p[i] = 0;
}
Ejemplo:
#include <iostream>
class ejemplo
{
private:
int *n;
public:
ejemplo();
~ejemplo();
};
ejemplo::ejemplo()
{
n = (int *) malloc (sizeof(int));
}
ejemplo::~ ejemplo()
{
free (n);
}
int rectangulo::area()
{
return (ancho * largo);
}
int rectangulo::perimetro()
{
return (2 * (ancho + largo));
}
int main()
{
int largo, ancho;
clrscr();
cout << "Introduce largo: ";
cin >> largo;
cout << "Introduce ancho: ";
cin >> ancho;
rectangulo obj(largo, ancho);
cout << "Área = " << obj.area();
cout << "Perímetro = " << obj.perímetro();
getch();
return 0;
}
Ejemplo. Creación de una clase llamada box, cuya función constructora recibe tres
valores de tipo entero, que representen la longitud de los lados del paralelepipedo.
También se va a crear una función miembro llamada vol que muestre el volumen de
cada objeto de la clase box.
#include <iostream.h>
#include <conio.h>
class box
{
private:
int a, b, c;
public:
box (int largo, int ancho, int alto);
int vol();
};
int box::vol ()
{
return (a * b * c);
}
9. Punteros a objeto.
int main()
{
rectangulo ob1(5,4), *ob2;
ob2 = &ob1;
cout << "Área = " << ob1.área();
cout << "Perímetro = "" << ob2->perímetro();
return 0;
}
De igual forma, en C++ las uniones y las clases también están relacionadas. En C++, una
unión también define un tipo de clase que puede contener como miembros tanto
funciones como variables. Una unión es como una estructura en la que, por omisión,
todos los miembros son públicos. Lo único que hay en una unión es que todos los
miembros comparten la misma posición de memoria (igual que en C). Las uniones
pueden contener funciones constructoras y destructoras.
Sin embargo, en C++ hay varias restricciones que aplicar a las uniones. No pueden
heredar ninguna otra clase y no se pueden usar como clases base por ningún otro tipo.
Las uniones no deben tener ningún miembro static. Además no pueden contener
ningún objeto que tenga un constructor o un destructor.
En C++, es posible definir funciones a las que no se llama realmente, pero se insertan en
el código en el momento de cada llamada. Es casi igual que la forma en que trabaja una
macro con parámetros en C. La ventaja de las funciones insertadas es que no tienen
nada asociado con el mecanismo de llamada y vuelta de la función. Esto significa que
las funciones insertadas se pueden ejecutar más rápidamente que las funciones
normales.
La desventaja de las funciones insertadas es que si son demasiado largas y se las llama
demasiado a menudo, el programa aumentará su longitud de forma considerable.
Para declarar una función insertada, simplemente hay que preceder la definición de la
función con el especificador inline.
Ejemplo:
#include <iostream.h>
inline int area (int lado)
{
return (lado * lado);
}
int main()
{
cout << "Área = " area(5);
// equivalente a cout << "Área = " << (5*5);
return 0;
}
Una función insertada se tiene que definir antes de llamarla. Si no es así, el compilador
no tiene forma de saber que tiene que insertarla
Si por cualquier razón, el compilador no es capaz de cumplir la petición, la función se
compila como una función normal y la solicitud inline se ignora.
Un objeto se puede asignar a otro siempre y cuando ambos objetos sean del mismo
tipo. Por omisión, cuando un objeto se asigna a otro, se hace una copia a nivel de bits
de todos los miembros.
Ejemplo:
#include <iostream.h>
class ejemplo
{
private:
int a, b;
public:
intro (int i, int j);
visualiza();
};
ejemplo::visualiza()
{
cout << a << " " << b;
}
int main()
{
ejemplo o1, o2;
o1.intro(10,5);
o2.intro(20,15);
o2.visualiza();
o2=o1;
o2.visualiza();
return 0;
}
Los objetos se pueden pasar a funciones como argumentos de la misma manera que se
pasan otros tipos de datos. Simplemente hay que declarar el parámetro como un tipo
clase y después usar un objeto de esa clase como un argumento cuando se llama a la
función. Igual que con otro tipo de datos, por omisión todos los objetos se pasan por
valor a una función.
Ejemplo:
#include <iostream.h>
class samp
{
private:
int i;
public:
samp (int n){i=n;}
void set_i (int n) {i=n;}
int get_i() {return i;}
};
void sqr_it(samp ob)
{
ob.set_i (ob.get_i() * ob.get_i());
cout << "La copia de ob tiene un valor i de " << ob.get_i();
}
int main()
{
samp o1(10);
sqr_it(o1);
cout << "pero o1.i no se cambia en main";
cout << o1.get_i(); return 0;
}
Las funciones pueden devolver objetos, de igual forma que se pueden pasar objetos a
funciones. Para hacerlo, primero hay que declarar la función para que devuelva un tipo
de clase y segundo, hay que devolver un objeto de ese tipo usando la sentencia return.
Cuando un objeto es devuelto por una función, se crea automáticamente un objeto
temporal que guarda el valor devuelto. Este es el objeto que realmente devuelve la
función. Después de devolver el valor, este objeto se destruye. La destrucción de este
objeto temporal puede causar efectos laterales inesperados en algunas situaciones.
Este es el caso, al devolver objetos desde funciones si esos objetos contienen funciones
destructoras, porque el objeto devuelto sale fuera de ámbito tan pronto como el valor
se devuelve a la rutina de llamada.
C++ soporta las funciones amigas. Una función amiga no es un miembro de una clase,
pero tiene acceso a sus elementos privados.
Una función amiga se define como una función no miembro normal. Sin embargo,
dentro de la declaración de clase para la que será una función amiga, está también
incluido su prototipo, precedido por la palabra clave friend.
Ejemplo:
#include <iostream.h>
class rectángulo
{
private:
int a, b;
public:
rectangulo (int ancho, int largo) {a = ancho; b = largo;}
int area() { return (a * b);}
int perimetro() { return (2 * (a + b));}
friend int volumen (rectángulo ob, int altura);
};
int volumen (rectángulo ob, int altura)
{
return (altura * ob.a * ob.b);
}
int main()
{
rectangulo rect(10,20);
cout << "Volumen: " << volumen (rect,5);
return 0;
}
Los objetos son variables y, por tanto, tienen las mismas capacidades y atributos que
cualquier otro tipo de variables. Por consiguiente, es perfectamente posible disponer
objetos en un array. La sintaxis para declarar un array de objetos es exactamente la
misma que la utilizada para declarar un array de cualquier otro tipo de variable. El
acceso a los array de objetos es igual al de los arrays de otros tipos de variables.
Ejemplo:
#include <iostream>
class cuadrado
{
private:
float lado;
public:
void intro_lado (float l) {lado=l;}
float area() {return lado*lado;}
float perimetro() {return 4*lado;}
};
int main()
{
cuadrado ob[4];
ob[1].intro_lado(5);
ob[2].intro_lado(10);
cout << "Área = " << ob[1].area();
cout << "Perimetro = " << ob[2].perimetro();
return 0;
}
#include <iostream>
class cuadrado
{
private:
float lado;
public:
cuadrado (float l) {lado=l;}
float area() {return lado*lado;}
};
int main()
{
cuadrado ob[4] = {1, 5, 4, 10};
for (int i=0;i<4;i++)
cout << "Área = " << ob[i].area();
return 1;
}
Ejemplo:
cuadrado ob[2][4] = {2, 3, 5, 7 4, 6, 9, 8};
Ejemplo:
#include <iostream>
class Rectangulo
{
private:
int ancho, largo;
public:
Rectangulo (int a, int b) {largo=a; ancho=b;}
int area() {return ancho*largo;}
};
int main()
{
Rectangulo ob[4]= {ob(2,3), ob(2,4), ob(3,5), ob(4,5)};
return 0;
}
Como para cualquier otra variable se puede acceder a los objetos mediante punteros.
Cuando se utiliza un puntero a un objeto, las funciones miembro del objeto se
referencian utilizando el operador flecha (->) en lugar del operador punto (.).
La aritmética de puntero a un objeto es igual a la de cualquier otro tipo de datos.
Ejemplo:
#include <iostream>
class Samp
{
private:
int a, b;
public:
Samp (int n, int m){a = n; b = m;}
int get_a() {return a;}
int get_b() {return b;}
};
int main()
{
Samp ob[4] = {Samp(1, 2), Samp (3, 4), Samp (5, 6), Samp (7,8)};
int i;
Samp *p;
p = ob;
for (i=0; i<4; i++)
{
cout << p->get_a() << ' ';
cout << p->get_b() << "\n";
p++;
}
return 0;
}
C++ consta de un puntero especial llamado this. Este es un puntero que se pasa
automáticamente a cualquier miembro cuando se invoca. Es un puntero al objeto que
genera la llamada. Por ejemplo, dada la siguiente sentencia:
ob.f1(); // se entiende que ob es un objeto
la función f1() recibe automáticamente un puntero a ob, que es el objeto que genera la
llamada. Este puntero se referencia como this.
Es importante entender que sólo se pasa a los miembros punteros "this". Por ejemplo,
una función amiga no tiene puntero this.
Cuando se llama a un miembro se pasa automáticamente un puntero "this" al objeto
que provocó la llamada.
Ejemplo:
#include <iostream>
#include <string.h>
class Inventario
{
private:
char nombre[20];
double coste;
int existencias;
public:
Inventario (char *nom, double c, int e)
{
strcpy (nombre, nom);
coste = c;
existencias = e;
}
void ver();
};
void Inventario::ver()
{
cout << "nombre:" << this->nombre << "\n";
cout << "precio de coste: " << this->coste << "\n";
cout << "existencias: " << this->existencias << "\n";
}
int main()
{
Inventario ob("Llave ", 4.95, 4);
ob.ver();
return 0;
}
C++ proporciona un método más conveniente y seguro para asignar y liberar memoria
que C. En C++, puede asignarse memoria utilizando new y liberarse mediante delete.
Estos operadores adoptan la siguiente forma general:
#include <iostream>
p_var=new <tipo>;
delete p_var;
En este caso, tipo es el especificador del tipo del objeto para el que se asigna memoria
y p_var es un puntero a este tipo. new es un operador que devuelve un puntero a la
memoria asignada dinámicamente. delete devuelve la memoria cuando ya no se
necesita.
Si no hay suficiente memoria disponible para atender la petición, new devuelve un
puntero nulo. Del mismo modo debe llamarse a delete sólo con un puntero obtenido
previamente mediante new. Si se llama a delete con un puntero incorrecto, se puede
bloquear el sistema.
Ejemplo:
#include <iostream>
main()
{
int *p; p = new int;
if (!p)
{
cout << "Error de asignación";
return 1;
}
*p = 1000;
cout << "El entero contenido en p es: " << *p << "\n";
delete p;
return 0;
}
Ejemplo:
#include <iostream>
class Ejemplo
{
private:
int i, j;
public:
void inicializa (int a, int b) {i = a; j = b;}
int producto() {return i * j;}
};
main()
{
Ejemplo *p;
p = new Ejemplo;
if (!p)
{
cout << "Error de asignación \n";
return 1;
}
p->inicializa(4,5);
cout << "El producto es: " << p->producto() << "\n";
delete p;
return 0;
}
Un objeto asignado dinámicamente puede tomar un valor inicial utilizando esta forma
de la sentencia new:
p_var = new <tipo> (valor inicial);
Ejemplo:
#include <iostream>
class Ejemplo
{
private:
int i, j;
public:
void inicializa (int a, int b) {i = a; j = b;}
~Ejemplo() {cout << "Destrucción ...\n";}
int producto() {return (i * j);}
};
main()
{
Ejemplo *p;
int cont;
p = new Ejemplo[10]; // Asignación dinámica de un array de objetos
if (!p)
{
cout << "Error de asignación \n";
return 1;
}
for (cont = 0; cont < 10; cont ++)
p[cont].inicializa(cont, cont);
for (cont = 0; cont < 10; cont ++)
{
cout << "Producto [" << cont << "] es: ";
cout << p[cont].producto() << "\n";
}
delete[] p;
return 0;
}
21. Referencias.
Una referencia es un puntero implícito que, a todos los efectos, se comporta como
cualquier otro nombre para una variable. Existen tres modos de utilizar una referencia:
1. Una referencia puede pasarse a una función.
2. Una referencia puede ser devuelta por una función.
3. Pueden crearse referencias independientes.
El uso más importante de una referencia es como parámetro de una función.
main()
{
int i = 0;
f(&i);
cout << "Este es el nuevo valor de i " << i << "\n";
return 0;
}
En un programa C, ésta es la única manera de realizar una llamada por referencia. Sin
embargo, en C++ puede automatizarse completamente este proceso utilizando un
parámetro por referencia, de la siguiente manera:
#include <iostream>
void f(int &n);
main()
{
int i = 0;
f(i);
cout << "Este es el valor de i: " << i << "\n";
return 0;
}
Para declarar una variable o parámetro por referencia el nombre de la variable debe ir
precedido por &. Cuando una variable (n en el ejemplo anterior) se declara como una
referencia, ya no es necesario, e incluso es erróneo, aplicar el operador *. En su lugar,
cada vez que se utiliza n dentro de la función f() automáticamente es considerada como
un puntero al argumento utilizado para llamar a la función.
main()
{
int a=5, b=10;
cout << "El valor de a es: " << a << "\n";
cout << "El valor de b es: " << b << "\n";
intercambia(5,10);
cout << "El nuevo valor de a es: " << a << "\n";
cout << "El nuevo valor de b es: " << b << "\n";
return 0;
}
#include <iostream>
class Miclase
{
private:
int x;
public:
Miclase (int n)
{
x = n;
cout << "Construcción " << x << "\n";
}
~Miclase () {cout << "Destrucción " << x <<"\n";}
int id() {return x;}
};
void f(Miclase o)
{
cout << "Recibido " << o.id() << "\n";
}
main()
{
Miclase x(1);
f(x);
return 0;
}
Ejemplo:
#include <iostream>
int &f();
int x;
main()
{
f() = 100;
cout << x << "\n";
return 0;
}
int &f()
{
return x;
}
La función f() devuelve una referencia. Por tanto, cuando f() se utiliza en el lado
izquierdo de una sentencia de asignación, la asignación se producirá sobre la referencia
devuelta por f(). Puesto que f() devuelve una referencia a x, es x la que recibe el valor
100.
Como es sabido, en C y en C++ no se comprueban los límites del array. Por consiguiente,
es posible sobrepasar o no alcanzar los límites del mismo. Sin embargo, en C++, puede
crearse una clase array que realice automáticamente la comprobación de los límites. La
clase array contiene dos funciones esenciales: una que almacena la información dentro
del array y otra que la recupera. Estas funciones pueden comprobar en tiempo de
ejecución, que no se sobrepasan los límites del array.
class Array
{
private:
int size;
char *p;
public:
Array (int num);
char &put (int i);
char get (int i);
};
Array::Array (int num)
{
p = new char [num];
if (!p)
{
cout << "Error de asignación";
exit (1);
}
size = num;
}
// Almacena la información dentro del array.
char &Array::put (int i)
{
if (i < 0 || i >= size)
{
cout << "Error en límites";
exit (1);
}
return p[i];
}
// Extrae información del array.
char Array::get(int i)
{
if (i < 0 || i >= size)
{
cout << "Error en límites";
exit (1);
}
return p[i];
}
main()
{
Array a(10);
a.put (3) = 'x';
a.put (2) = 'R';
cout << a.get(3) << a.get(2) << "\n";
a.put (11) = ‘g’; //Error de límites
return 0;
}