Wskaźnik na void

0

Witam, uczę się właśnie c++ i mam taki mały problem. Potrzebuje napisać program który będzie tworzył listę przechowującą dane różnego typu. I wymyśliłem sobie, że zrobie jedną klasę szablonową wezel No i klasa wezel bedzie przechowywala tylko jakas dana i wskaznik do nastepnego wezla. Typ danej zalezy od parametru template a wskaznik do nastepnego wezla to zrobilem void *next (o nie wiemy jakiego będzie typu następny wezel). i teraz jak robie w main.cpp:
[code]
void *pierwszy;
Wezel<int> *nowy = new Wezel(5);
pierwszy = nowy;
[/code]
i oczywiscie mam zaprzyjazniony przeciazony operator << w klasie wezel to o ile dziala
[code]
cout << (*nowy);
[/code]
o tyle nie dziala juz
[/code]
cout <<(pierwszy);
[/code]
Wyskakuje komunikat, że "error: ‘void
’ is not a pointer-to-object type"
Jak temu zaradzić?

0

Jak dla mnie to błąd w designie. Zrób klasę szablonową Lista która ma klasę wewnętrzną Węzeł przechowującą wartość typu T oraz wskaźnik na kolejny Węzeł. Dodatkowo obiekty klasy Lista przechowują sobie head (i ew tail). Chyba ze chcesz mieć strukturę która przechowuje dane róznych typów, ale wtedy to bym jednak się zastanowił czy to danych nie trzymać pod void*.
Ogolnie problem polega na tym ze void* to moze być cokolwiek. Program nie jest w stanie tego zinterpretować, więc musiałbyś najpierw to zrzutować na odpowiedni węzeł żeby to wypisać jakoś.

0

Myślałem o takim rozwiązaniu ale wtedy wewnętrzna klasa węzeł będzie miała wskaźnik do kolejnego węzła ale tego samego typu. A w jednej liście mają być różne typy. Więc w tym momencie nie potrzebuje szablonu tylko znowu będzie dana typu void więc i tak będę musiał rzutować. DObrze myślę?

0

Tak. Ja nie widzę za bardzo innej opcji. Ale C++ nie ma mechanizmu refleksji co moze być małym utrudnieniem (bo będziesz musiał gdzieś przechować info o tym jaki to właściwie jest obiekt). Ale w takim razie może lepiej zamiast void* jakaś unia?
Chyba ze możesz te typy przechowywanych danych samemu wyspecyfikować, tzn samemu napisać implementację calej ich hierarchii wtedy mógłbyś spokojnie użyć polimorfizmu.

0

Wybrałem w sumie rozwiązanie z rzutowaniem niestety błąd non stop ten sam. Tylko zapomniałem dopisać w pierwszym poście że ten błąd nie wyskakuje mi jak robie coś takiego:
[code]
Wezel <int> *nowy = new Wezel(5);
void *pierwszy;
pierwszy = nowy_wezel;
[/code]

tylko dopiero jak chce wypisać elementy
[code]
cout << (*pierwszy);
co to w ogóle za błąd?

0

Omg. Nic nie zrozumiałeś z tego co napisałem. masz void* pierwszy. To jest wskaźnik na JAKIEŚ MIEJSCE W PAMIĘCI. Dereferencja tego wskaźnika powoduje odczytanie wartości na którą on wskazuje. Kiedy masz wskaźnik jakiegoś typu to dereferencja jest poprawna -> jeśli masz wskaźnik na int, to dereferencja odczytuje wartość 4 bajtów począwszy od tego miejsca i zwraca ci jako wartość. Jeśli masz wskaźnik do obiektu jakiejs klasy to też dokładnie wiadomo ile bajtów odczytać i jak to traktować. Jak masz void* to guzik wiadomo, dlatego dereferencja się nie powiedzie.

0

Może jeszcze prościej wytłumaczę, masz np:
int x=3;
int *w;
w=&x;
cout<<*w;
Wszystko proste, "w" to wskaźnik na int'a zaś *w to sam int.
W twoim przypadku *pierwszy to void czyli nic, obiekty takiego typu w języku C/C++ nie istnieją.

0

