Lista, która przechowuje elementy różnych typów

0

Witam,
mam do napisania program: Listę, która przechowuje elementy różnych typów (tzn. każdy element może przechowywać dane innego typu) + interfejs do dodawania i usuwania elementów listy. Do tego mam zrobić zapis i odczyt do/z pliku (strumieniami).

Oto co wypociłem, ale za dużo razy mi się powtarza w klasach praktycznie to samo i chciałbym to jakoś wrzucić pod template, ale nie wiem jak... I do tego chciałbym zrobić jakoś tak, że wystarczyłoby dołączyć odpowiednią klasę, aby dodać obsługę nowego typu (bez ingerencji w kod źródłowy)

main.cpp

#include <string>
#include <iostream>
#include <stdio.h>
#include <list>
#include <fstream>
#include "classes.h"
using namespace std;

int main()
{ 
   char type;

   Calk *nr1 = new Calk();
   Rzecz *nr2 = new Rzecz(); 
   Str *nr3 = new Str(); 
   Zn *nr4 = new Zn();
   
   cout << "i - liczba calkowita\nd - liczba rzeczywista\ns - ciag znakow\nc - jeden znak\n0 - koniec dodawania\n\n";
   
   for(;;)
   {
      cout << "\nWprowadz typ danych jaki chcesz wprowadzic (i,d,s,c,0): ";
      cin >> type;
      
      if(type=='i')
      {
         int calkowita;
         cout << "Podaj wartosc (liczba calkowita): ";
         cin >> calkowita;
         nr1->add(calkowita);
         //nr1->print();
      }
      else if(type=='d')
      {
         double rzeczywista;
         cout << "Podaj wartosc (liczba rzeczywista): ";
         cin >> rzeczywista;
         nr2->add(rzeczywista);
         //nr2->print();
      }
      else if(type=='s')
      {
         string lancuch;
         cout << "Podaj wartosc (ciag znakow): ";
         cin >> lancuch;
         nr3->add(lancuch);
         //nr3->print();
      }
      else if(type=='c')
      {
         char znak;
         cout << "Podaj wartosc (jeden znak): ";
         cin >> znak;
         nr4->add(znak);
         //nr4->print();
      }
      else if(type=='0')
      {
         break;
      }
      else
      {
         cout << "inny typ...\n";
      }
   }
   
   int opcja;
   for(;;)
   {
      cout << "\n1. Dopisz wprowadzone dane do pliku\n2. Wczytaj dane z pliku\n3. Wyswietl dane\n4. Skasuj wszystkie dane\n0. Koniec\n\nOpcja nr: ";
      cin >> opcja;
      
      if(opcja==1) // zapisz do pliku
      {
         nr1->add_to_file();
         nr2->add_to_file();
         nr3->add_to_file();
         nr4->add_to_file();
      }
      else if(opcja==2) // wczytaj z pliku
      {
            
            char typ;
            int i;
            double d;
            string s;
            char c;
            
            ifstream plik;
            plik.open("dane.txt", ios::binary);
            plik.seekg(0, ios::beg);
            if(plik.is_open())
            {
               while(!plik.eof())
               {
                  plik >> typ;
                  
                  if(typ=='i')
                  {
                     plik >> i;
                     nr1->add(i);
                  }
                  else if(typ=='d')
                  {
                     plik >> d;
                     nr2->add(d);
                  }
                  else if(typ=='s')
                  {
                     plik >> s;
                     nr3->add(s);
                  }
                  else if(typ=='c')
                  {
                     plik >> c;
                     nr4->add(c);
                  }
               }
           }
      }
      else if(opcja==3) // wyswietl
      {
         cout << "Dane na liscie:\n";
         nr1->print();
         nr2->print();
         nr3->print();
         nr4->print();
      }
      else if(opcja==4) // skasuj
      {
         nr1->del();
         nr2->del();
         nr3->del();
         nr4->del();
      }
      else if(opcja==0) // koniec
      {
         delete nr1;
         delete nr2;
         delete nr3;
         delete nr4;
         break;
      }
      else
      {
         cout << "Nie ma takiej opcji wyboru\n\n";
         continue;    
      }   
          
   }

   system("PAUSE");
   return 0;
}

classes.h

#ifndef CLASSES_H
#define CLASSES_H
#include <string>
#include <iostream>
#include <stdio.h>
#include <list>
#include <fstream>

using namespace std;

/////////////////////////////////////////////////////////////////////////////////
class Calk
{
   protected:
   	int calkowita;
   	list<int> lista1;
   
   public:  	
   	//metody
   	void add(int);
   	void print();
   	void del();
   	void add_to_file();
};

void Calk::add(int calkowita)
{
   lista1.push_back(calkowita); 
}

