file - BluWiki

Anuncio
EXAMEN FINAL DE TACCII
4º INGENIERIA INFORMÁTICA, UAM, 31-ENERO–2006
Problema 1) NOTAS: i) Se recomienda leer todo el enunciado antes de comenzar a
resolver este problema.
ii) El apartado c) es opcional. Las restantes partes del examen suman 10 puntos.
a) (1,5 puntos) Se desea desarrollar un programa en C++ que permita colocar ítems
rectangulares de tamaños diversos en un contenedor formando hileras. La longitud
máxima de las hileras es fija y los ítems se añaden colocándolos al final de la última
hilera hasta que no caben en ella, momento en que se comienza una hilera nueva
(para fijar ideas, el contenedor podría ser esta hoja de papel, los ítems podrían ser
las palabras que aparecen en este párrafo y las hileras las líneas que lo forman).
Se pide un programa que incluya la estructura de un conjunto de clases que permitan
la funcionalidad indicada y la definición de un método que añade un item al
contenedor, colocándolo al final de la última hilera o, si no hay espacio, al comienzo
de la siguiente. Las hileras tendrán una altura variable para ajustarse a la altura de
los itms que contienen. Entre cada dos hileras y entre cada dos ítems consecutivos
no se dejará ningún espacio. Los ítems contenidos en una hilera tendrán la parte más
próxima a la hilera anterior pegada a la misma. La altura de cada hilera será la
mayor de las alturas de los ítems que contiene.
b) (1,5 puntos) Se desea utilizar el programa anterior (adaptándolo o extendiéndolo)
para programar un visualizador de documentos de texto basado en MFC de acuerdo
con las siguientes especificaciones:
I. Los documentos no tienen márgenes superiores, inferiores, izquierdos ni
derechos. Los documentos están formados por un sólo párrafo en el que las
palabras se alinean a la izquierda, como en el primer párrafo de este enunciado.
Las longitudes que se mencionan a partir de aquí se miden en pixels.
II. Los párrafos están formados por palabras. Las palabras corresponden a cadenas
de caracteres, implementadas mediante la clase CString de MFC y se dibujan
mediante el método CDC::TextOut(int x, int y, CString contenido), en el que los
argumentos x e y denotan la posición del vértice más próximo al comienzo del
documento donde se va a escribir la cadena de caracteres. Las dimensiones
(altura y anchura en pixels) de una cadena de caracteres al dibujarla en la
pantalla se obtienen mediante el método CDC::GetTextExtent(CString), que
devuelve una estructura (struct) de tipo CSize con atributos cx y cy que son de
tipo int. (Nota: Se puede también utilizar la subclase CPaintDC de CDC en lugar
de esta última). Las palabras no están separadas por ningún espacio (se puede
considerar que contienen los espacios en blanco necesarios como caracteres).
Se pide un programa que incluya la estructura de un conjunto de clases que permitan
la funcionalidad indicada y la definición de un método que dibuja el documento.
c) (opcional, 1,5 puntos) Se pide un programa semejante al del apartado b) anterior
(conjunto de clases y método de dibujado) que permita visualizar documentos cuyo
contenido está formado por palabras y rectángulos. Para ello se supondrá que en la
clase CDC se dispone de un método DibujaRectangulo(int x, int y, int ancho, int
alto), en el que los argumentos x e y tienen un significado análogo al que tienen en
el método CDC::TextOut.
d) (2 puntos) Se pide un diagrama de clases que englobe en la mayor proporción
posible los programas pedidos en los apartados anteriores.
Apartado 1.a)
template <class I>
class Contenedor {
public:
int
int
vector<Hilera<I> *>
void
ancho;
alto; // Hasta la última hilera completa
hileras;
Contenedor(int ancho);
anyadir(I *);
};
template <class I>
class Hilera {
public:
Contenedor<I> *
int
int
int
vector<I *>
Bool
cont;
pos; // Altura dentro del contenedor
largo;
alto;
items;
Hilera(Contenedor<I> *cont, int pos, I *item);
anyadir(I *item);
};
// Para utilizar en las clases Contenedor e Hilera
template <class C>
class Item {
public:
int
ancho;
int
alto;
int
posx;
int
posy;
C
contenido;
};
template <class I>
void Contenedor<I>::anyadir(I *item) {
if (hileras.size() == 0) {
hileras.push_back(new Hilera<I>(this, 0, item));
return;
}
else if (hileras[hileras.size()-1]->anyadir(item)) return;
else {
alto += hileras[hileras.size()-1]->alto;
hileras.push_back(new Hilera<I>(this, alto, item));
};
};
template <class I>
bool Hilera<I>::anyadir(I *item) {
if (largo + item->ancho > cont->ancho) return false;
item->posx = largo;
item->posy = cont->alto;
items.push_back(item);
alto = max(alto, item->alto);
largo += item->ancho;
return true;
};
Apartado 1.b)
La versión que se muestra utiliza documentos y vistas; se puede hacer análogamente sin
utilizar documentos, utilizando directamente una ventana marco sin vista, con una vista
de formulario o con un diálogo.
Mediante el wizard de Visual C++ o por otro método se definen de manera estándar las
clases CContenedorMFCApp, CContenedorMFCView y CContenedorMFCDoc.
A la clase CContenedorMFCDoc se le añade un atributo vector<CString *>
contenido.
Se define una clase específica de ítems:
class ContenedorMFCItem : Item<CString *> {
ContenedorMFCItem(CString *str, CDC *cdc);
};
El constructor de esta clase asigna las dimensiones al item utilizando el resultado del
método CDC::GetTextExtent(CString):
ContenedorMFCItem(CString *str, CDC *dc) : Item<CString *>(str) {
CSize size = dc->GetTextExtent(*str);
ancho = size.cx;
alto = size.cy;
};
La clase CContenedorMFCView se hace que sea subclase de
Contenedor<ContenedorMFCItem> además de serlo de CView.
El método CContenedorMFCView::OnInitialUpdate() asigna el ancho del
contenedor y construye iterativamente un ContenedorMFCItem por cada cadena del
contenido del documento, que añade al contenedor:
void CContMFCView::OnInitialUpdate() {
CView::OnInitialUpdate();
GetDocument()->cView = this;
LPRECT rect = new tagRECT;
GetClientRect(rect);
ancho = rect->right;
for (int i = 0; i < GetDocument()->contenido.size(); i++)
anyade(CContenedorMFItem(GetDocument()->strings[i]));
};
El método de dibujado recorre las hileras y sus ítems y dibuja en las posiciones
correspondientes sus contenidos:
void CContenedorMFCView::OnDraw(CDC* pDC) {
vector<Hilera<Item<String> > > hileras = getDocument()
->getContainer()->getHileras();
for (int i = 0; i < hileras.size(); i++)
for (int j = 0; j < hileras[i]->items.size(); j++)
pDC->TextOut(hileras[i]->items[j]->posx,
hileras[i]->items[j]->posy, *hileras[i]->items[j]->str);
};
Apartado 1.c)
Los cambios a realizar con respecto al apartado anterior son:
i) Definimos la clase ItemContenido y las subclases ItemCString y ItemRectangulo:
class ItemContenido {
};
class ItemCString : public ItemContenido {
CString str;
};
class ItemRectangulo : public ItemContenido {
int ancho;
int alto;
};
El contenido del documento es un vector de punteros a ItemContenidos en lugar de
CStrings.
ii) Se crean dos subclases de la clase ContenedorMFCItem: ContenedorMFCItemString
y ContenedorMFCItemRect, que son subclases respectivamente de
Item<ItemCString> y de Item<ItemRectangulo>. Los constructores calculan las
dimensiones de los ítems de distintas formas según el caso (en un caso lo hacen como
en el apartado anterior, en otro a través del rectángulo). Además, la clase
ContenedorMFCItem tiene un método virtual abstracto draw(CDC *). Este método se
define en cada subclase mediante los métodos CDC::TextOut y
CDC::DibujaRectangulo
iii) El método de dibujado sustituye la llamada a pDC->TextOut por una llamada al
método draw sobre los ítems, con pDC como argumento:
void CContenedorMFCView::OnDraw(CDC* pDC) {
for (int i = 0; i < hileras.size(); i++)
for (int j = 0; j < hileras[i]->items.size(); j++)
hileras[i]->items[j]->draw(pDC);
};
Apartado 1.d)
Diagrama de clases para el apartado a):
Contenedor 1
Hilera
1
Item
Diagrama de clases para el apartado b) (no se incluye la clase de la aplicación ni la
cardinalidad de las relaciones):
Contenedor
Hilera
Item
ContMFCView
MFCItem
ContMFCDoc
CView
CDocument
CString
El diagrama de clases para el apartado 1.c) es análogo, excepto que la clase CString se
sustituye por la jerarquía formada por la clase ItemContenido y las subclases
ItemCString y ItemRectangulo y análogamente a la clase MFCItem se le añaden sus
dos subclases:
Contenedor
Hilera
Item
ContMFCIStr
ContMFCView
MFCItem
ContMFCDoc
ContMFCIRect
CView
CDocument
ItemCStr
ItemCont
ItemRect
Problema 2) (10 puntos) Completar el siguiente programa para que la salida sea la que
se indica:
Programa:
#include <iostream>
#include <string>
using namespace std;
class X {
public:
void f () {
cout << "Metodo f en clase X" << endl;
}
};
class Y {
public:
void f () {
cout << "Metodo f en clase Y" << endl;
}
};
void main(int argc, char* argv[])
{
X x;
Y y;
x.f();
y.f();
Z<X*> z1=&x;
Z<Y*> z2=&y;
z1.f();
z2.f();
}
Salida:
Metodo
Metodo
Metodo
Metodo
f
f
f
f
en
en
en
en
clase
clase
clase
clase
X
Y
X
Y
Solución:
template <class T>
class Z {
public:
T t;
Z (T t) : t(t) {}
void f () {
t->f();
}
};
Problema 3) (10 puntos) Escribir el código de la clase A para que la salida sea la que se
indica, y que se verifique para cualesquiera valores de (x1,x2,x3):
Programa:
#include <iostream>
#include <string>
using namespace std;
void main(int argc, char* argv[])
{
int x1=5, x2=7, x3=8;
A a1(x1), a2(x2), a3(x3);
a1.valor(); a2.valor(); a3.valor();
(a1+=a2)+=a3;
a1.valor(); a2.valor(); a3.valor();
(x1+=x2)+=x3;
cout << "Valores: " << x1 << ", " << x2
<< ", " << x3 << endl;
}
Salida:
Valor: 5
Valor: 7
Valor: 8
Valor: 20
Valor: 7
Valor: 8
Valores: 20, 7, 8
Solución:
class A {
public:
int x;
A (int x) : x(x) {}
void valor () {
cout << "Valor: " << x << endl;
}
A& operator+= (const A& a) {
x=x+a.x;
return *this;
};
};
Problema 4) Suponer un sistema de realización de preguntas basadas en menús, en el
cual se formulan preguntas a un usuario y éste debe averiguar la respuesta correcta. Por
ejemplo, un menú basado en números para dos iteraciones sería del estilo siguiente
(“ejemplo 1”):
Pregunta: Capital de Francia
1. Londres
2. Roma
3. Paris
4. Caracas
Introduce el numero correcto
1
Respuesta incorrecta
Pregunta: Capital de Francia
1. Londres
2. Roma
3. Paris
4. Caracas
Introduce el numero correcto
3
Respuesta correcta
Cada menú corresponde a un estilo distinto. Por ejemplo, otro caso sería el estilo
correspondiente a los menús basados en subcadenas, en donde el usuario debe responder
con las dos primeras letras de la opción ante las mismas preguntas formuladas con este
otro tipo de menú. En este caso el ejemplo (“ejemplo 2”) sería:
Pregunta:
- Londres
- Roma
- Paris
- Caracas
Introduce
Pa
Respuesta
Pregunta:
- Londres
- Roma
- Paris
- Caracas
Introduce
Ca
Respuesta
Capital de Francia
las dos primeras letras de la solucion
correcta
Capital de Francia
las dos primeras letras de la solucion
incorrecta
a) Implementar este sistema en C++ usando el patrón de “Fábrica Abstracta”
correspondiente al siguiente pseudocódigo para el método “main” en el caso de un
menú de números, de modo que la ejecución de este método “main” sea el “ejemplo 1”
anterior
int main(int argc, char* argv[])
{
<Creación de una fábrica de menús de números>
<Creación por la fábrica de un menú >
<Crear la pregunta "Capital de Francia" para el menú>
<Crear la opción "Londres" para el menú>
<Crear la opción "Roma" para el menú>
<Crear la opción "París" para el menú, como la correcta>
<Crear la opción "Caracas" para el menú>
<repetir dos veces>
<Ejecutar la acción “preguntar” en el menú>
<Ejecutar la acción “responder” en el menú>
<fin de repetición>
}
En la implementación se deberán utilizar las plantillas “vector” y “string” (para
representar colecciones de objetos y caracteres, respectivamente, sin utilizar arrays en
ningún caso), liberando la memoria dinámica utilizada. Además se debe cumplir (que es
lo que permite este patrón de diseño) que para obtener el “ejemplo 2” en lugar del
“ejemplo 1” baste con cambiar la línea de <Creación de una fábrica de menús de números>
por la correspondiente de cadenas <Creación de una fábrica de menús de subcadenas>.
Solución:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Menu {
protected:
vector<string> elementos;
string solucion;
string pregunta;
string respuesta;
public:
void crearPregunta(string s) {
pregunta=s;
}
void crearNuevaOpcion(string s, int correcta=0) {
elementos.push_back(s);
if (correcta!=0)
solucion=s;
}
void responder () {
if (respuesta==solucion)
cout << "Respuesta correcta" << endl;
else
cout << "Respuesta incorrecta" << endl;
}
virtual void preguntar () = 0;
};
class MenuDeNumeros : public Menu {
public:
virtual void preguntar () {
cout << "Pregunta: " << pregunta << endl;
for (int i=0; i<elementos.size(); i++) {
cout << (i+1) << ". " << elementos[i] << endl;
}
cout << "Introduce el numero correcto" << endl;
int j;
cin >> j;
respuesta=elementos[j-1];
}
};
class MenuDeSubcadenas : public Menu{
public:
virtual void preguntar () {
cout << "Pregunta: " << pregunta << endl;
for (int i=0; i<elementos.size(); i++) {
cout << "- " << elementos[i] << endl;
}
cout << "Introduce las dos primeras letras de la solucion" << endl;
string s;
cin >> s;
for (int j=0; j<elementos.size(); j++) {
if (elementos[j].substr(0,2)==s)
respuesta=elementos[j];
}
}
};
class FabricaDeMenus {
public:
virtual Menu* crearMenu () = 0;
};
class FabricaDeMenusDeNumeros : public FabricaDeMenus {
public:
virtual Menu* crearMenu () {
return new MenuDeNumeros();
}
};
class FabricaDeMenusDeSubcadenas : public FabricaDeMenus {
public:
virtual Menu* crearMenu () {
return new MenuDeSubcadenas();
}
};
int main(int argc, char* argv[])
{
FabricaDeMenus* f;
// f=new FabricaDeMenusDeNumeros ();
f=new FabricaDeMenusDeSubcadenas ();
Menu* m=f->crearMenu();
m->crearPregunta("Capital de Francia");
m->crearNuevaOpcion("Londres");
m->crearNuevaOpcion("Roma");
m->crearNuevaOpcion("Paris", 1);
m->crearNuevaOpcion("Caracas");
for (int i=0; i<2; i++) {
m->preguntar();
m->responder();
}
delete m;
delete f;
return 0;
}
b) Dibujar el diagrama de clases necesarias para los “ejemplo 1” y “ejemplo 2”.
Solución:
Menu
#elementos: vector<string>
#solucion: string
#pregunta: string
#respuesta
+crearPregunta(s:string): void
+crearNuevaOpcion(string,correcta:int): void
+responder(): void
+preguntar()
FabricaDeMenus
+crearMenu(): Menu
FabricaDeMenusDeSubcadenas
+crearMenu(): Menu
FabricaDeMenusDeNumeros
MenuDeNumeros
+crearMenu(): Menu
+preguntar(): void
MenuDeSubcadenas
+preguntar(): void
c) Dibujar el diagrama de secuencia correspondiente al “ejemplo 1”
un
Usuario
new
una
FabricaDeMenusDe Numeros
crearMenu
new
crearPregunta
* crearNuevaOpcion
preguntar
responder
un
MenuDeNumeros
Descargar