Określenie hierarchii klas.

0

Hej:)
Mam zadane takie oto zadanie:

//Tego pliku nie mo?na modyfikowaa
//
//Prosze napisaa program realizuj1cy hierarchie klas wynikaj1c1 z poni?szego pliku
//
//Uwaga brak dokumentacji dyskwalifikuje zadanie - nale?y napisaa dlaczego tak a nie inaczej dziedziczymy!!!!
//
//Brak b3edów kompilacji przy zdefiniwanych COMPILE_ERROR[1-2] rownie? dyskwalifikuje zadanie!!!

#include "lab.h"

//using namespace oop;

int main(int argc, char** argv)
{
        std::auto_ptr<Vector> v1(new Vector);

  v1->push_back(new String("Test string"));
  v1->push_back(new Double(2.2));
  v1->push_back(new Point(2.2, 2.2));

        std::auto_ptr<Vector> v2(new Vector(v1->clone()));
  v2->push_back(new Vector(v1->clone()));

  std::cout << "v1:\n" << *v1 << "\n\nv2:\n" << *v2;

#ifdef COMPILE_ERROR1
        Vector test = *v1;
#endif
#ifdef COMPILE_ERROR2
        *v1 = *v1;
#endif

}
}

Zastanawiałam się nad hierarchią tych klas. Od razu narzuca mi się rozwiązanie:
class Vector {}
class String: public Vector {}
class Doube: public Vector {}
class Point: public Vector {}

Jednak nie wiem czy jest ono prawidłowe z punktu widzenia projektowania programu. Czy ktoś mógłby to sprawdzić i poprawić? Chodzi mi o samą hierarchię, nic więcej :)

0

jedynym dziedziczeniem którego ja bym użył było by
class Point : Public Double
Bo reszta jakoś ni jak ma się do siebie (nie mają nic wspólnego). Trochę tego zadania nie rozumiem. Może dlatego, że późno, może dlatego ze oglądałem zombiaki i teraz potrzebuje MOOOZGU, a może dlatego, że jestem za słaby. Lepiej żeby ktoś jeszcze się wypowiedział, ale jak dla mnie to tutaj tylko jedna klasa powinna dziedziczyć a klasa Vector powinna być szablonowa.

0

Zadanie może i jest trochę bez sensu, ale ma sprawdzać głównie umiejętność zrobienia drzewa dziedziczenia (pewnie trochę na siłę), więc domyślam się, że tych dziedziczeń ma być sporo (czytaj- więcej niż jedno, dwa)
Anybody? (:

2

Przeanalizujmy program testowy linijka po linijce.
std::auto_ptr<Vector> v1(new Vector); oznacza, że istnieje klasa Vector, która musi mieć publiczny, konstruktor domyślny.
v1->push_back(new String("Test string")); oznacza, że istnieje klasa String, która ma konstruktor przyjmujący const char* albo coś, do czego się const char* bezpośrednio konwertuje, np. std::string oraz, że Vector ma metodę push_back, która przyjmuje wskaźnik do String albo jego klasy bazowej.
Kolejne dwie linie analogicznie mówią nam to samo o klasach Double i Point, oraz to, że te klasy mają wspólną klasę bazową i to ją przyjmuje push_back.
std::auto_ptr<Vector> v2(new Vector(v1->clone())); może nam sugerować jeden z dwóch wariantów: klasa Vector ma metodę clone, która zwraca nowy obiekt klasy Vector i ma ona publiczny konstruktor kopiujący ALBO clone zwraca jakiś inny typ, a klasa Vector ma do niego konstruktor konwertujący. Póki co możemy wybrać oba warianty.
v2->push_back(new Vector(v1->clone())); znowu sugeruje dwa warianty. Pierwszy to przeładowanie push_back, które przyjmuje wskaźnik do klasy Wektor i kopiuje wszystkie elementy z wektora przekazanego do siebie. Drugi natomiast to, że przeładowania nie ma i klasa Vector również dziedziczy ze wspólnej klasy. W takim wypadku dla uproszczenia można przyjąć, że zachowa się tak jak pozostałe klasy i cały wektor będzie elementem drugiego. Pozwoliłoby to na osiągnięcie struktury drzewiastej. Być może przykładowy wynik operacji wypisania tych wektorów coś by nam zasugerował. Ja przyjmę ten drugi wariant.
std::cout << "v1:\n" << *v1 << "\n\nv2:\n" << *v2; tutaj nic skomplikowanego - istnieje globalne przeładowanie operatora << dla np. (ostream&, const Vector&).
Vector test = *v1; tutaj ma wystąpić błąd kompilacji, więc konstruktor kopiujący w klasie Vector musi być ukryty. Jednoznacznie określa to również wariant z metodą clone - musi ona zwracać coś innego - wskaźnik.
*v1 = *v1; to też ma być błąd, więc w klasie Vector musi być zablokowany operator przypisania.

Żeby ułatwić sobie sprawę z zarządzaniem pamięci, załóżmy, że wszystkie elementy będą przechowywane jako wskaźniki inteligentne - dzięki temu nie napiszemy ani jednego delete.
Proponuję następującą strukturę:
Klasa abstrakcyjna Element z czysto wirtualną metodą clone(), która zwróci wskaźnik inteligentny na sklonowany obiekt; z czysto wirtualnym operatorem << przyjmującym ostream& (który będzie wypisywał zawartość elementu) oraz pustym, wirtualnym destruktorem.
Proste klasy String, Double oraz Point, które dziedziczą z Element.
Klasa Vector, która również z niego dziedziczy oraz posiada prywatne pole typu std::vector<std::auto_ptr<Element>>, w którym przechowywane będą wszystkie jej elementy. Metoda push_back, która przyjmuje wskaźnik na Element, tworzy z niego auto_ptr i dodaje do wektora wewnętrznego. Metoda clone() stworzy nowy wektor, doda do niego kopie wszystkich swoich elementów, stworzy wskaźnik inteligentny i zwróci go. Ma także konstruktor konwertujący, który przyjmuje inteligentny wskaźnik na Element i dodaje go jako pierwszy swój element. Operator << wywoła globalny operator << dla Vector, który wywoła operator << dla wszystkich swoich elementów.
I tyle.

Zadanie jest ciekawe, zahacza o sporo zagadnień związanych z C++.

1 użytkowników online, w tym zalogowanych: 0, gości: 1