Diseño y Programación Orientados a Objetos
1er. Sem 2009
Tarea 4: Diferentes Implementaciones para Conjuntos

Recomendación: Lea detenidamente la tarea, si algo no lo entiende consulte en clases, si es preciso se incorporarán aclaraciones al final.

Objetivos: A través de esta tarea usted practicará: Herencia en C++, copia profunda de objetos en C++, consideraciones ante presencia de miembros dato tipo puntero.

Desarrolle cada uno de los siguientes pasos:

1) Baje los archivos de la tarea desde aquí.

2) Una forma de proveer distintas implementaciones para una estructura de datos en C es vía el uso de compilación condicional. En esta se usa define una constante para seleccionar la versión que deseamos compilar. Los archivos del directorio conditionalCompilation muestran esta estrategia.

2.1) Los archivos set.h, set.cpp, y testsets.cpp conforman el primer programa. Compile (Ej., g++ -g -o testArray set.cpp testsets.cpp) y corra esta versión.

   Luego recompile con la variable de compilación TREE. (Ej.,  g++ -g -o testTree -DTREE sets.cpp testsets.cpp)  Corra esta segunda versión.

2.2) ¿Por qué la versión TREE no funciona?

2.3) Modifique los archivos necesarios (sets.h, sets.cpp posiblemente) de tal manera que la  versión TREE funciones correctamente.

3) El ofrecer diferentes implementaciones de una clase o tipo de dato abstracto (ADT) es una situación común en bibliotecas. Una forma de hacerlo es usar una clase base única para proveer la parte común de la interfaz del ADT, y subclases en la que cada una se provee una diferente estructura de dato para su implementación. Los archivos del directorio usingInheritance muestran esta estrategia.

   En teoría, uno puede escribir código para manipular el ADT y éste debería operar igualmente bien en todas las estructuras de datos. En este caso dos un Arreglo y un Árbol. testset.cpp contiene ejemplos de tales códigos.
3.1)  Transfiera sus cambios pertinentes del paso 2. Compile el programa basado en herencia.

3.2 ¿Por qué el programa no compila? Haga la corrección correspondiente.

3.3) Luego corra el programa ¿Por qué este nuevo programa no funciona correctamente?

3.4) Haga los cambios necesarios y los mínimos en testset.cpp para que funcione correctamente.
 

Ponga lo siguiente en su directorio a enviar con su solución:

  1) Su versión modificada de los archivos bajo ambos directorios.   No incluya ejecutables. No cambie los nombres de los archivos.
  2) Un archivo RESPUESTAS.TXT con sus respuestas a la pregunta del paso 2.2, 3.2, y 3.3. 


Aspectos a tener en cuenta

Acceso a Miembros Protected

Los miembros protected de una clase son accesibles para las operaciones de las clases derivadas.
Esto significa que los métodos sólo pueden acceder a variables protected de objetos de su misma clase.
Veamos un ejemplo:
class Set
{
    ......
protected:
      int size; 
};

class TreeSet:  public Set
{
    .....
private:
    ....
  void copyMembers(Set& into) const;
};

void TreeSet::copyMembers(Set& into) const
{
  into.size=0;   //  PROBLEMA PORQUE NO TENEMOS ACCESO A INTO.SIZE
    .....
}

En este ejemplo, no tenemos acceso a into.size ya que es un miembro protegido de posiblemente otra clase (que herede de Set). Durante la ejecución, podría tratarse de un objeto ArraySet (que es otra clase derivada de Set ). El lenguaje niega el acceso por no garantizar que se trata de un objeto de la misma clase.

En contraste no hay problemas en:
void TreeSet::copyMembers(Set& into) const
{
  int original = size; 
    .....
}

Es OK porque estamos accediendo al size del mismo objeto (luego de la misma clase TreeSet).

Tampoco hay problemas en:

TreeSet::TreeSet(const TreeSet& s)  
 {
  size =s.size;
  root=0;
  s.copyMembers(*this);
}

Porque estamos accediendo al campo protegido de s que también es TreeSet.

Constructor Copia

En C++, definición de una nueva variable, como en:

Set s1 = s;
equivale a la definición
Set s1(s);   // A
 y no a lo que se podría pensar:
Set s1;  s1=s;  //B

La gran diferencia entre ambas es que en A se invoca al constructor de copia
 Set (const Set& s);
mientras que en B se llama al constructor elemental (o sin parámetros) y luego al operador asignación. Como la verdadera interpretación dada por el compilador corresponde a A, es preciso que se implemente el constructor de copia si queremos que esta definición funcione bien en todos los casos, incluso cuando hay punteros involucrados, en los otros casos el constructor de copia por defecto funciona bien como ocurre con el caso de arreglo.
Resultado de lo anterior considere en su tarea implementar este constructor.

En General se recomienda la siguiente regla: "Si por alguna razón usted debe implementar el operador de asignación, también implemente el constructor de copia y el destructor"  En general si usted sospecha que requiere uno de ellos, lo más seguro es que requerirá los otros dos también.

Si hay dudas escriban.... además de la clase, el correo es otro medio para discutir los tópicos del ramo.