Koncepcja wzorca

0
Chcę napisać wzorzec klasy, która pozwoli mi na indeksowanie jej ciągiem znaków dowolnego typu, natomiast wartości będą przechowywane w osobnej klasie; będą obiektami innych klas. Na początek chcę napisać dodawanie nowych wartości, wyszukiwanie ich. W tej chwili zastanawiam się, jak skonstruować obiekt mojej struktury... dlatego piszę ten post. Szkielet wygląda tak:
template <class ID, class Info>
class as_tab
{
  private:
    struct at_node
    {
      at_node * next;
      ID * key;
      Info *  content;
      at_node (const ID * k, const Info * c = NULL) : next(NULL), key(k), content(c)
      {}
    };
    at_node * head;
    void Add(const ID * k, const Info * c);
    at_node * Find(const ID * k) const;
  public:
    as_tab();
    as_tab(const as_tab & at);
    as_tab & operator= (const as_tab & at);
    ~as_tab();
};
0
io_ napisał(a)

Chcę napisać wzorzec klasy, która pozwoli mi na indeksowanie jej ciągiem znaków dowolnego typu, natomiast wartości będą przechowywane w osobnej klasie; będą obiektami innych klas.

Brzmi jak hashtable, albo jak map z stla http://cplusplus.com/reference/stl/map/
To chcesz napisać?

Btw. Nie postawiłeś żadnego pytania. Znaczy, że nie masz problemu i zakładasz tutaj bloga?

0
io_ napisał(a)

W tej chwili zastanawiam się, jak skonstruować obiekt mojej struktury...

To jest pytanie. Widocznie źle przeczytałeś post.

0
Patryk27 napisał(a)
io_ napisał(a)

W tej chwili zastanawiam się, jak skonstruować obiekt mojej struktury...

To jest pytanie. Widocznie źle przeczytałeś post.

Pytanie poznajemy po tym że na końcu jest pytajnik(?) http://pl.wikipedia.org/wiki/Pytanie
Zacytowane to coś wygląda mi na zadanie oznajmujące, np.

W tej chwili zastanawiam się, jak bardzo będzie bolało walnięcie baranka o ścianę...

Czy to jest dla Ciebie pytanie?

0

Tak, chodzi o coś na kształt map. Powinno tam być pytanie: czy mogę prosić o naprowadzenie mnie na poprawny sposób skonstruowania tej struktury i ewentualne rady co do dalszej części?

0
io_ napisał(a)

Tak, chodzi o coś na kształt map. Powinno tam być pytanie: czy mogę prosić o naprowadzenie mnie na poprawny sposób skonstruowania tej struktury i ewentualne rady co do dalszej części?

Nie wiem jaki jest poprawny sposób na tworzenie czegokolwiek, ale ja bym radził zrobić to tak:

  1. Postaw sobie boost testing framework
    http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/
    http://www.beroux.com/english/articles/boost_unit_testing/?part=2
  2. Zacznij jeszcze raz, tym razem najpierw napisz proste testy np:
as_tab<int> wyniki;
wyniki["Jas"]=5;

BOOST_CHECK(wyniki["Jas"]==5);
  1. Teraz zaimplementuj w najprostszy sposób tą klasę, tak aby to się skompilowało i przeszło testy, np:
template<typename T>
class as_tab{
public:
  T & operator[](const std::string& str){
    return value;
  }
private:
  T value;
};
  1. Teraz zmodyfikuj test w taki sposób, aby był failed, czyli np.
[..]
BOOST_CHECK(wyniki["Jas"]==5);
BOOST_CHECK(wyniki["Krzys"]==0); //<-dodana linijka
 
  1. I znowu myślisz, żeby jak najmniej zrobić w klasie, a testy przeszły i dla przykładu możesz zrobić tak:
template<typename T>
class as_tab{
public:
  T & operator[](const std::string& str){
    if(key!=str){
      key=str;
      value=T(0);
    }
    return value;
  }
private:
  std::string key;
  T value;
};
  1. Znowu coś zmieniasz w testach aby zawiodły np.
