Stworzenie obiektu na podstawie wczytywanych danych

0

Witam mam problem jak w temacie. Chciałbym wczytać z pliku listę Ofert. Klasa Oferta zawiera w sobie posiadacza i obiekt, z tym że obiekt może być domem albo mieszkaniem. Jest klasa Obiekt, po niej dziedziczy klasa Dom i Mieszkanie. Zdefiniowałem dla każdej z nich operator >>

Kawałek funkcji wczytującej z pliku wygląda tak:

                        std::getline(plik, typ_obiektu);
			Rozpoznaj_Typ_Obiektu(typ_obiektu, aktualna);

			std::getline(plik, line);
			std::istringstream istrSS(line);
			istrSS >> aktualna.obiekt; 

Natomiast funkcja rozpoznajaca obiekt:

 void Rozpoznaj_Typ_Obiektu(std::string& typ_obiektu, Oferta& akt)
{
	if(typ_obiektu == "Dom")
		akt.obiekt = new Dom;
	if(typ_obiektu == "Mieszkanie")
		akt.obiekt = new Mieszkanie;

W klasie oferta występuje deklaracja:

//okreslenie typu obiektu posiadanego przez wlasciciela
Obiekt * obiekt;

Błąd kompilatora jest następujący:
' Error 1 error C2678: binary '>>' : no operator found which takes a left-hand operand of type 'std::istringstream' (or there is no acceptable conversion) '

Dzięki za wszelkie wskazówki.
Pozdr.

1

Musisz zrobić przeciążenie operatora >> dla klasy obiekt. Chyba w najprostszy sposób nie da się tego zrobić, wirtualne funkcje muszą znajdować się w klasie, czyli nie możesz przeciążyć operatora wczytywania ze strumienia, żeby otrzymać pożądany efekt. Natomiast możesz w przeciążonej funkcji wywołać wirtualną funkcję dla danego obiektu (pamiętaj, żeby przekazywać przez referencję, konstruktor kopiujący przekaże tylko klasę bazową). Tutaj masz przykład, zrobiony na szybko żeby sprawdzić czy się dobrze wypowiadam, proszę o nie czepianie się do stylu:

 #include <iostream>
using namespace std;
class Obiekt{
	public:
		virtual void r(){
			cout<<"a\n";
		}

};
void read(Obiekt& o){
	o.r();
}
struct B:public Obiekt{
	void r(){
		cout<<"b\n";
	}
};
int main(){
	B a;
	read(a);
}
0

Ok, nie wiem czy do końca rozumiem, ale zrobiłem coś takiego.
Dla każdej klasy wirtualne metody typu:

void Mieszkanie::Pobierz_Dane(std::string& strS)//, Mieszkanie& mieszkanie)
{
	std::istringstream istrS(strS);
	istrS >> *this;
} 

i potem już w tej funkcji wczytującej wywołuje:

aktualna.obiekt->Pobierz_Dane(line);

Niestety nie działa to tak jak powinno. Zresztą wydaję mi się, że coś knoci się przy tworzeniu obiektu, ponieważ wywołując typeid(aktualna.obiekt).name() zwraca mi Obiekt* . Niby ok, ale nie o to mi w zamyśle chodziło jak to robiłem.

2

jeżeli nie chcesz zajmować się tgo typu problemami, zainsteresuj się np. boost::serialization

a jeżeli chcesz, to Twoj pierwszy program nie ma szansy działac poprawnie, bez napisanego prawidłowo i bardzo szczegółowo operatora >> (istream, Obiekt*).
problem w tym, że probujesz używac "polimorfizmu" na opak

AAA) w C++ polimorfizm dziala tylko "na lewą stronę", czyli mając linijkę:
wskNaObiektA -> wirtualnametoda (be)
jedynym "polimorficznym" zachowaniem jest wywolanie NA obiektA jednej z wielu wersji jego wirtualnejmetody, w zaleznosci od jego faktycznego typu (nie typu zmiennej wskNaObiektA, tylko typu obiektuA na ktory ona wskazuje), tzn. jesli obiektA bedzie Żyjątkiem, Wielokomorkowcem, Zwierzeciem, Ssakiem, Naczelnym, czy Czlowiekiem, to wywola sie "metoda" ustalona przez tę właśnie klasę, zakladająć "naturalne" dziedziczenia pomiedzy nimi..

