SEMINARIO C++
Introducción a laProgramación Orientada a Objetos
Parte 3v. 20101014
Depto. Lenguajes y Sistemas Informáticos - Universidad de Alicante
Pedro J. Ponce de León
2
1. Funciones amigas2. Entrada / Salida3. Sobrecarga de funciones y operadores4. Gestión de memoria dinámica5. Atributos y métodos de clase6. Implementación de relaciones entre objetos7. Pruebas unitarias
C++ÍNDICE
3
C++FUNCIONES AMIGAS
Función Amiga: Función NO miembro de una clase,que puede tener acceso a la parte privada de esaclase. Rompe el principio de “encapsulación”.
Una función se declara como amiga de una clasemediante la palabra reservada “friend”.
class MiClase { friend void unaFuncionAmiga(int, MiClase&);public: //...private: int datoPrivado;};
4
C++FUNCIONES AMIGAS
void unaFuncionAmiga(int x, MiClase& c) { c.datoPrivado = x; // ¡OK!}
…int main() { MiClase objeto; unaFuncionAmiga(10,objeto);}
Conceptualmente, las funciones amigas forman partede la interfaz de una clase (tanto como las funcionesmiembro).
PROHIBIDO SU USO EN POO: rompen el principio deencapsulación.
8
1. Funciones amigas2. Entrada / Salida3. Sobrecarga de funciones y operadores4. Gestión de memoria dinámica5. Atributos y métodos de clase6. Implementación de relaciones entre objetos7. Pruebas unitarias
C++ÍNDICE
9
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 deentrada y salida: istream : flujo de entrada de caracteres
istream cin;
ostream : flujo de salida de caracteresostream 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).
11
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ónde salida:cout.width(4); // por defecto, cero
cout.fill(‘*’); cout.width(4); cout << 12 << “+” << 1; // imprime **12+1 y no **12***+***1
13
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:
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 deerror y no almacena nada en la variable; Cuando el formato de entrada no es conocido (por ejemplo, en entrada interactiva), al leer enteros oreales se debe comprobar tras cada lectura que la operación no ha producido error. Por tanto no esbuena idea concatenar lecturas como en el ejemplo de arriba.
int x; float y; cin >> x >> y;
14
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.
15
C++BIBLIOTECA ENTRADA/SALIDA
Operaciones de entrada para cadenasstring s; cin >> s;
Salta espacios. Almacena caracteres en ‘s’ hasta encontrar un espacio en blanco o elfinal 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.
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)
char cadena[100]; cin.getline(cadena, 100); // equiv. a cin.getline(cadena,100,’\n’);
16
C++BIBLIOTECA ENTRADA/SALIDA
Otras operaciones de entrada
cin.ignore() descarta el siguiente carácter en cin
cin.ignore(int ncar) descarta ‘ncar’ caracteres en cin
cin.ignore(int ncar, char delim) 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 deentrada tras un error de lectura.
17
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".
Importante: cuando un flujo de entrada está en estado ‘fail’no se puede leer nada de él.
if (!cin) {
// o ‘if (cin.fail())’: si ha habido algún error...cin.clear();
cin.ignore(...);
// ...siguiente operación de lectura}
19
C++BIBLIOTECA ENTRADA/SALIDA: ejemplo
void Fecha::leer() { do{ cout<<"Introduce fecha (formato dd/mm/aaaa)” <<endl; if (!cin) { cin.clear(); cin.ignore(100,'\n'); } cin >> dia; cin.ignore(); cin >> mes; cin.ignore(); cin >> anyo; } while (!cin);}
Entrada de datos numéricos con formato
24
1. Funciones amigas2. Entrada / Salida3. Sobrecarga de funciones y operadores4. Gestión de memoria dinámica5. Atributos y métodos de clase6. Implementación de relaciones entre objetos7. Pruebas unitarias
C++ÍNDICE
25
En C++ varias funciones pueden utilizar el mismo nombre(selector) en el mismo ámbito, distinguiéndose por el número ytipo de sus argumentos. Estas funciones se dice que estánsobrecargadas. Sobrecarga de funciones miembro (en el ámbito de la clase)
class Coordenada {
public: ...
Coordenada distancia(Coordenada &op2);
Coordenada distancia(float &op2);
};
En general, cualquier función se puede sobrecargar.
C++SOBRECARGA DE FUNCIONES
26
En C++ se pueden sobrecargar los operadores dellenguaje 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 operatorseguida del operador a sobrecargar.int operator+(int,int);
float operator+(float,int);
C++SOBRECARGA DE OPERADORES
27
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 serfunciones miembro de la clase. El primer operando es unobjeto 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.
C++SOBRECARGA DE OPERADORES
28
Sobrecarga del operador de asignación (=)
C++SOBRECARGA DE OPERADORES. Asignación
Fecha& operator=(const Fecha& 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 objetoSe devuelve referencia al objeto (esto permite concatenar operadores)
Fecha a,b,c;a=b=c; // a.operator=(b.operator=(c));
38
1. Funciones amigas2. Entrada / Salida3. Sobrecarga de funciones y operadores4. Atributos y métodos de clase5. Gestión de memoria dinámica6. Implementación de relaciones entre objetos7. Pruebas unitarias
C++ÍNDICE
39
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 directamente a
atributos de clase
40
Atributos y métodos de clase
class Fecha { 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;};
41
Atributos y métodos de clase:Definición y acceso
//Fecha.cc// Definición de un atributo de clase no constantestring Fecha::cadenaFormato = “DD/MM/AAAA”;
// Definición de metodo estáticostring Fecha::getFormato() { return cadenaFormato;}
// main.cc (Acceso)int main() { Fecha f; cout << Fecha::semanasPorAnyo << “ “ << f.diasPorSemana << endl; cout << Fecha::getFormato() << “ “ << f.getFormato() << endl;}
42
1. Funciones amigas2. Entrada / Salida3. Sobrecarga de funciones y operadores4. Atributos y métodos de clase5. Gestión de memoria dinámica6. Implementación de relaciones entre objetos7. Pruebas unitarias
C++ÍNDICE
43
C++GESTIÓN DE MEMORIA DINÁMICA (recordatorio)
Operadores new y delete new
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
delete pDato; pDato=NULL;
delete [] pArray; pArray=NULL;
IMPORTANTE: No olvidar los corchetes en delete si los hemos usado en elnew correspondiente
44
C++GESTIÓN DE MEMORIA DINÁMICA
Array de objetos Hay que guardar memoria para dos conceptos
El array en sí mismo Cada uno de los objetos que componen el array Con variables automáticas, C++ permite hacer ambas cosas en una
sola línea:Naipe arrayDeCartas[52];
//invoca a ctor. por defecto de Naipe para cadacomponente
Con memoria dinámica:Naipe *arrayDeCartas[52];arrayDeCartas = new *Naipe[52]; // Array de punteros
for (int i=0; i<52; i++) arrayDeCartas[i] = new Naipe(…);
// permite invocar a ctores. sobrecargados
49
1. Funciones amigas2. Entrada / Salida3. Sobrecarga de funciones y operadores4. Atributos y métodos de clase5. Gestión de memoria dinámica6. Implementación de relaciones entre objetos
C++ÍNDICE
50
Implementación de relacionesAsociación/Agregación -> mediante punteros
class Asignatura {...};
class Persona
{
private:
string nombre;
string dni;
vector<Asignatura*> amatr;public:
Persona() : amatr() {} ~Persona() {
amatr.clear(); // no destruye los objetos Asignatura }
void setNombre(string n);
string getNombre();
bool anyadeAsig(Asig &a) {
… amatr.push_back(&a); … }};
Persona- string nombre- string dni
…+ bool anyadeAsig(Asignatura &a)
Asignatura- string nombre- string créditos
…+ bool asignaAlumno(Persona &p)
0..10 - amatr
52
Implementación de relacionesComposición
class A {
private:
B b;…};
A
1 - b
B
class A {
private:
static const int MAXB=10; vector<B*> b; public:
A() {for (int i=0; i<10; i++)
b.push_back(new B());…}
…};
A
10 - b
B
A
0..10 - b
B
A
0..* - b
B
Composición: Un objeto A tiene (contiene, esta formado por) objetos B
class A { private:
static const intMAXB=10;
vector<B*> b;…};
class A { private:
vector<B*> b;…};
53
Implementación de relacionesComposición
A
0..10 - b
B
A::A(): b() { … }
A::addB(B& unB) {
… if (b.size()<MAXB)
b.push_back(new B(unB));
…}
A::~A() {
…
for (int i=0; i<b.size(); i++)
{ delete b[i]; b[i]=NULL; }
b.clear();
…}
A::A(const A& otroA)
: b(otroA.b.size()) {
…// ‘deep copy’
for (int i=0; i<b.size(); i++)
b[i] = new B(*(otroA.b[i]));
…}
InicialmenteA no contiene
ningún B
El objeto detipo A tiene supropia copia decomponentes B
Los componentesB desaparecen
con A
class A { private:
static const int MAXB=10;
vector<B*> b;…};
54
Implementación de relacionesComposición
A
* - b
B
A::A(): b() { … }
A::addB(const B& unB) {
… b.push_back(new B(unB));
…}
A::~A() {
…
for (int i=0; i<b.size(); i++)
{ delete b[i]; b[i]=NULL; }
b.clear();
…}
A::A(const A& otroA) {
…// ‘deep’ copy’
for (int i=0; i<otroA.b.size(); i++)
b.push_back(new B(*(otroA.b[i])));
…
class A {
private: vector<B*> b;
…};
55
1. Funciones amigas2. Entrada / Salida3. Sobrecarga de funciones y operadores4. Atributos y métodos de clase5. Gestión de memoria dinámica6. Implementación de relaciones entre objetos7. Pruebas unitarias
C++ÍNDICE
56
• La interfaz de una clase debe ser probada de manera sistemática.
• Una prueba unitaria comprueba que un método determinado, anteuna llamada determinada, se comporta como se espera.
• Se realiza un caso de prueba unitaria para cada función no trivial o métodode forma que cada caso sea independiente del resto.
• Las pruebas se realizan mediante aserciones
C++PRUEBAS UNITARIAS
58
Herramientas de pruebaSon unas colecciones de clases con las que se puedendesarrollar casos de prueba fácilmente.
Para c++ existen multitud de herramientas:• CPPUnit.• Boost.Test.• CPPUnitLite.• NanoCPPUnit.• Unit++.• CxxTest <-- Usaremos ésta
C++PRUEBAS UNITARIAS
59
C++PRUEBAS UNITARIAS: CXXTEST
Herramienta especifica de C/C++. Se considera una de las herramientasmás sencillas y potentes en comparación con el resto.Es software libre (GNU Lesser Public License)
Sólo necesita un compilador de c++ ligeramente moderno y actualizado ysoporte para alguno de los dos lenguajes en los que se basa: Python oPerl.
60
C++PRUEBAS UNITARIAS: CXXTEST
Instalación
1. Obtener CXXTEST: http://cxxtest.sourceforge.net
2. Desempaquetar en un subdirectorio ‘test’(normalmente en el directorio de trabajo)
(para las prácticas de POO, CXXTEST vendrápreinstalado en los autocorrectores).
61
C++PRUEBAS UNITARIAS: CXXTEST
UsoLas pruebas se organizan en clases escritas en ficheros .h; Cadamétodo cuyo nombre comience por ‘test’ es ejecutado por cxxtest.
Por ejemplo:// MyTestSuite.h#include <cxxtest/TestSuite.h> class MyTestSuite : public CxxTest::TestSuite { public: void testAddition( void ) { TS_ASSERT( 1 + 1 > 1 ); TS_ASSERT_EQUALS( 2 * 2, 5 ); }};
62
C++PRUEBAS UNITARIAS: CXXTEST
Ejecución de las pruebas:
$ cxxtestgen.pl --error-printer -o runner.cpp MyTestSuite.h$ g++ -o runner runner.cpp$ ./runner
Resultado:
Running 2 tests. MyTestSuite.h:15: Expected (2 * 2 == 5), found (4 != 5) Failed 1 of 2 tests Success rate: 50%
63
C++PRUEBAS UNITARIAS: CXXTEST
TS_ASSERT
Es la prueba básica. Comprueba que una expresión escierta.
void testCalculadora(){ TS_ASSERT( suma( 0, 0 ) == 0 );}
Especificación de casos de prueba mediante aserciones
64
C++PRUEBAS UNITARIAS: CXXTEST
TS_ASSERT_EQUALS
Verifica la igualdad entre dos expresiones.Equivalente al operador == en TS_ASSERT.
void testResta(){ TS_ASSERT_EQUALS(resta(4,2),2);}
Especificación de casos de prueba mediante aserciones
65
C++PRUEBAS UNITARIAS: CXXTEST
TS_ASSERT_DELTA
Se usa para comparar si dos valores son iguales hasta delta.Básicamente se usa para números en coma flotante.
void testSqrt( void ){ TS_ASSERT_DELTA(sqrt(4.0), 2.0, 0.00001);}
Especificación de casos de prueba mediante aserciones
66
C++PRUEBAS UNITARIAS: CXXTEST
TS_ASSERT_DIFFERS
Es lo contrario a TS_ASSERT_EQUALS. Se usa paracomprobar que dos valores son distintos. Esequivalente a usar != en TS_ASSERT.
void testNumeros ( void ){ TS_ASSERT_DIFFERS(5,7);}
Especificación de casos de prueba mediante aserciones
67
C++PRUEBAS UNITARIAS: CXXTEST
TS_WARN
Muestra una lista de mensajes de cosas por hacerdel tipo “to do”.
void testToDoLista( void ){ TS_WARN( "TODO: Escribir todos los test!");
Especificación de casos de prueba mediante aserciones
68
C++PRUEBAS UNITARIAS: CXXTEST
TS_FAIL
Muestra un fallo incondicional.
void testFallo(){ TS_FAIL( "No se puede testear." );}
Especificación de casos de prueba mediante aserciones
69
C++PRUEBAS UNITARIAS: CXXTEST
Dada una clase a evaluar, basta incluir su .h en el fichero de pruebas.Por ej., dada la clase Coordenada de la práctica 0:
#ifndef PUNTO_TESTSUITE_H_#define PUNTO_TESTSUITE_H_
#include <cxxtest/TestSuite.h>#include "../include/Punto.h"
class PuntoTestSuite : public CxxTest::TestSuite { Punto* c00; Punto* c11;
public: void setUp() { // Código que se ejecuta antes de cada prueba c00 = new Punto; c11 = new Punto; c11->setX(1.0); c11->setY(1.0); }
void tearDown() { // Código que se ejecuta después de cada prueba delete c00; c00=NULL; delete c11; c11=NULL; } ...
70
C++PRUEBAS UNITARIAS: CXXTEST
// (Continuación)
void testFormaCanonica() { // Prueba unitaria: método cuyo nombre empieza por ‘test’ TS_ASSERT_EQUALS(c11->getX(), 1); TS_ASSERT_EQUALS(c11->getY(), 1);
// Test constructor de copia const Punto c(*c11);
TS_ASSERT_EQUALS(c.getX(),1.0); TS_ASSERT_EQUALS(c.getY(),1.0);
// Test operator= Punto c7; c7.setX(2); c7.setY(3);
c7=c7; TS_ASSERT_EQUALS(c7.getX(),2.0); TS_ASSERT_EQUALS(c7.getY(),3.0);
c7=*c11; TS_ASSERT_EQUALS(c7.getX(),1.0); TS_ASSERT_EQUALS(c7.getY(),1.0); }
};