C++ - RUA - Universidad de Alicante

Anuncio
SEMINARIO C++
Introducción a la
Programación Orientada a Objetos
Parte III
v. 20070918
Cristina Cachero
Pedro J. Ponce de León
Depto. Lenguajes y Sistemas Informáticos - Universidad de Alicante
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
Funciones amigas
Entrada / Salida
Sobrecarga de funciones y operadores
Gestión de memoria dinámica
Atributos y métodos de clase
Implementación de relaciones entre objetos
2
C++
FUNCIONES AMIGAS

La parte privada de una clase sólo es accesible por:




Métodos (funciones miembro) de la propia clase
Funciones amigas (C++).
Función Amiga: Función NO miembro de una clase, que puede
tener acceso a la parte privada de esa clase. Rompe el principio
de “encapsulación”.
Una función se declara como amiga de una clase mediante la
palabra reservada “friend”.
class MiClase {
friend void unaFuncionAmiga(int, MiClase&);
public:
//...
private:
int datoPrivado;
};
3
C++
FUNCIONES AMIGAS
void unaFuncionAmiga(int x, MiClase& c) {
c.datoPrivado = x; // ¡OK!
}
Conceptualmente, las funciones amigas forman parte
de la interfaz de una clase (tanto como las funciones
miembro).

No es aconsejable abusar de su uso.

4
C++
FUNCIONES AMIGAS

Razones para usarlas:



Algunas funciones necesitan acceso privilegiado a
más de una clase. (por ejemplo, una función para
multiplicar un objeto Matriz por un objeto Vector).
Algunas funciones (operadores) tienen como primer
operando un objeto distinto del que llama a la
función.
Algunas funciones resultan más legibles si pasamos
todos sus argumentos a través de la lista de
argumentos.
5
C++
FUNCIONES AMIGAS

Ejemplos Funciones Amigas
class TFecha {
friend void Copiar(TFecha*,TFecha*); // por legibilidad
...
};
void Copiar( TFecha* p1, TFecha* p2 ) {
p2->dia = p1->dia;
p2->mes = p1->mes;
Preguntas:
p2->anyo = p1->anyo;
}

int main() {
TFecha p, q;
p.dia = 3;
p.setDia(3);
Copiar(&p, &q);
}


¿Hay algún error en el main?
¿Es la función Copiar una función
miembro de la clase TFecha?
¿Se os ocurre algún otro modo de
implementar la función Copiar sin
que sea amiga de TFecha?
6
C++
FUNCIONES AMIGAS

A veces, por comodidad se decide declarar TODA
UNA CLASE como amiga de otra.


En este caso todas las funciones de la clase amiga pueden
acceder a las partes privadas de la otra clase.
Sólo para clases intimamente relacionadas.
class IteradorLista { /*…*/ };
class Lista {
friend class IteradorLista;
//…
};
7
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
Funciones amigas
Entrada / Salida
Sobrecarga de funciones y operadores
Gestión de memoria dinámica
Atributos y métodos de clase
Implementación de relaciones entre objetos
8
C++
BIBLIOTECA ENTRADA/SALIDA


