Documentos de Académico
Documentos de Profesional
Documentos de Cultura
- 126 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
POO en C++
Es una extensión del lenguaje C (o C ampliado) que proporciona entre otras cosas, las bases para
la programación orientada a objetos.
La programación orientada a objetos en C++, se edifica sobre el concepto de clase.
Clases
Se entiende por clase (class) a un tipo de dato definido por el programador, que contiene la
información (atributos) para construir un objeto de dicho tipo, y el conjunto de procedimientos o
funciones (métodos) que permiten manejarlo. La forma de construir la clase, es la que garantizará
el encapsulamiento del objeto. Las uniones (union) y las estructuras (structs) han sido potenciadas
en C++, permitiendo también la implementación de clases.
Al igual que en las estructuras, a cada uno de los componentes de una clase se los denomina
miembros. A los atributos se los denomina datos miembros y a los métodos funciones miembros.
C++ incorpora, lo que se denomina nivel de acceso, este va a permitir el ocultamiento
(encapsulamiento) de miembros (atributos o métodos) específicos de la clase. Los tres niveles de
acceso que utiliza son:
public: (Público) Permite acceder al miembro, desde cualquier parte que se pueda acceder
a la clase. Todo miembro público, forma parte de la Interface de la clase.
private: (Privado). Solo puede accederse a este miembro, mediante métodos declarados
dentro de la misma clase.
protected: (Protegido). Solo puede accederse a este miembro, mediante métodos
declarados dentro de la misma clase y métodos de clases descendientes.
Tales niveles de acceso estarán presentes en class, struct y union. 17
Sintaxis
La definición de una clase se asemeja mucho a lo que ya hemos visto cuando, definíamos structs.
Esta comienza con la palabra reservada class, a continuación el nombre de la clase y entre llaves
los miembros de la clase categorizados por nivel de acceso. Las diferencias más sobresalientes, se
encuentra en los niveles de acceso y la inclusión de funciones (métodos), como miembros.
La sintaxis para una definición típica de una clase es la siguiente:
class nombclase {
tipo var_miembro_priv_1;
tipo var_miembro_priv_2;
.
.
tipo var_miembro_priv_n;
public:
funcion_miembro_1;
funcion_miembro_2; Interface de la clase o el
. objeto
.
funcion_miembro_n;
}
17
Dependiendo, que la clase se cree con struct, union o class, tienen implícitamente, definidos niveles de acceso,
estos podrán ser cambiados explícitamente.
Con struct los miembros son por defecto públicos
Con class los miembros son por defecto privados.
Con union los miembros son por defecto públicos, y no factibles de modificar explícitamente.
- 127 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
La sintaxis para la declaración de un objeto, se lo hace de la misma forma que se declaraba una
variable del tipo estructura o uniones. Una declaración típica podría ser:
punto p,p1,p2,paux;
angulo a;
Asimismo, cuando se realiza un mensaje a un objeto (o se invoca a un método; o función miembro).
p.poner(3,4);
cout << “La absisa del punto es: ” << p.getX();
otro ej.
a.ponervalor(M_PI_2); // Constante definida en MATH.H que indica pi/2
cout << “El seno(pi/2)= ” << a.seno();
Una implementación válida del método seno de la clase angulo sería:
float angulo::seno(){
return sin(v);
}
Respecto a la implementación de las funciones miembros, se introduce un símbolo no utilizado hasta
el momento, el operador “::”, denominado operador de alcance. La forma de realizar la
implementación de la función miembro, se hace como en las funciones convencionales, solo
antecediéndole al nombre de la función, el nombre de la clase y el operador de alcance.
Por ej.
void punto::poner(int x, int y){ void punto::poner(int x, int y){
?
x = x; punto::x = x;
y = y; punto::y = y;
} }
- 128 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
class punto{
int x,y; // datos miembro privados de punto
public: // miembros publicos de punto
void poner(int,int); // Permite alterar coordenadas del punto
int getx(); // devuelve la absisa del punto
int gety(); // devuelve la ordenada del punto
};
int main(void){
punto p,q;
p.poner(0,0);
q.poner(100,100);
cout << "Valor absisa: " << p.getx() <<" ordenada: "<<p.gety()<< "\n";
p.poner(5,5);
cout << "Valor absisa: " << p.getx() <<" ordenada: "<<p.gety() <<endl;
}
int punto::getx(){
return x;
}
int punto::gety(){
return y;
}
/***********************************/
/* Implementacion de Clase ANGULO */
/***********************************/
#include <math.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
class angulo{
float v; // Angulo o arco en radianes
public:
float tomarvalor(); // Devuelve el valor del ángulo
void ponervalor(float v); // Actualiza valor del ángulo
float seno();
float coseno();
float tangente();
};
int main(){
angulo a;
- 129 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
system("cls");
cout.precision(2);
a.ponervalor(M_PI_4); // Constante definida en MATH.H que indica pi/2
cout << " seno(1/4 pi)= " << a.seno() << "\n";
cout << " coseno(1/4 pi)= " << a.coseno() << "\n";
cout << "tangente(1/4 pi)= " << a.tangente() << "\n";
a.ponervalor(M_PI_2);
cout << " seno(1/2 pi)= " << a.seno() << "\n";
a.ponervalor(M_PI_2+M_PI_4);
cout << " seno(3/4 pi)= " << a.seno() << "\n";
a.ponervalor(M_PI);
cout << " coseno(pi)= " << a.coseno() << "\n";
return 0;
}
float angulo::tomarvalor(){
return v;
}
float angulo::seno(){
return sin(v);
}
float angulo::coseno(){
return cos(v);
}
float angulo::tangente(){
return tan(v);
}
La salida del programa es la siguiente:
seno(1/4 pi)= 0.71
coseno(1/4 pi)= 0.71
tangente(1/4 pi)= 1
seno(1/2 pi)= 1
seno(3/4 pi)= 0.71
coseno(pi)= -1
- 130 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
Funciones inline
Esta característica incorporada al C++, permite declarar e implementar funciones que no utilizan el
mecanismo convencional de las funciones, el cual insume muchos recursos de espacio y tiempo,
sinó que se incorpora el código donde se lo invoca. Esto no pasa a nivel codigo fuente, sino es
resuelto en las siguientes etapas. Cabe aclarar, que este mecanismo se pude utilizar
independientemente de las clases o de la programación OO. Cuando se abordó el tema funciones,
se analizaron las conveniencias de convertir un segmento de código en función, como así también
se analizaron la implementación de funciones vs. las macros. Si el procedimiento es relativamente
grande o poco invocado, se justifica la implementación convencional de una función. En C++, cuando
se implementan funciones, muy invocadas y/o de poco código, existe una alternativa a las macros,
que evita los tiempos de invocación y salida de una función. Esta alternativa, son las funciones inline.
Esto es aplicable también a las funciones miembros de una clase. Todas las funciones miembros,
que se han visto hasta el momento, son candidatas para ser inline, ya que son de una o dos líneas
de código, provocando que mejore la performance global del programa.
Se define una función en línea precediendo a la definición de la función, la palabra reservada inline.
Por ejemplo, en la clase angulo se implementó la función miembro valor. Para que esta sea
declarada “en línea”, la sintaxis debe ser:
inline float angulo::valor()
{return v;}
en el caso de las funciones miembros de una clase, existe una alternativa, para la definición inline
aún más simple que la mencionada. Es colocando el código de la función en la declaración de la
clase, sin necesidad de la palabra reservada inline. Como ejemplo reutilizaremos la clase ángulo.
class angulo{
float v; // Angulo o arco en radianes
public:
float valor(){return v;}
void ponervalor(float valor){v=valor;}
float seno() {return sin(v);}
float coseno() {return cos(v);}
float tangente() {return tan(v);}
};
Constructores
Es un mecanismo que implementa C++, para inicializar objetos. Son funciones miembros especiales,
que no tienen tipo devuelto y pueden o no tener parámetros, en las que se indican todas las
acciones necesarias para que el objeto sea inicializado. El nombre que lleva esta funcion miembro
es el mismo de la clase.
El constructor se autoinvoca, cuando se declara un objeto estático o cuando se crea un objeto
dinámico (El concepto de objeto dinámico lo veremos más adelante).
Los ejemplos que hemos visto hasta el momento, requerirían implementaciones muy simples como
la que sigue en la clase punto:
punto::punto(){
x=0; y=0;
}
- 131 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
Constructor parametrizado
Es el constructor tiene parámetros. Por ej.
punto::punto(int vx, int vy){
x= vx;
y= vy;
}
Esto permite en el momento de la declaración inicializar los miembros datos de la clase, utilizando
los argumentos de la invocación. Por ej.
punto p(1,1);
A esta forma de definir el constructor se le llama Constructor general, en el cual se les da el valor
de los argumentos del constructor, a todos los miembros datos del objeto. De la misma manera
que en el caso del constructor por defecto, la forma adecuada de implementarlo inline es la siguiente:
punto(int vx, int vy): x(vx), y(vy){}
Constructor de copia
Cuando es necesario crear objetos a partir de otros existente, se lo puede hacer a través de este
mecanismo. El constructor tiene un único argumento, la referencia constante al objeto de su misma
clase:
punto(const punto &p)
{ x= p.x;
y= p.y;
}
y análogamente a los ejemplos anteriores:
punto(const punto &p) : x(p.x), y(p.y) {}
Entonces dado algún objeto punto declarado, por ejemplo:
punto p1(10,20);
el constructor de copia se autoinvocaría en las siguientes declaraciones:
punto p2 = p1; // Declarar p2 y hacerlo igual a p1
o lo que es lo mismo:
punto p2(p1);
si el constructor de copia no es declarado, se crea implícitamente uno por defecto, que hace
exactamente lo que hicimos anteriormente, si no fuera necesario redefinir de alguna manera esa
lógica, no habría necesidad de declarar el constructor de copia.
Destructores
El destructor es quien cumple la actividad complementaria del constructor, finalizando o dandole un
cierre a las actividades comenzadas por el constructor.
Este a diferencia del constructor, nunca tendrá argumentos. Se autoinvoca, en el caso de los
objetos estáticos cuando se abandona el ambito en el cual fue declarado.
La implementación de un destructor de la clase punto, debería ser:
~punto()
El destructor toma sentido cuando se debe realizar una o más operaciones, cuando es eliminado el
objeto. Por ejemplo, eliminar asignaciones dinámicas de memoria o cerrar archivos.
- 132 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
Prueba: 10 20 30
- 133 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
En el siguiente ejemplo se crea una clase curso, que tiene los siguientes atributos
curso
-nombmat: char* = NULL
-alumnos: char*[]
-calu:int=0 Este diagrama responde a la
+curso(const char *) representación que se hace de las
+cantalu(): int clases en UML
+materia(void): char *
+agregar_alumno(const char *): void
+alumno(int): char *
18
/********************************************/
/* CURSO: Definicion de la clase <curso> */
/********************************************/
#include <string.h>
#include <stdlib.h>
#include <conio.h>
#include <iostream>
#define maxalumnos 100
using namespace std;
class curso
{ char *nombmat; // Nombre de la materia
char *alumnos[maxalumnos]; // Arreglo de nombres de alumnos
int calu; // Contador de alumnos
public:
curso(const char *);// el constructor
int cantalu() {return calu;} // Cantidad de alumnos ingresados
char *materia(void) {return nombmat;} // Devuelve nombre materia
void agregar_alumno(const char *); // Agrega un alumno al curso
char *alumno(int orden) {return alumnos[orden];} // devuelve alumno
};
int main()
{ curso pr("Programacion I"),pd("Procesamiento de datos I");// Declara de
18
Visibilidad se aplica a los componentes de un diagrama de clases.
Publica. Los elementos son accesibles desde cualquier clase de cualquier paquete. Se señalizan con un signo más [ + ] o un icono
característico.
Paquete. En Java los elementos son visibles desde cualquier clase del mismo paquete. Se señalizan con un signo tilde [ ~ ] o un icono
característico.
Protegida. Los elementos con este alcance son visibles únicamente dentro de la clase y desde las clases descendientes de ella. Se señalizan
con un signo numeral [ # ] .
Privada. Los elementos marcados son visibles únicamente dentro de la clase a la que pertenecen. Se señalizan con un signo menos [ - ] o
un icono característico.
- 134 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
objetos
// Asignaciones
pr.agregar_alumno("AREVALO, JORGE ALFREDO");
pr.agregar_alumno("ARIQUE, FEDERICO");
pr.agregar_alumno("BALBUENA, ARIEL");
pr.agregar_alumno("CASEROS, ALDO JULIAN");
pd.agregar_alumno("AYAN, ALBERTO");
pd.agregar_alumno("ALBRIEU GUZMAN, CARLOS");
pd.agregar_alumno("ARIKA, LUIS ESTEBAN");
// Impresion
system("cls");
cout << "MATERIA: "<< pr.materia() <<
" - Cant. Alumnos: " << pr.cantalu()<<"\n";
int i;
for (i = 0; i<pr.cantalu(); ++i)
cout << (i+1) << ": " << pr.alumno(i) << "\n";
- 135 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
Objetos Dinámicos
Una característica que distingue al new y al delete respecto del malloc y el free, es la construcción
y destrucción de objetos, con las actividades asociadas a estos, es decir, ante la asignación dinámica
de memoria a un objeto se dispara el constructor y viceversa con el destructor.
A continuación se muestra un ejemplo, en el cual se aplica el operador new. En este revisaremos
varios de los conceptos vistos. En el se define la clase Cstring. Además se definen las funciones
miembro que permiten el acceso a los datos.
En el siguiente ejemplo se pretende comprobar, la autoinvocación del constructor y el destructor, en
el momento de la declaración del objeto.
/*******************************************************
* NewObjeto - Creacion dinamica de objetos e *
* invocacion implicita del constructor y el destructor *
********************************************************/
#include <iostream>
#include <string.h>
using namespace std;
class CString{
char str[30];
public:
CString();
~CString(){cout << "Objeto destruido";}
char *getStr() {return str;}
};
int main(){
CString *p; // declaro puntero a CStrig
p= new CString; // creo objeto p dinámicamente, se llama al aconstructor
delete p; // elimino el objeto dinam. Se llama al destructor
return 0;
}
CString::CString(){
strcpy(str,"Cadena inicializada\n");
cout << str << "Objeto Construido\n\n";
}
Como salida del pequeño ej., se mostrará la cadena inicializada por el constructor.
Cadena inicializada
Objeto Construido
Objeto destruido
Los objetos por sí solos, no son los que dotan de potencia al C++, sino el mecanismo de herencia,
que permite que sea más fácil el mantenimiento del código desarrollado, y su futura reutilización en
nuevas aplicaciones.
- 136 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
#include <iostream>
using namespace std;
class CRectangulo{
int ancho, alto;
public:
void set_val (int, int);
int superficie() {return (ancho * alto);}
friend void duplicar(CRectangulo&); // Funcion amiga, no miembro
};
19
"C++: Donde los amigos tienen acceso a tus miembros privados" -- Gavin Russell Baker
20
Una función amiga puede declararse en cualquier sección de la clase y no se ve afectada por los modificadores private
y public, debido a que no es miembro de la clase.
- 137 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
Clases Amigas
Así como se puede definir una función amiga, también se puede definir una clase amiga,
concediendo a la primera clase el acceso a los miembros protegidos y privados de la segunda.
#include <iostream>
using namespace std;
class Cuadrado{
private:
int lado;
public:
void setlado (int a){lado=a;}
friend class Rectangulo; // permito que los metodos de Rectangulo
}; // use miembros de Cuadrado
class Rectangulo{
int ancho, alto;
public:
int superficie (){return (ancho * alto);}
void convertir(Cuadrado cua){ancho = cua.lado;alto = cua.lado;}
}; //convertir accede a la parte privada
//de Cuadrado (Cuadrado::lado).
int main (){
Cuadrado cuad;
Rectangulo rect;
cuad.setlado(4);
rect.convertir(cuad);
cout << rect.superficie();
return 0;
}
En este ejemplo, hemos declarado Rectangulo como amiga de Cuadrado para que las funciones
miembro Rectangulo pueden tener acceso a los miembros protegidos y privados de Cuadrado y
más concretamente a Cuadrado::lado.
También se ve algo nuevo al comienzo del programa, una declaración vacía de la clase Cuadrado.
Esto es necesario, porque dentro de la declaración de Rectangulo nos referimos a Cuadrado (como
un parámetro de convertir()). La definición de Cuadrado se hace más adelante, así que si no
incluimos una declaración vacía previa de Cuadrado esta clase no sería visible en la definición de
Rectangulo. Se debe considerar que las amistades no son conmutativas. En el ejemplo,
Rectangulo se considera como clase amiga de Cuadrado, pero Rectangulo no considera amiga
Cuadrado, por lo tanto Cuadrado no puede acceder a los miembros privados y protegidos de
Rectangulo. Si así se deseara, se puede además declarar a Cuadrado como amigo Rectangulo.
Otra propiedad de las amistades es la no transitividad: el amigo de un amigo, no es considerado
como amigo, a menos que se especifique de forma explícita.
- 138 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
Herencia (Generalización)
Herencia es la capacidad de un objeto (clase) para utilizar los atributos y los métodos existentes en
clases antepasadas o ascendientes. Esta permite la reutilización de código desarrollado
anteriormente.
Cuando una clase toma características o hereda de una clase de nivel superior se dice que la clase
inferior deriva de la clase superior. La flecha y su sentido indica “deriva de”.
Mediante el mecanismo de herencia una clase puede aumentar su especialización, en clases de
niveles o jerarquías inferiores. Y asimismo, clases que comparten ciertas carácterísticas, pueden
dar lugar a una nueva clase de jerarquia superior que concentre las características comunes, a
esto se le llama generalización. Algunos autores y software de diseño de diagramas de clase UML
hacen referencia a la herencia como generalización.
Una clase puede tener subclases. Por ejemplo, la clase persona puede tener las subclases
estudiante y empleado. A su vez, la clase estudiante puede tener como subclases a los de pregrado,
grado y postgrado, mientras que la clase empleado puede tener como subclases al administrativo y
al de mantenimiento. De este modo es factible crear diversas jerarquías de tipos, dependiendo del
problema a analizar.
Persona
Estudiante Empleado
Una subclase hereda los datos y los métodos, de su superclase. También puede agregar sus datos
y métodos específicos.
Cuando una subclase incluye un atributo extra, éste no afectará a la clase superior. Por otro lado,
es común que en una clase que constituye una especialización, desee modificar alguno de los
métodos heredados, para adecuar su respuesta a algún mensaje.
Tipos de herencia
Herencia simple
Un tipo derivado se crea a partir de una única clase base.
Figura
El triángulo es derivado de
figura, únicamente.
Triángulo Rectángulo
Herencia múltiple
Una clase tienen más de una ascendiente Inmediato.
Alumno Graduado
- 139 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
Herencia en C++
En C++, la herencia se maneja por medio de la derivación de clases.
La derivación de clases en C++ se rige por la siguiente sintaxis:
Metodos virtuales
Los métodos o funciones miembros virtuales en C++ son una solución que brinda el lenguaje para
redefinir métodos heredados de clases padres. Esto es lo que permite aplicar un mismo método
en objetos polimórficos. Dicho de otra manera, los métodos virtuales permiten que clases
derivadas de una misma base (clases hermanas) puedan tener diferentes versiones de un método.
Se utiliza la palabra reservada virtual para avisar al compilador que este método será polimórfico y
que en las clases derivadas existen distintas definiciones de el.
Sintaxis:
virtual tipo nom_func() {Implementación inline;} //
La declaración de virtual en un método de una clase, implica que esta es polimórfica, y que no se
utilizará directamente para instanciar objetos, sino como superclase de una jerarquía.
La posibilidad que un mismo método puede exhibir distintos comportamientos en los descendientes
de una base común, es precisamente lo que posibilita y define el polimorfismo. En estos casos se
dice que las funciones de clases descendientes se sobrescriben21 ("override") la función virtual de
la superclase, pero esta versión de la superclase puede no existir en absoluto. Es probable que en
ella solo exista una declaración del tipo virtual, sin que exista una definición explícita de la misma.
Por ejemplo, una clase vehículo podría tener las clases virtuales lavar, estacionar o manejar, y de
esta clase podrían heredar las clases automóvil, moto y bicicleta, las implementaciones de estos
métodos variarán en su lógica para cada tipo de vehículo.
# include <iostream>
using namespace std;
class Figura {// No es abstracta porque superficie está implementada
protected:
int ancho, alto;
public:
void setval ( int a, int b){ancho = a, alto = b;}
virtual int superficie() {return 0;}
};
21
Se pueden encontrar como traducciones de override a sustituir o reemplazar, aunque se popularizó en castellano
sobreescribir en el contexto de la POO.
- 140 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
};
int main () {
Rectangulo rect; // Se crea un rectangulo
Triangulo trgl; // Se crea un triangulo
Figura poli; // Instanciacion de Figura
Figura* poli1 = ▭
Figura* poli2 = &trgl;
Figura* poli3 = &poli;
poli1-> setval(4,5);
poli2-> setval(4,5);
poli3-> setval(4,5);
cout << poli1-> superficie() << endl; // Salida 20
cout << poli2-> superficie() << endl; // Salida 10
cout << poli3-> superficie() << endl; // Salida 0
return 0;
}
- 141 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
Probar con el ejemplo anterior haciendo a superficie una funcion virtual pura y a la clase abstracta.
# include <iostream>
using namespace std;
class Figura{ // Es una clase abstracta porque superficie no está
implementada
protected:
int ancho, alto;
public:
void setval ( int a, int b){ancho = a, alto = b;}
virtual int superficie() =0; // función miembro virtual pura
}; // o método abstracto
class Rectangulo: public Figura {
public:
int superficie(){return (ancho * alto);}
};
int main () {
Rectangulo rect; // Se crea un rectangulo
Triangulo trgl; // Se crea un triangulo
Figura poli; // Instanciacion de Figura
Figura* poli1 = ▭
Figura* poli2 = &trgl;
Figura* poli3 = &poli;
poli1-> setval(4,5);
poli2-> setval(4,5);
poli3-> setval(4,5);
cout << poli1-> superficie() << endl; // Salida 20
cout << poli2-> superficie() << endl; // Salida 10
cout << poli3-> superficie() << endl;
return 0;
}
- 142 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
el acceso a la función
Si en clase base y el modif. de acceso
miembro de la clase base
el miembro es: de la clase deriv. es:
desde la clase derivada es:
Private Private inaccesible
Protected Private private
Public Private private
Private public inaccesible
Protected public protected
Public public public
Modificadores de acceso de
TEGI D miembros de la clase base
RO
P
Modificadores de acceso de
la clase derivada
P RO TEGID O
PU BLIC O
INACCESIBLE
PR IVAD O
Cuando se utiliza la palabra class, el modificador de acceso está predefinido como private; cuando
se utiliza la palabra struct, está predefinido como public.
- 143 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
#include <iostream>
#include <stdlib.h>
using namespace std;
class punto{
float x,y;
public:
punto(float vx,float vy){x=vx;y=vy;}
void setpunto(float vx,float vy){x=vx;y=vy;}
float getx(){return x;}
float gety(){return y;}
};
int main()
{ system("cls");
circulo c(1.2,3.4,5.6);
cout << "Salida: ";
c.mostrar();
c.setpunto(2,6);
c.setradio(10);
c.mostrar();
return 0;
}
void circulo::mostrar(){
cout << getx() << " " << gety() << " " << radio << "\n";
}
Salida:
1.2 3.4 5.6
2 6 10
- 144 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
Composición
ordenado Punto
3..* 1
Círculo
Polígono
Radio
* *
Estilo
Color Agregación
1 Relleno 1
Aregación y composición
Como se lee esta grafica:
Un polígono tiene o se compone de 3 o más vértices que requieren su ubicación en el plano.
Asimismo, tiene un estilo que puede ser compartido por más de un poligono.
- 145 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
c) Asociación ( dirigida) dos instancias A y B están asociadas, sí previo a la relación existían ambas,
en forma independiente. La creación o desaparición de una de ellas no implica la creación o
destrucción de la otra.
Las relaciones de asociación son más débiles que las relaciones de agregación, en el sentido de
que no requieren crear un objeto nuevo a partir de otros objetos, sino únicamente que los objetos
interactúen entre sí.
Las relaciones de asociación crean enlaces entre objetos. Estos enlaces no tienen por qué ser
permanentes.
Un ejemplo clásico de relación de asociación es la relación "empresa -empleado". Podemos definir
una clase "Empresa" y una clase "Empleado" que nos permitan representar esta relación:
La asociación expresa una relación (uni o bidireccional) entre las instancias de clases conectadas.
El sentido en que se recorre la asociación se denomina direccionalidad o navegabilidad de la
asociación (puede ser unidireccional o bidireccional). Cada extremo de la asociación se
caracteriza por el rol o papel que juega cada objeto situado en cada extremo de dicha relación. La
cardinalidad o multiplicidad es el número mínimo y máximo de instancias que pueden relacionarse
con la otra instancia del extremo opuesto de la relación. Por defecto es 1. El formato en el que se
especifica es (mínima..máxima) o (mínima, máxima).
1 Uno y sólo uno (por defecto) Un auto tiene un motor.
1..* Uno a muchos (al menos uno) Un auto tiene muchas autopartes.
*..* Muchos a muchos Un auto puede tener muchos dueños o un
dueño puede tener muchos autos.
[Ubicacion]
^
|
[Punto]
^
|
[Circulo]
*/
#include <winbgim.h>
// En Proyect/Build Options/Linker Settings/Other linker options
// Pegar: -lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32
#include <iostream>
#include <stdio.h>
#define ESC 27
#define ARR 75
#define ABA 77
#define IZQ 72
#define DER 80
#define PGUP 73
#define PGDN 71
int GetX() {
return X;
- 146 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
cout<<"Ubicacion::GetX"<<endl;
}
int GetY() {
return Y;
cout<<"Ubicacion::GetY"<<endl;
}
void SetX(int X) {
Ubicacion::X= X;
cout<<"Ubicacion::SetX("<<X<<')'<<endl;
}
void SetY(int Y) {
Ubicacion::Y= Y;
cout<<"Ubicacion::SetY("<<Y<<')'<<endl;
}
};
cout<<"Punto::Punto("<<InitX<<','<<InitY<<','<<Visible<<')'<<endl;
if (Visible) {
Punto::Visible = true;
Ver();
}
else {
Punto::Visible= false;
Ocultar();
}
}
boolean GetVisible() {
return Visible;
}
void SetVisible(boolean Visible) {
Punto::Visible= Visible;
cout<<"Punto::SetVisible("<<Visible<<')'<<endl;
}
void Ver();
void Ocultar();
void Mover(int NewX, int NewY);
};
cout<<"Circulo::Circulo("<<InitX<<','<<InitY<<','<<Radio<<','<<Visible<<
')'<<endl;
SetRadio(Radio);
if (Visible) {
Circulo::Visible = true;
Ver();
}
else Circulo::Visible= false;
- 147 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
}
int GetRadio() {
return Radio;
}
void SetRadio(int Radio) {
Circulo::Radio= Radio;
}
void Ver(void);
void Ocultar(void);
void Expandir(int);
void Contraer(int);
void Mover(int, int);
};
void Punto::Ver(void) {
Visible = true;
putpixel(GetX(), GetY(), getcolor()); // utiliza el color por
defecto
cout<<"Punto::Ver"<<endl;
};
void Punto::Ocultar(void) {
Visible = false;
cout<<"Punto::Ocultar"<<endl;
putpixel(GetX(), GetY(), getbkcolor()); // utiliza el color de fondo
para borrar
};
void Circulo::Ver(void) {
cout<<"Circulo::Ver"<<endl;
Visible = true;
if (Punto::GetVisible()) Punto::Ver();
circle(GetX(),GetY(), GetRadio()); // Graficar el Circulo
}
void Circulo::Ocultar(void) {
cout<<"Circulo::Ocultar"<<endl;
unsigned int TempColor;
TempColor = getcolor(); // salvar el color actual
setcolor(getbkcolor()); // configurar el color de fondo
Visible = false;
circle(GetX(), GetY(), Radio); // borrar circulo
setcolor(TempColor); // devolver el color de graficacion
};
void Circulo::Expandir(int n) {
cout<<"Circulo::Expandir"<<endl;
Ocultar(); // borrar el circulo anterior
Radio += n;
Ver(); // Dibujar el nuevo circulo
};
- 148 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
void Circulo::Contraer(int n) {
cout<<"Circulo::Expandir"<<endl;
Ocultar(); // borrar el circulo anterior
if (Radio > 0) Radio -= n; // expandir el radio evitando que se
negativice
Ver(); // Dibujar el nuevo circulo
};
int main() {
initwindow(800,600);
Punto P(100,100,true);
getch();
cout<<endl;
P.Mover(150,100);
getch();
cout<<endl;
P.Mover(200,100);
getch();
cout<<endl;
P.Ocultar();
getch();
cout<<endl;
P.Ver();
getch();
cout<<endl;
Circulo C(300,300,100,true);
getch();
cout<<endl;
C.Contraer(10);
getch();
- 149 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
cout<<endl;
C.Mover(C.GetX(),C.GetY()+85);
getch();
cout<<endl;
C.Mover(C.GetX(),C.GetY()+92);
getch();
cout<<endl;
C.Punto::Ver();
getch();
cout<<endl;
VerUbicacionEnXY(C.GetX(),C.GetY());
getch();
cleardevice();
C.Ver();
bool seguir = true;
while (seguir) {
int ch = getch();
switch (ch) {
case ESC:
seguir = false;
break;
case '+':
C.Expandir(3);
break;
case '-':
C.Contraer(3);
break;
case 0:// Teclas especiales de doble codigo con 224
ch = getch();
switch (ch) {
case 71:
cout <<"inicio\n";
break;
case 79:
cout <<"fin\n";
break;
case ARR:
C.Mover(C.GetX()-3,C.GetY());
break;
case ABA:
C.Mover(C.GetX()+3,C.GetY());
break;
case IZQ:
C.Mover(C.GetX(),C.GetY()-3);;
break;
case DER:
C.Mover(C.GetX(),C.GetY()+3);
break;
default :
cout<<"0+"<<ch<<endl;
}
break; // del case 0:
}
}
closegraph();
return 0;
}
- 150 -
UNLaR - APUNTES DE PROGRAMACIÓN
Prof. Ing. Marcelo Daniel Camargo
El puntero this
El puntero this es una variable predefinida para todas las funciones de una clase. Este puntero
contiene la dirección del objeto concreto de la clase, al que se le está aplicando la función miembro.
Se puede decir que *this es un alias del objeto correspondiente.
Conviene tener en cuenta que cuando una función miembro se aplica a un objeto de su clase (su
argumento implícito), accede directamente a las variables miembro (sin utilizar el operador punto o
flecha), pero no tiene forma de referirse al objeto como tal, pues no le ha sido pasado explícitamente
como argumento. Este problema se resuelve con el puntero this.
En el caso de operadores miembro sobrecargados, tema que se abordará luego, el puntero this es
la forma que se utiliza para referirse al objeto al que se está aplicando el operador.
Hay que señalar que las funciones friend (funciones “no miembro”, que acceden a la clase) no
pueden usar el puntero this.
Ej.
char * alumno::getnombre()
{ return this->nombre;} /* En este caso es lo mismo que return nombre */
Un ejemplo del uso del puntero this es el retornar la dirección de memoria del objeto:alumno
Sobrecarga
Sobrecarga de funciones
La sobrecarga (overload) de funciones, es una característica que incorpora el C++, para dar soporte
al polimorfismo de la programación orientada a objetos, sin embargo, podemos hacer uso de ella sin
programar orientado a objetos.
La sobrecarga consiste en declarar varias funciones que tienen un mismo nombre, pero distinto
comportamientos. Dichas funciones se definen de forma diferente condicionado a sus argumentos.
En el momento de la ejecución se llama a una u otra función dependiendo del número y/o tipo de
argumentos en la invocación de la función. Por ejemplo, se pueden definir varias funciones para
calcular el valor absoluto de una variable, todas con el mismo nombre abs(), pero cada una acepta
un tipo de argumento diferente y con un valor de retorno diferente. Cabe aclarar, que la sobrecarga
de funciones es aplicable a cualquier funcion del C++ (de librerías estándar y de usuario) y para dar
soporte a la POO, a las que son miembros de una clase.
La sobrecarga no admite que las funciones difieran sólo en el tipo del valor de retorno, pero con el
mismo número y tipo de argumentos. De hecho, el valor de retorno no influye en la determinación
de la función que es llamada; sólo influyen el número y tipo de los argumentos.
Tampoco se admite que la diferencia sea el que en una función un argumento se pasa por valor y
en otra función, ese argumento se pasa por referencia.
Se presenta, a continuación, un ejemplo de dos funciones sobrecargadas, llamadas ambas
string_copy(), que copia cadenas de caracteres. Una de tiene dos argumentos y la otra tres.
Cada una de ellas llama a una de las funciones estándar del C: strcpy() que requiere unicamente
dos argumentos, y strncpy() que requiere a los tres. El número de argumentos en la llamada
determinará la función concreta que vaya a ser ejecutada:
inline void copiastring(char *copia, const char *original, const int longitud);
- 151 -