Już wiem o co wam chodzi, ale nadal nie rozwiązuje to mojego zadania. Shalom powiedziałeś, żeby zapisywać jaki typ danych został użyty w danym węźle. Możesz to rozwinąć? Chodzi o to, że najpierw robię węzeł trzymający int, potem double, potem sting. I jak tworze pierwszy wezel (int) to
[code]
void* nastepny
[/code]
zostaje bez zmian. Jednak jak już stworzę kolejny węzeł double to jak mam zapisać że jest on double i jak będę chciał wypisywać elementy listy to strumien będzie pobieral do rzutowania odpowiedni typ? Oczywiscie Od razu mówię, że nie może być tak że zakładam jakąś kolejność bo może być tak że najpierw dodaje 10 intów potem 1 double i 3 string albo jeszcze inaczej. za każdym uruchomieniem programu moze byc inaczej.

0

wyobraz sobie:

int const TYP_INT = 1;
int const TYP_DOUBLE = 2;
int const TYP_WTF = 3;

struct Wezel{ int rodzaj; }
struct WezelInt : Wezel { int wartosc; WezelInt (){rodzaj = TYP_INT;} }
struct WezelDouble : Wezel { double wartosc; WezelDouble (){rodzaj = TYP_DOUBLE;} }
struct WezelWTF : Wezel { WTF wartosc; WezelWTF (){rodzaj = TYP_WTF;} }

teraz:
mojnowywezel = new WezelDouble();
mojnowywezel->wartosc = 555.555;
i costam zrob z wezlem..

pozniej masz
void* wsk = .....; // skads sobie go bierzesz
Wezel* wezel = (Wezel*)wsk;
if(wezel-> rodzaj == TYP_INT)
{
WezelInt* intowy = (WezelInt*)wezel;
intowy -> wartosc = 666;
}else ....
....

to jest najprostsze wyjscie - niech po prostu węzeł pamięta czym jest.
jeżeli nie chcesz robic tego ręcznie, możesz uinteligentnic węzly dorzucając do bazowego "Węzeł" cokolwiek wirtualnego i później korzystać z dynamic_cast albo typeid żeby sprawdzac jakiego typu jest węzeł (tzn. zamiast "int rodzaj")

0

Chodziło mi mniej wiecej o to co napisał @quetzalcoatl przy czym ja bym tutaj zrobił po porostu unię jakąś (do przechowania wartości) + pole określające typ. Albo faktycznie jakąś hierarchię + dynamic_cast

1

A może coś w tym stylu:

#include <iostream>
#include <typeinfo>
using namespace std;

class Wezel
  {
   public:
   virtual ostream &out(ostream &s)const=0;
   virtual istream &inp(istream &s)=0;
   virtual const type_info &type()=0;
  };
ostream &operator<<(ostream &s,const Wezel &w) { return w.out(s); }
istream &operator>>(istream &s,Wezel &w) { return w.inp(s); }

template<typename T> class TWezel:public Wezel
  {
   private:
   T t;
   public:
   TWezel(const T &t=T()):t(t) {}
   T &operator*()const { return t; }
   T &operator*() { return t; }
   virtual ostream &out(ostream &s)const { return s<<t; }
   virtual istream &inp(istream &s) { return s>>t; }
   virtual const type_info &type() { return typeid(T); }
  };

int main()
  {
   Wezel *tb[3];
   tb[0]=new TWezel<double>(3.5);
   tb[1]=new TWezel<int>(10);
   tb[2]=new TWezel<char>('A');

   for(int i=0;i<3;++i) cout<<*tb[i]<<' ';
   cout<<endl;
   for(int i=0;i<3;++i)
     {
      if(tb[i]->type()==typeid(double))
        {
         **((TWezel<double>*)tb[i])=4.5;
        }
      if(tb[i]->type()==typeid(int))
        {
         **((TWezel<int>*)tb[i])=11;
        }
      if(tb[i]->type()==typeid(char))
        {
         **((TWezel<char>*)tb[i])='B';
        }
     }
   for(int i=0;i<3;++i) cout<<*tb[i]<<' ';
   cout<<endl;

   cin.get();
   return 0;
  }

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