void Calk::print()
{ 
   for(list<int>::iterator iter=lista1.begin(); iter != lista1.end(); ++iter)
   {
        cout << *iter << endl;
   }
}

void Calk::del()
{
   lista1.clear();
}

void Calk::add_to_file()
{
   for(list<int>::iterator iter=lista1.begin(); iter != lista1.end(); ++iter)
   {
      ofstream plik;
      plik.open("dane.txt",ios::app);
      plik << "\n";     
      plik << "i ";
      plik << *iter;
      plik.close();  
   }  
}
/////////////////////////////////////////////////////////////////////////////////

class Rzecz
{
   protected:
   	double rzeczywista;
   	list<double> lista1;
   
   public:  	
   	//metody
   	void add(double);
   	void print();
   	void del();
   	void add_to_file();
};

void Rzecz::add(double rzeczywista)
{
   lista1.push_back(rzeczywista);
}

void Rzecz::print()
{
   for(list<double>::iterator iter=lista1.begin(); iter != lista1.end(); ++iter)
   {
        cout << *iter << endl;
   }
}

void Rzecz::del()
{
   lista1.clear();
}

void Rzecz::add_to_file()
{
   for(list<double>::iterator iter=lista1.begin(); iter != lista1.end(); ++iter)
   {
      ofstream plik;
      plik.open("dane.txt",ios::app);
      plik << "\n";
      plik << "d ";
      plik << *iter;
      plik.close();  
   }  
}
/////////////////////////////////////////////////////////////////////////////////

class Str
{
   protected:
   	double lancuch;
   	list<string> lista1;
   
   public:  	
   	//metody
   	void add(string);
   	void print();
   	void del();
   	void add_to_file();
};

void Str::add(string rzeczywista)
{
   lista1.push_back(rzeczywista);
}

void Str::print()
{
   for(list<string>::iterator iter=lista1.begin(); iter != lista1.end(); ++iter)
   {
        cout << *iter << endl;
   }
}

void Str::del()
{
   lista1.clear();
}

void Str::add_to_file()
{
   for(list<string>::iterator iter=lista1.begin(); iter != lista1.end(); ++iter)
   {
      ofstream plik;
      plik.open("dane.txt",ios::app);
      plik << "\n";
      plik << "s ";
      plik << *iter;
      plik.close();  
   }  
}
/////////////////////////////////////////////////////////////////////////////////
class Zn
{
   protected:
   	char lancuch;
   	list<char> lista1;
   
   public:  	
   	//metody
   	void add(char);
   	void print();
   	void del();
   	void add_to_file();
};

void Zn::add(char rzeczywista)
{
   lista1.push_back(rzeczywista);
}

void Zn::print()
{
   for(list<char>::iterator iter=lista1.begin(); iter != lista1.end(); ++iter)
   {
        cout << *iter << endl;
   }
}

void Zn::del()
{
   lista1.clear();
}

void Zn::add_to_file()
{
   for(list<char>::iterator iter=lista1.begin(); iter != lista1.end(); ++iter)
   {
      ofstream plik;
      plik.open("dane.txt",ios::app);
      plik << "\n";
      plik << "c ";
      plik << *iter;
      plik.close();  
   }  
}
/////////////////////////////////////////////////////////////////////////////////

#endif

Aha i obsługę plików należy wykonać w myśl zasady "zdobywanie zasobów jest inicjowaniem" - nie wiem o co z tym chodzi... oraz mam użyć wyjątków do obsługi nieznanych typów.

1

Chyba nie dokońca o to chodziło w tym programie. W tym momencie masz po prostu kilka list, które przechowują dane jednego typu. Twoim zadaniem jest stworzenie listy, która mogłaby przechowywać dane dowolnego typu.

Możesz to rozwiązać np. tworząc klasę abstrakcyjną Typ z metodami add, delete, itp. Po tej klasie dziedziczą klasy definiujące już konkretne typy, implementując metody add, delete, itp. W ten sposób chcąc dodać kolejny typ dziedziczysz po klasie Typ i implementujesz odpowiednie metody. Dodatkowo tworzysz klasę Lista, która będzie przechowywała wskaźniki na klasę bazową Typ, oraz zawierała odpowiednie metody - w ten sposób mając możliwość dodawania dowolnych typów.

1

Sorki, trochę namieszałem :) Metody w add i delete w klasie bazowej Typ są Tobie do niczego nie potrzebne (tak samo w klasach po niej dziedziczących). W klasie Typ wystarczą metody do zapisu i odczytu ze strumieni.

0