ifstream y ofstream son en realidad clases.
La biblioteca iostream (#include <iostream>)
define otras clases (más generales) para los flujos de
entrada y salida:

istream : flujo de entrada de caracteres
istream cin;

ostream : flujo de salida de caracteres
ostream cout;
ostream cerr;
Nota: Las operaciones de lectura y escritura sobre ‘streams’ que se
presentan a continuación funcionan para cualquier objeto de tipo
istream/ostream (no sólo cin y cout).
9
C++
BIBLIOTECA ENTRADA/SALIDA

Operaciones de salida:



Operador <<
cout << 17;
Se pueden concatenar llamadas
cout << x << y;
Algunas operaciones:


Indicar el carácter de relleno:
cout.fill(‘*’); // por defecto, espacio en blanco
Especificar el número mínimo de caracteres para la próxima operación
de salida:
cout.width(4); // por defecto, cero
cout.fill(‘*’);
cout.width(4);
cout << 12 << “+” << 1; // imprime **12+1 y no **12***+***1
11
C++
BIBLIOTECA ENTRADA/SALIDA

Operaciones de entrada


Operador >>. Definido para los tipos básicos. Salta espacios en
blanco (espacio, tabulador o salto de línea)
Entrada numérica:
int x; float y;
cin >> x >> y;
Salta espacios y lee dígitos hasta encontrar un espacio en blanco o un carácter no numérico.

Importante: si la operación encuentra un carácter no númerico, deja el flujo de entrada en estado de
error y no almacena nada en la variable;

Cuando el formato de entrada no es conocido (por ejemplo, en entrada interactiva), al leer enteros o
reales se debe comprobar tras cada lectura que la operación no ha producido error. Por tanto no es
buena idea concatenar lecturas como en el ejemplo de arriba.

13
C++
BIBLIOTECA ENTRADA/SALIDA

Operaciones de entrada para carácter
char c; cin>>c;

Salta espacios y guarda en ‘c’ el primer carácter no blanco.
c=cin.get(); // ó cin.get(c)

get() devuelve el siguiente carácter en el flujo de entrada.
No salta espacios.
14
C++
BIBLIOTECA ENTRADA/SALIDA

Operaciones de entrada para cadenas
string s; cin >> s;


Salta espacios. Almacena caracteres en ‘s’ hasta encontrar un espacio en blanco o el
final del fichero.
Lectura de una línea completa:

Con cadenas tipo C:
cin.getline(char* destino,int numcar,char delimitador=‘\n’);

Lee como máximo numcar-1 caracteres hasta encontrar el carácter delimitador o el
final del fichero. No salta espacios.
char cadena[100];
cin.getline(cadena, 100);
// equiv. a cin.getline(cadena,100,’\n’);

Con string:
getline(istream& is, string s);

Lee una línea completa de la entrada y la almacena en ‘s’ (no almacena el salto de
línea final)
15
C++
BIBLIOTECA ENTRADA/SALIDA

Otras operaciones de entrada

cin.ignore()


cin.ignore(int ncar)


descarta ‘ncar’ caracteres en cin
cin.ignore(int ncar, char delim)


descarta el siguiente carácter en cin
descarta ‘ncar’ caracteres como máximo hasta llegar al delimitador (que también se descarta)
Los métodos ignore() son útiles para limpiar el flujo de entrada tras un error de
lectura.
16
C++
BIBLIOTECA ENTRADA/SALIDA

Otras operaciones de entrada



cin.fail() : devuelve cierto si ha habido algún error al leer la entrada
cin.eof() : devuelve cierto si se ha alcanzado el final del fichero (se ha
intentado leer cuando no había nada en el buffer de entrada)
cin.clear() : recupera al stream del estado "fail".
if (!cin) {
// o ‘if (cin.fail())’: si ha habido algún error...
cin.clear();
cin.ignore(...);
// ...siguiente operación de lectura
}
Importante: cuando un flujo de entrada está en estado ‘fail’
no se puede leer nada de él.
17
C++
BIBLIOTECA ENTRADA/SALIDA

Funciones de conversión de C (<cstdlib> ó <stdlib.h>) de
cadena a número




long
double
float
long double





strtol(char* cad,char** endp, int base);
strtod(char* cad,char** endp);
strtof(char* cad,char** endp);
strtold(char* cad,char**endp)
cad: cadena a convertir
endp: Si es !=NULL, se devuelve aquí un puntero al primer
carácter no analizado
base=0 : decimal, octal si empieza con ‘0’, hexadecimal
si empieza con ‘0x’
2 <= base <= 36
$ man strtol; man strtof
18
C++
BIBLIOTECA ENTRADA/SALIDA: ejemplo
void
TFecha::leer() {
char *dAux=new char[3];
char *mAux=new char[3];
char *aAux=new char[5];
do{
cout<<"Introduce fecha formato dd/mm/aaaa (3)"<<endl;
cin.clear(); cin.ignore(100,’\n’);
cin.getline(dAux,3,'/'); //lee hasta /
cin.getline(mAux,3,'/'); //lee hasta /
cin.getline(aAux,5); //lee hasta newline
if (cin) { // sin problemas de lectura…
dia=(int)strtol(dAux,NULL,0); //string to double
mes=(int)strtol(mAux,NULL,0);
anyo=(int)strtol(aAux,NULL,0);
}
} while (!cin);
}
19
C++
BIBLIOTECA ENTRADA/SALIDA

Manipuladores



Operaciones para modifcar el estado del flujo o
formato de entrada/salida. Se insertan en los lugares
apropiados en una operación de entrada/salida.
Manipuladores sin argumentos: <iostream>
Manipuladores con argumentos: <iomanip>
20
C++
BIBLIOTECA ENTRADA/SALIDA

Algunos manipuladores sin argumentos

Numéricos: dec, hex, oct, fixed, scientific
(Para entrada y salida)
int num=12; float num_real=0.0;
cout << hex << num; // escribe ‘c’
cin >> hex >> num; //lee en formato hexadecimal
cout << 12.3456789; // 12.3457 (precision 6)
cout << fixed << 12.3456789; // 12.345679
cin >> scientific >> num_real; // 1.23456e+1
 Tienen efecto a partir del momento en que se insertan en el
flujo.
cout.unsetf(ios_base::floatfield);
// volver a formato float general

21
C++
BIBLIOTECA ENTRADA/SALIDA

Algunos manipuladores sin argumentos:

Sólo salida:
 flush (vacia buffer de entrada)
cout << “sin salto de línea” << flush;
endl (envía fin línea y vacía buffer de entrada)
cout << “con salto de línea” << endl;

22
C++
BIBLIOTECA ENTRADA/SALIDA

Manipuladores con argumentos (para salida):



setfill(int c) : fija el carácter de relleno para el siguiente dato.
setprecision(int n) : fija la conversión en coma flotante al
número de dígitos especificado. (por defecto, n=6)
setw(int n) : fija la anchura del siguiente dato en la salida.
Justifica valores numéricos a la derecha y los datos tipo carácter a la
izquierda.
cout << setw(10) << setfill(‘*’) << 12.3456 << endl;
// imprime ***12.3456
cout << setprecision(3) << 12.3456 << endl;
// imprime 12.4
cout << fixed << 12.3456 << endl;
// imprime 12.346
23
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
Funciones amigas
Entrada / Salida
Sobrecarga de funciones y operadores
Gestión de memoria dinámica
Atributos y métodos de clase
Implementación de relaciones entre objetos
24
C++
SOBRECARGA DE FUNCIONES

En C++ varias funciones pueden utilizar el mismo nombre (selector) en
el mismo ámbito, distinguiéndose por el número y tipo de sus
argumentos. Estas funciones se dice que están sobrecargadas.
 Sobrecarga de funciones miembro (en el ámbito de la clase)
class Coordenada {
public: ...
Coordenada distancia(Coordenada &op2);
Coordenada distancia(float &op2);
};

Sobrecarga de funciones amigas (en el ámbito de la aplicación).
friend Coordenada suma (int &b, Coordenada &a);
friend Coordenada suma (float &b, Coordenada &a);

En general, cualquier función se puede sobrecargar.
25
C++
SOBRECARGA DE OPERADORES


En C++ se pueden sobrecargar los operadores del
lenguaje para utilizarlos con cualquier tipo de dato,
incluso clases definidas por el usuario.
Los operadores son en realidad funciones cuyo nombre
está formado por la palabra reservada operator
seguida del operador a sobrecargar.
int operator+(int,int);
float operator+(float,int);
26
C++
SOBRECARGA DE OPERADORES

Las expresiones 1), 2) y 3) son equivalentes:
Coordenada a, b(5,3), c(10,10);
1) a=b+c;
2) a.operator=(b.operator+(c));
3) operator+(a,operator+(b,c));



