seminario c++ - RUA - Universidad de Alicante

Anuncio
SEMINARIO C++
Introducción a la
Programación Orientada a Objetos
Parte 3
v. 20101014
Pedro J. Ponce de León
Depto. Lenguajes y Sistemas Informáticos - Universidad de Alicante
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
7.
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
Pruebas unitarias
2
C++
FUNCIONES AMIGAS


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!
}
…
int main() {
MiClase objeto;
unaFuncionAmiga(10,objeto);
}
Conceptualmente, las funciones amigas forman parte
de la interfaz de una clase (tanto como las funciones
miembro).

PROHIBIDO SU USO EN POO: rompen el principio de
encapsulación.

4
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
7.
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
Pruebas unitarias
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 el siguiente carácter en cin
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 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: ejemplo
Entrada de datos numéricos con formato
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);
}
19
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
7.
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
Pruebas unitarias
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);
};

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 (=)
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 objeto
Se devuelve referencia al objeto (esto permite concatenar operadores)

Fecha a,b,c;
a=b=c; // a.operator=(b.operator=(c));
28
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
7.
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
Pruebas unitarias
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 directamente a
atributos de clase
39
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;
};
40
Atributos y métodos de clase:
Definición y acceso
//Fecha.cc
// Definición de un atributo de clase no constante
string Fecha::cadenaFormato = “DD/MM/AAAA”;
// Definición de metodo estático
string Fecha::getFormato() {
return cadenaFormato;
}
// main.cc (Acceso)
int main() {
Fecha f;
cout << Fecha::semanasPorAnyo << “ “ << f.diasPorSemana << endl;
cout << Fecha::getFormato() << “ “ << f.getFormato() << endl;
}
41
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
7.
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
Pruebas unitarias
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
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 cada
componente

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
44
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
49
Implementación de relaciones
Asociación/Agregación -> mediante punteros
Persona
- string nombre
- string dni
…
+ bool anyadeAsig(Asignatura &a)
0..10
- amatr
Asignatura
- string nombre
- string créditos
…
+ bool asignaAlumno(Persona &p)
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); … }
};
50
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:
static const int MAXB=10;
static const int
vector<B*> b;
MAXB=10;
public:
vector<B*> b;
A() {
…};
for (int i=0; i<10; i++)
b.push_back(new B());…}
…};
A
0..*
-b
B
class A {
private:
vector<B*> b;
…};
52
Implementación de relaciones
Composición
A::A(): b() { … }
A
0..10
-b
B
Inicialmente
A no contiene
ningún B
A::addB(B& unB) {
… if (b.size()<MAXB)
A::~A() {
…
for (int i=0; i<b.size(); i++)
{
delete b[i]; b[i]=NULL; }
b.clear();
…}
Los componentes
B desaparecen
con A
b.push_back(new B(unB));
…}
class A {
private:
static const int MAXB=10;
vector<B*> b;
…};
A::A(const A& otroA)
El objeto de
tipo A tiene su
propia copia de
componentes B
: b(otroA.b.size()) {
…// ‘deep copy’
for (int i=0; i<b.size(); i++)
b[i] = new B(*(otroA.b[i]));
…}
53
Implementación de relaciones
Composición
A::A(): b() { … }
A::~A() {
…
A
*
for (int i=0; i<b.size(); i++)
-b
B
A::addB(const B& unB) {
{
… b.push_back(new B(unB));
b.clear();
…}
delete b[i]; b[i]=NULL; }
…}
A::A(const A& otroA) {
class A {
private:
vector<B*> b;
…};
…// ‘deep’ copy’
for (int i=0; i<otroA.b.size(); i++)
b.push_back(new B(*(otroA.b[i])));
…
54
C++
ÍNDICE
1.
2.
3.
4.
5.
6.
7.
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
Pruebas unitarias
55
C++
PRUEBAS UNITARIAS
•
La interfaz de una clase debe ser probada de manera sistemática.
•
Una prueba unitaria comprueba que un método determinado, ante
una llamada determinada, se comporta como se espera.
•
Se realiza un caso de prueba unitaria para cada función no trivial o método
de forma que cada caso sea independiente del resto.
•
Las pruebas se realizan mediante aserciones
56
C++
PRUEBAS UNITARIAS
Herramientas de prueba
Son unas colecciones de clases con las que se pueden
desarrollar casos de prueba fácilmente.
Para c++ existen multitud de herramientas:
• CPPUnit.
• Boost.Test.
• CPPUnitLite.
• NanoCPPUnit.
• Unit++.
• CxxTest <-- Usaremos ésta
58
C++
PRUEBAS UNITARIAS: CXXTEST
Herramienta especifica de C/C++. Se considera una de las herramientas
má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 y
soporte para alguno de los dos lenguajes en los que se basa: Python o
Perl.
59
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).
60
C++
PRUEBAS UNITARIAS: CXXTEST
Uso
Las pruebas se organizan en clases escritas en ficheros .h; Cada
mé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 );
}
};
61
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%
62
C++
PRUEBAS UNITARIAS: CXXTEST
Especificación de casos de prueba mediante aserciones
TS_ASSERT
Es la prueba básica. Comprueba que una expresión es
cierta.
void testCalculadora()
{
TS_ASSERT( suma( 0, 0 ) == 0 );
}
63
C++
PRUEBAS UNITARIAS: CXXTEST
Especificación de casos de prueba mediante aserciones
TS_ASSERT_EQUALS
Verifica la igualdad entre dos expresiones.
Equivalente al operador == en TS_ASSERT.
void testResta()
{
TS_ASSERT_EQUALS(resta(4,2),2);
}
64
C++
PRUEBAS UNITARIAS: CXXTEST
Especificación de casos de prueba mediante aserciones
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);
}
65
C++
PRUEBAS UNITARIAS: CXXTEST
Especificación de casos de prueba mediante aserciones
TS_ASSERT_DIFFERS
Es lo contrario a TS_ASSERT_EQUALS. Se usa para
comprobar que dos valores son distintos. Es
equivalente a usar != en TS_ASSERT.
void testNumeros ( void )
{
TS_ASSERT_DIFFERS(5,7);
}
66
C++
PRUEBAS UNITARIAS: CXXTEST
Especificación de casos de prueba mediante aserciones
TS_WARN
Muestra una lista de mensajes de cosas por hacer
del tipo “to do”.
void testToDoLista( void )
{
TS_WARN( "TODO: Escribir todos
);
los test!"
67
C++
PRUEBAS UNITARIAS: CXXTEST
Especificación de casos de prueba mediante aserciones
TS_FAIL
Muestra un fallo incondicional.
void testFallo()
{
TS_FAIL( "No se puede testear." );
}
68
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;
} ...
69
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);
}
};
70
SEMINARIO C++
Introducción a la
Programación Orientada a Objetos
FIN parte III
Descargar