istotne jest tu, że NIE JEST ISTOTNE czym FAKTYCZNIE jest BE.
jeżeli BE jest "obiekt*", to nie wazne czy jest ono Psem, Kotem, Domem, czy Osobą, uzywa zostanie sygnatura "wirtualnametoda(Obiekt*)" - tyle że dynamicznie wzieta z "wlasciwej klasy wg. obiektuA"

to znaczy, że jeżeli chcesz operowac na Dom albo Mieszkanie poprzez Obiekt*, to NIE MASZ (sensownej) szansy na użycie polimorfizmu między nimi, jeżeli Twoja linijka wygląda tak:
cos -> bum (obiektdomlubmieszkanie)
a jedynie moglbys probowac, jesli wyglada tak:
obiektdomlubmieszkanie -> bum (cos)

natychmiast neutralizuje to probę użycia strumien>>zmiennatypuobiekt* i zmusza Cie do napisania jakiejs wirtualnej metody "wczytaj" w domie lub mieszkaniu i wywolywanie:
domlubmieszkanie -> wczytaj ( strumien )

to jest "podstawa", w ten sposob i Dom i Mieszkanie beda mialy wlasne kody wczytywania, i w ten sposob wlasciwe wczytywanie "samo sie" odpali.
a jak to sobie teraz "ładnie opakujesz", to Twoja sprawa. Mozesz np. opakowac to w operator >> na strumieniu:

operator >> (strumien, obiekt* ob)
ob -> wczytaj (strumien)

i juz! od tej pory kazde wywolanie >> na strumieniu, z podaniem jakiegos domlubmieszkanie, samo sobie wybierze wlasciwe "wczytaj", zgodnie z faktyczna klasa "ob"

BBB) istnieje jeszcze druga metoda - korzystanie z "statycznego polimorfizmu". W zasadzie, czemu masz w jednej metodzie rozpoznawac typ obiektu i go przygotowywac, a pozniej w drugiej sie glowic co to za obiekt byl i odpalac wlasciwa metode? Skoro pierwsza wykryla ze jest to DOM, niech odpali czytanie DOMu natychmiast, i tak dalej. W tym momencie, przygotowujesz sobie kod wczytywania poszczegolnych typow obiektow i kod "rozpoznawania obiektu" z:

czy dom? -> return new Dom;
czy mieszkanie -> return new Mieszkanie;

zamieniasz na:

czy dom? -> zrób nowe Dom; wczytaj Dom ze strumienia; return ów Dom
czy mieszkanie -> zrób nowe Mieszkanie; wczytaj mieszkanie ze strumienia; return owo Mieszkanie