En 2) los métodos operator= y operator+ deben ser
funciones miembro de la clase. El primer operando es un
objeto de la clase (el segundo puede no serlo).
En 3) los métodos no son miembros de la clase.
Se debe respetar el significado original de los operadores
para no confundir al usuario.
27
C++
SOBRECARGA DE OPERADORES. Asignación

Sobrecarga del operador de asignación (=)
TFecha& operator=(const TFecha& f) {
if (this!=&f)
// protección contra autoasignación
{
d=f.d; m=f.m; a=f.a; }
return *this;
}
Es un ejemplo de sobrecarga de un operador binario que modifica al objeto
(operando de la izquierda):

Se almacena el resultado de la operación en el propio objeto
Se devuelve referencia al objeto (esto permite concatenar operadores)

TFecha a,b,c;
a=b=c; // a.operator=(b.operator=(c));
28
C++
SOBRECARGA DE OPERADORES. Operadores binarios

Sobrecarga de un operador binario que no modifica a
los operandos:


El resultado se devuelve por valor
Ejemplo: Sobrecarga de la suma (+)
TFecha operator+(const TFecha& f) {
TFecha suma(*this);
suma.d+=f.d; suma.m+=f.m; suma.a+=f.a;
return suma;
}
TFecha a,b,c;
a+b+c; // a.operator+(b).operator+(c);
29
C++
SOBRECARGA DE OPERADORES. Entrada/salida