Ale czy listy (#include <list> ) mogą przechowywać wiele typów zmiennych naraz?
Np. list<int> lista1 - przechowuje inty. Czy można jakoś zmienić typ listy1, żeby zachował inty, a przyjmował od teraz np. double?

0

list<void *>? :P Ewentualnie unia typów: int32, int64, float, double, char, void *. Tzn trochę prymitywów do wyboru, a struktury trzymane jako wskaźniki do nich.

0

Lub jakaś klasa typu any.

0

Poprawiłem trochę kod:

#include <string>
#include <iostream>
#include <stdio.h>
#include <list>
#include <fstream>
#include "classes.h"
using namespace std;

class Typ
{
   public:
   virtual void print();
};

void Typ::print()
{
    cout << "\n* Do nothing *\n";        
}

//.......................................//

class Integer : public Typ
{
   public:
   int iteg;
   
   public:
   Integer(int);
   void print();
};

Integer::Integer(int i) // konstruktor
{
   iteg = i;
}

void Integer::print()
{
   cout << iteg << endl;;    
}

//.......................................//

class Double : public Typ
{
   public:
   double doub;
   
   public:
   Double(double);
   void print();  
};

Double::Double(double d) // konstruktor
{
   doub = d;
}

void Double::print()
{
   cout << doub << endl;
}

//.......................................//

class String : public Typ
{
   public:
   string str;
   
   public:
   String(string);
   void print();
};

String::String(string s) // konstruktor
{
   str = s;
}

void String::print()
{
   cout << str << endl;;    
}

//.......................................//
   
void printPersonList(list<Typ*> s) // wyslietlanie
{
   for(list<Typ*>::const_iterator i = s.begin(); i != s.end(); i++)
   {
      (*i)->print();
   }
}

//''''''''''''''''''''''''''''''''''''''//

int main()
{
   String *str1 = new String("testing");
   Integer *int1 = new Integer(7);
   Integer *int2 = new Integer(8);
   Double *dbl1 = new Double(3.14);
   Integer *int3 = new Integer(15);
   
   list<Typ*> os; // ...
   os.push_back(int1);
   os.push_back(int2);
   os.push_back(str1);
   os.push_back(dbl1);
   os.push_back(int3);
   
   printPersonList(os); // ...wypisz   
	
   delete int1;
   delete int2;
   delete int3;
   delete str1;
   delete dbl1;
   
   system("PAUSE");
   return 0;
}

Ale mam pytanie - jak zrobić do tego interfejs użytkownika? W sensie że user może zacząć dodawać do programu np. dowolną ilość intów - jak tworzyć n-ty obiekt klasy Integer?

0

Troszkę bez sensu bo robisz każdy typ (klase reprezentującą typ) osobno. Lepiej by było zrobić 2 klasy.

  1. klasa abstrakcyjna np: abstract_wezel
    niech posiada tylko i wyłącznie metody wirtualne
  2. klasa szablonowa węzeł dziedzicząca po klasie abstract_wezel
    niech posiada odpowiednie konstruktory itp itd
    oraz najważniejsze to T *dana; gdzie T to rozumiem jako parametr szablonu wezel.
    oraz wskaźnik do następnego elementu czyli abstract_wezel *next; -> to ważne że wskaźniki do elementów listy będą wskazywać na klase podstawową a nie pochodną (w ten sposób pozbyliśmy się problemu z różnymi typami)
    I teraz, żeby stworzyć nowy element do listy to robisz:
    [code]
    TYP wartosc; (albo TYP *wartosc = new TYP(wartosc) )
    abstract_wezel nowy;
    nowy = new wezel<TYP>(wartosc)
    [/code]
    Gdzie jako TYP wstawiasz, int, double, char, string -> cokolwiek może być nawet typ complex (ale musisz go wcześniej zdefiniować) a za wartość wstawiasz to co chcesz wstawić.
    i potem możesz albo zrobić
    list<abstract_wezel
    > moja_lista i juz masz liste przechowującą dowolne elementy
    albo możesz sam zaimplementować listę, bo lista to nic innego jak poukładane elementy gdzie każdy posiada wskaźnik do następnego elementu (w przypadku listy jednokierunkowej).
    Musisz tylko pamiętać, że jak będziesz definiował nowy wskaźnik to nie robisz tak:
    [code]
    wezel nowy;
    [/code]
    tylku musisz to być wskaźnik do klasy podstawowej a za pomocą new tworzysz już element klasy pochodnej (pamiętając że to jest szablon)
    Ogólnie możesz też wykorzystać powyższy pomysł z list<void
    > ale Od razu mówię, że będziesz miał problem z rzutowaniem. Ponieważ będziesz gdzieś musiał pamiętać jaki w danym elemencie został użyty typ danych i jakiego typu jest wskaźnik do następnego elementu co jest troszke uciążliwe (wydaje mi się, że mój sposób jest prostszy).

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