[..]
BOOST_CHECK(wyniki["Krzys"]==0);
BOOST_CHECK(wyniki["Jas"]==5); //zmiana kolejnosci
 
  1. No i teraz możesz wprowadzić listę do kodu
template <typename T>
class as_tab
{
public:
  T& operator[](const std::string& str){
    Iter it = getByKey(str);   
    return it->second;
  }
private:

  typedef typename
    std::list< std::pair<std::string,T> >::iterator Iter;

  Iter getByKey(const std::string& str){
    for(Iter it= values.begin();it!=values.end();++it)
      if(it->first==str)
	return it;
    values.push_front(std::make_pair(str,T(0)));
    return values.begin();    
  }

  std::list<std::pair<std::string, T> > values;
};

Nie wiem czy można było zrobić coś prostszego, ale poszedłem o kilka kroków do przodu.

itd. dodajesz kolejne funkcjonalności, poprawiasz kod, aby był czytelny, kompilujesz i uruchamiasz testy sprawdzając czy czegoś poprawki nie zepsuły. Nie zapomnij refaktoryzować też testów. Wszystko powinno być czytelne, nawet bez pomocy komentarzy.
Aż zamiast listy, postanowisz zrobić drzewo, aby poprawić szybkość lub wymyślisz jakąś tam inna perwersję.

No, ale jak chcesz dokładną kopię funkcjonalności map to zajrzyj do katalogu include po szczegóły.

0

Dziękuję za tę odpowiedź, jest bardzo pomocna, ale muszę napisać, że niestety ogranicza mnie tak prozaiczna kwestia jak czas i nie zdążyłbym najpewniej napisać żądanego wzorca idąc tą drogą w czasie, jakim dysponuję. To nie znaczy, że przypomniałem sobie o zadaniu na kilka dni przed terminem, po prostu oprócz tego muszę opanować jeszcze zbyt wiele innych rzeczy. Szczerze chciałbym się w to zagłębić, bo zajmuję się tym ze względu na zainteresowanie, ale w tej chwili muszę poprosić o pomoc w napisaniu tego, co stanowi "podstawowe wymagania", po rozważeniu wszystkich okoliczności.

Chcę prosić o ocenę tego, co "wymyśliłem" w konstruktorze węzła. Wzorzec ma pozwalać na przechowywanie obiektów klas zawierających informacje typu opis przedmiotu, człowieka. Dostęp do poszczególnych obiektów ma odbywać się za pomocą identyfikatora, np. typu liczba, napis. Powinien udostępniać wypisywanie zawartości całej "bazy danych" obiektów, dodawanie, wyszukiwanie ich, to mój cel na początek. Napisałem przykładową klasę przechowującą informacje i konstruktor elementu bazy danych.

Nie mam jeszcze dużego doświadczenia, dlatego zwróciłem się z prośbą o pomoc w tym miejscu, będę wdzięczny za każdą wskazówkę.
class People
{
  const char * name, * occupation;
  unsigned int age;
  public:
    People(const char * n, const char * o, unsigned int a) :
    name(n), occupation(o), age(a)
    {}
    void PDisp()
    {
      cout << " Name: " << name << "\n";
      cout << " Occupation: " << occupation << "\n";
      cout << " Age: " << age << "\n";
    }
      
};
template <class ID, class Info>
class as_tab
{
  private:
    struct at_node
    {
      at_node * next;
      ID * key;
      const Info & content;
      at_node (const ID * k, const Info & c) : next(NULL)
      {
	unsigned int ksize = sizeof(k)/sizeof(k[0]);
	key = new ID[ksize];
	for (int i = 0; i < ksize; i++)
	  *(key+i) = *(k+i);
	content = c;
      }
    };
    at_node * head;
    void Add(const ID * k, const Info * c);
    at_node * Find(const ID * k) const;
  public:
    as_tab()
    {
      head = NULL;
    }
    as_tab(const as_tab & at);
    as_tab & operator= (const as_tab & at);
    ~as_tab()
    {
      while (head)
      {
        at_node * h = head->next;
        delete head;
        head = h;
      }
    }
};
0
io_ napisał(a)