Sobrecarga de los operadores de entrada/salida.
cout << x; cin >> x;
 No pueden ser funciones miembro de la clase de ‘x’ porque el primer
operando no es un objeto de esa clase (es un ‘stream’).
 Se sobrecargan como funciones amigas:
friend ostream& operator<< (ostream &o, const TFecha& obj);
friend istream& operator>> (istream &o, TFecha& obj);
TFecha a,b;
cout << a << b; // operator<<(operator<<(cout,a),b);
cin >> a >> b; // operator>>(operator>>(cin,a),b);
30
C++
SOBRECARGA DE OPERADORES. Entrada/salida
class TFecha {
friend ostream& operator<< (ostream &os, const TFecha& obj);
friend istream& operator>> (istream &is, TFecha& obj);
public:
TFecha (int d=1, int m=1, int a=1900);
...
private:
int dia, mes, anyo;
};
31
C++
SOBRECARGA DE OPERADORES. Entrada/salida
ostream& operator<< (ostream &os, const TFecha& obj) {
os << obj.dia << ”/”
<< obj.mes << ”/”
<< obj.anyo; //formato D/M/A
return os;
}
Implementación:
Salida con formato del estado del objeto.
Devolver siempre referencia al primer argumento
32
C++
SOBRECARGA DE OPERADORES. Entrada/salida
istream& operator>> (istream &is, TFecha& obj) {
char *dAux=new char[3]; char *mAux=new char[3]; char *aAux=new char[5];
is.getline(dAux,3,'/'); //lee hasta /
is.getline(mAux,3,'/'); //lee hasta /
is.getline(aAux,5); //lee hasta newline
if (is) { // sin errores
obj.dia=(int)strtol(dAux,NULL,0);
obj.mes=(int)strtol(mAux,NULL,0);
obj.anyo=(int)strtol(aAux,NULL,0);
}
return is;
}
Implementación:
Lectura con formato de todos los atributos.
Leer de la entrada una sola vez (no hacer bucles).
Devolver siempre referencia al primer argumento.
33
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
Funciones amigas
Entrada / Salida
Sobrecarga de funciones y operadores
Atributos y métodos de clase
Gestión de memoria dinámica
Implementación de relaciones entre objetos
38
Atributos y métodos de clase




También llamados estáticos. Se representan subrayados en UML.
Los atributos de clase son comunes a todos los objetos de la clase.
Sólo existe una copia en memoria compartida por todos los
objetos.
Los métodos de clase sólo pueden acceder a atributos de clase
39
Atributos y métodos de clase
class TFecha {
public:
static const int semanasPorAño = 52;
static const int diasPorSemana = 7;
static const int diasPorAnyo = 365;
static string getFormato();
static boolean setFormato(string);
private:
static string cadenaFormato;
};
40
Atributos y métodos de clase:
Definición y acceso
//TFecha.cc
string TFecha::cadenaFormato = “DD/MM/AAAA”; // Definición
string TFecha::getFormato() {
return cadenaFormato;
}
boolean TFecha::setFormato(string f) {
if (formatoValido(f)) {
cadenaFormato=f;
return true;
} else return false;
}
// main.cc
int main() {
TFecha f;
cout << TFecha::semanasPorAnyo << “ “ << f.diasPorSemana << endl;
cout << TFecha::getFormato() << f.getFormato() << endl;
}
41
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
Funciones amigas
Entrada / Salida
Sobrecarga de funciones y operadores
Atributos y métodos de clase
Gestión de memoria dinámica
Implementación de relaciones entre objetos
42
C++
GESTIÓN DE MEMORIA DINÁMICA (recordatorio)
Operadores new y delete