i ponownie, jak to teraz sobie "ubierzesz", tak się wyspisz..
mozesz na przyklad uznac, ze Dom/Mieszkanie powinny miec takie konstruktory:
public:
Dom (strumien & ) { wczytywanie ze strumienia...

i wtedy:

czy dom? -> zrób nowe Dom(strumien);
czy mieszkanie -> zrób nowe Mieszkanie(strumien);

ale, mozesz tez chciec miec konstruktory "puste" i uzywac operatorow >> !

czy dom? -> DOM * = zrób nowe Dom(); cin >> *ówdom ; return ówdom

czy tez nawet globalnych funkcji:

czy dom? -> DOM * = zrób nowe Dom(); wczytaj(cin, ówdom) ; return ówdom

i czegokolwiek innego, BYLEBY "ówdom" miało typ zmiennej DOKLADNIE taki sam jak tworzonego obiektu - czyli Dom*, Mieszkanie*, Pies*, a nie "ogólne" Obiekt*.


w skrocie:

  • polimorfizm i funkcje wirtualne wymagają, aby "polimorficzność" stala po lewej stronie. metoda "do dopasowania" ma byc NA polimorficznym czyms wykonana
  • "statyczny polimorfizm" opiera sie na przeciązeniach i rozpatrywaniu podczas kompilacji. wymaga, aby typy zmiennych byly sztywne, konkretne i dokladne.

ciekawostka:
polimorfizm zwykly "lewostronny" mozesz tez sprytnie rozszerzac na "wiecej stron" poprzez użycie .. statycznego polimorfizmu. Ogólnie chodzi o DoubleDispatch widziany często w "implementacjach wzorca Visitor", i sprowadza się to do napisanie kilku (a czasem bardzo bardzo wielu) metodek pomocniczych, "odbijających piłeczkę" pomiędzy klasami "zbierających po drodze" informację kto-kim-jest:

KlasaA
wirtualna zrobCos( obiektX ) { obiektX->zrobCos(this)

KlasaB : KlasaA
wirtualna zrobCos( obiektX ) { obiektX->zrobCos(this)

KlasaC : KlasaA
wirtualna zrobCos( obiektX ) { obiektX->zrobCos(this)

KlasaD : KlasaA
wirtualna zrobCos( obiektX ) { obiektX->zrobCos(this)

wywolujac np. wskaNaKlasaA->zrobCos, w efekcie wywolanie zostanie odbite do obiektuX, ale dzieki wirtualności "zrobCos" oraz dzieki pieczolowitemu przepisaniu tej metodki wszedzie owo "THIS" będzie statycznie wiedziało, że jest typu KlasaD. jeżeli teraz ObiektX będzie miał:

KlasaX
wirtualna zrobCos( KlasaA ob) { ....
wirtualna zrobCos( KlasaB ob) { ....
wirtualna zrobCos( KlasaC ob) { ....
wirtualna zrobCos( KlasaD ob) { .... <- to wywola się to
wirtualna zrobCos( Klasa... ob) { ....

KlasaY : KlasaX
wirtualna zrobCos( KlasaA ob) { ....
wirtualna zrobCos( KlasaB ob) { ....
wirtualna zrobCos( KlasaC ob) { ....
wirtualna zrobCos( KlasaD ob) { .... <- lub to, zaleznie czy obiektX FAKTYCZNIE byl Xem czy Ykiem..
wirtualna zrobCos( Klasa... ob) { ....

czyli majac takie "krzyzowe" konstrukcje "szeroko" rozpisane, uzyskujesz, że wywolujac wskaNaKlasaA->zrobCos( wskaNaKlasaX ) to pomimo jedynie "lewostronnego polimorfizmu", w efekcie wywoła się wersja zrobCos dokladnie wiedząca że chodzi o KlasaD<->KlasaY..

tak wiec "bawic sie" mozna.. ale czy tego potrzebujesz? ocen sam

0

OK, dzięki za odpowiedzi. Metoda opisana w punkcie AAA(zresztą podobnie opisana przez 'zjarka') działa i udało mi się ją zaimplementować.
Jednak mam kolejne pytanie tym razem chodzi mi o odwrotną sytuację, a więc podczas wypisywania do pliku również chciałem użyć wypisywania do strumienia korzystając z funkcji wirtualnych lecz również tym razem kompilator daje o sobie znać.
Jednak do rzeczy:
w funkcji wypisującej mam linie następujące:

Iterator->obiekt->Wypisz_Dane(line);
			plik << line << "\n";
 

A funkcje Wypisz_Dane(std::string&)

void Mieszkanie::Wypisz_Dane(std::string& str)
{
	std::ostringstream ostrS(str);
	*this << str;
}
 

I również i tutaj zdefiniowałem operator >> dla każdej klasy pochodnej oraz klasy bazowej. (Przy okazji nasuwa mi się pytanie czy mogę wywołać zaprzyjaźniony operator funkcji bazowej dla obiektu klasy pochodnej? Żeby zwyczajnie przy definiowaniu każdego operatora dla klas bazowych nie powtarzać linijek wypisujących dane z klasy bazowej)

Błąd kompilatora:
"Error 1 error C2678: binary '<<' : no operator found which takes a left-hand operand of type 'Obiekt' (or there is no acceptable conversion) "

0

Znowu wg mnie mylisz kierunek.
Ta metoda WypiszDane miała wypisać dane do stringu?

0

Tak, wypisać do stringu, ażebym mógł w funkcji zapisującej do pliku użyć:

Iterator->obiekt->Wypisz_Dane(line);
			plik << line << "\n";
 
1

No to musisz wpisać do stringu nie ze stringa:

void Mieszkanie::Wypisz_Dane(std::string& str)
  {
   std::istringstream s(str);
   s<<*this;
   str=s.str();
  }

ale dla takich operacji radziłbym rozwiązanie rodem z C#

std::string Mieszkanie::ToString()
  {
   std::istringstream s(str);
   s<<*this;
   return s.str();
  }

wtedy wszystko się upraszcza:

plik << Iterator->obiekt->ToString() << "\n";

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