Dziękuję za tę odpowiedź, ale muszę napisać, że niestety ogranicza mnie tak prozaiczna kwestia jak czas i nie zdążyłbym najpewniej napisać żądanego wzorca idąc tą drogą w czasie, jakim dysponuję. To nie znaczy, że przypomniałem sobie o zadaniu na kilka dni przed terminem, po prostu oprócz tego muszę opanować jeszcze zbyt wiele innych rzeczy. Szczerze chciałbym się w to zagłębić, bo zajmuję się tym ze względu na zainteresowanie, ale w tej chwili muszę poprosić o pomoc w napisaniu tego, co stanowi "podstawowe wymagania", po rozważeniu wszystkich okoliczności.

Acha i dlatego postanowiłeś użyć najbardziej powolnego i niegwarantującego rozwiązania sposobu jakim jest napisanie na forum? To żeś się popisał sprytem.
No chyba, że szukasz buraka który za darmochę napisze to za ciebie? Tu nie odbieram nadziei, może się tu taki trafić.

io_ napisał(a)

Chcę prosić o ocenę tego, co "wymyśliłem" w konstruktorze węzła.

Czyli tego kawałka kodu:

      at_node (const ID * k, const Info & c) : next(NULL)
      {
	unsigned int ksize = sizeof(k)/sizeof(k[0]);
	key = new ID[ksize];
	for (int i = 0; i < ksize; i++)
	  *(key+i) = *(k+i);
	content = c;
      }

ksize, dzielisz rozmiar wskaźnika, przez rozmiar typu na który wskaźnik wskazuje, ale co tam, może jest w tym geniusz, zobaczmy to w akcji, dlatego zmieniam private na public w as_tab i w mainie mogę sobie pozwolić na:

int a=3,b=6;
as_tab<int,int>::at_node node(&a,b);

Bardzo toporna obsługa, ale spróbujmy to skompilować ... nie kompiluje się, dobra poprawiam w kodzie to o co się pluł(inicjalizacja referencji i warn za porównywanie signed z unsigned), poszło. Jaką wartość ma ksize? U mnie 8/4, czyli 2. Key jest tablicą 2 intów, z czego pierwszy zawiera klucz właściwy, a w drugim skopiowane śmieci, możliwe że nie ze swojej pamięci. Dla klucza typu char(jeden znak) klucz byłby 8 elementową tablicą, fajnie.
Coś tam pisałeś, że kluczem może być też napis, czyli jaki const char*?
Zobaczmy, zmieniam maina

as_tab<const char*,int>::at_node node("Ala",b); 

I się nie kompiluje, kolizja z konstruktorem kopiującym. To zrobiło się nudne.

Do tego można zaliczyć brak dealokacji tablicy key w destruktorze(a tego tu nie ma), niesprawdzanie czy alokacja się udała, content może dostać referencję na zmienną lokalną, albo tą samą zmienną która będzie używana przy konstrukcji innych nodów np. w pętli, itd.
I jak to teraz ocenić? A może to ja robię inaczej niż ty chciałeś to używać.

Reszta kodu, to nie na moją skacowaną głowę, kiedy indziej na to zerknę.

0

Podszedłem do tego spokojnie, napisałem to prostym sposobem i chciałbym teraz zapytać: w jaki sposób najlepiej zapewnić sobie możliwość korzystania z prywatnych składowych klasy przechowującej dane, do których odwołuję się za pomocą klucza, we wzorcu?

Oto kod:

class People
{
  string name, occupation;
  unsigned int age;
  public:
    People(const string n, const string p, unsigned int a) :
    name(n), occupation(p), age(a) {}  
};

template <class ID, class Info>
class as_tab
{
... // powinien mieć dostęp do prywatnych składowych klasy przechowującej informacje - z parametru Info
}

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