new





delete




Dato *pDato = new Dato;
Dato *pArray = new Dato [nElem] ;
Comprobación error: if (pDato==NULL)...
Ventaja frente a array convencional: permite decidir el número de
elementos en tiempo de ejecución.
delete pDato; pDato=NULL;
delete [] pArray; pArray=NULL;
IMPORTANTE: No olvidar los corchetes en delete si los hemos usado en el
new correspondiente
43
Estructura de una Clase
C++
GESTIÓN DE MEMORIA DINÁMICA: creación

Suponed que deseamos simular un tablero de coordenadas.
int main(){
int i,j,cfila,nfilas;
cout<<"Introduce num coord por fila"<<endl;
cin>>cfila;
cout<<"Introduce num filas"<<endl;
cin>>nfilas;
TCoordenada **c=new TCoordenada*[nfilas];
//puntero a un array de nfilas punteros a Coordenada
for (i=0;i<nfilas;i++)
c[i]=new TCoordenada[cfila];
//aquí creamos array de coordenadas
...
45
Estructura de una Clase
C++
GESTIÓN DE MEMORIA DINÁMICA: trabajo
...
for (int i=0; i<nfilas;i++){
for (j=0;j<cfila;j++){
c[i][j].setCoordX(i);
c[i][j].setCoordY(j);
}
}
for (int i=0; i<nfilas;i++){
cout<<endl;
for (j=0;j<cfila;j++){
cout<<c[i][j];
}
}
...
46
Estructura de una Clase
C++
GESTIÓN DE MEMORIA DINÁMICA: destrucción
...
for (i=0; i<nfilas; i++){
delete [] c[i]; //borramos filas
c[i]=NULL;//Ojo!!!
}
delete [] c; //borramos array ptros
c=NULL;//Ojo!!!
}
47
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
Funciones amigas
Entrada / Salida
Sobrecarga de funciones y operadores
Gestión de memoria dinámica
Atributos y métodos de clase
Implementación de relaciones entre objetos
48
Implementación de relaciones
Composición
Composición: Un objeto A tiene (contiene, esta formado por) objetos B
A
1
-b
B
class A {
private:
B b;
…};
A
10
-b
B
A
0..10
-b
B
class A {
class A {
private:
private:
B b[10];
B *b[10];
…};
int num_b;
// o B *b[10];
…};
// si B tiene
// clases derivadas
A
0..*
-b
B
class A {
private:
B **b;
int num_b;
…};
50
Implementación de relaciones
Composición
A::A(): numb(0) {
A
A::~A() {
…
…
for (int i=0; i<10; i++)
for (int i=0; i<numb; i++)
{
b[i]=NULL;
0..10
-b
… }
B
class A {
private:
B *b[10];
int num_b;
…};
Inicialmente
A no contiene
ningún B
(opcional)
delete b[i]; b[i]=NULL;}
numb=0;
…}
Los componentes
B desaparecen
con A
A::addB(B& unB) {
A::A(const A& otroA) {
… if (numb<10)
…// ‘deep copy’
for (int i=0; i<num_b; i++)
b[numb++] = new B(unB);
b[i] = new B(otroA.b[i]);
…}
El objeto de
tipo A tiene su
propia copia de
componentes B
for (int i=num_b; i<MAX_B; i++)
b[i]=NULL;
…}
51
Implementación de relaciones
Composición
A::A(): numb(0) {
A
*
…
… for (int i=0; i<numb; i++)
B = new B*[MAX_B];
{ delete b[i]; b[i]=NULL; }
for (int i=0; i<MAX_B; i++)
numb=0;
delete b; b=NULL;…
b[i]=NULL;
-b
B
A::~A() {
… }
}
A::A(const A& otroA) {
…// ‘deep’ copy’
class A {
private:
B **b;
int num_b;
…};
#define MAX_B …
A::addB(B& unB) {
b = new B*[MAX_B];
… if (numb<MAX_B)
for (int i=0; i<num_b; i++)
b[i] = new B(otroA.b[i]);
b[numb++] = new B(unB);
for (int i=num_b; i<MAX_B; i++)
…}
b[i]=NULL;
…}
52
SEMINARIO C++
Introducción a la
Programación Orientada a Objetos
FIN parte III
Descargar