Polimorfizm - jak zastosować ??

0

Witam, pisałem juz wcześniej bo miałem problem z klasami, ale chodziło tylko o pomysł podzielenia programu na klasy. Sam program juz napisałem jednak... musze go zmienić :/

Mianowicie miałem klasę cKsiazka w której były pola np. tytuł, autor, wypozyczona oraz pole które określało co jest wpożyczane - czy "Książka" czy "Czasopismo". Nastepnie sprawdzałem to pole instrukcją IF poniewaz było mi to potrzebne do dalszej pracy. I to właśnie nie spodobało się mojemu wykładowcy :/

Teraz po lekkim tutningu mam cos takiego :

class cPozycja {
 private:
        AnsiString tytul;
        bool wypozyczone;
        int data_oddania;
        bool spoznione;
        int kto_pozyczyl;
 public :
  void SetTitle(AnsiString);
 };
//-------------------------------------------------
class cKsiazka : public cPozycja {
 private:
        AnsiString autor;
 public :
        void SetAutor(AnsiString);
        AnsiString RAutor() { return autor;};
 };
//-----------------------------------------------------
class cCzasopismo : public cPozycja {
 private:
        int numer;
 public :
        void SetNumer(int);
        int RNumer() { return numer;};
 };
//------------------------------------------------------

class cObsluga {
 private:
        cUzytkownicy _osoby[50];
        //cPozycja _pozycja[50];  <-- tak mam zrobić 
        cKsiazki _ksiazki[50];  // to podobno nie potrzebne skoro ma byc tablica typu cPozycja
        cCzasopisma _czasopisma[50]; // jak wyzej 
        
        int l_osob; //licznik osob, czyli gdzie w danym momencie jestem w tablicy
        int l_ksiazek; //jak wyzej tyle ze ksiazek
        int l_czasopism; // jak wyzej tyle ze czasopisma
 public :
        cObsluga::cObsluga(int,int,int);
        void LendBook(int);//funkcja ktora wypozycza ksiazke
 };

Według zalecen pana DR. mam skorzystać z zalet polimorfizmu :/ Tylko moje pytanie JAK ? :/

Zielonego pojęcia nie mam jak to moge zrobić :/

Do tej pory jak widać, korzystalem z funkcji LendBook(int) z klasy cObsluga, i dane zapisywałem do tablicy typu _ksiazka[].

Wiem ze nalezy dodać do klasy cPozycja metode virtualną oraz do klas po ktore dziedziczą, no ale skoro one robią jako rekord (Struct w c++) to jak i gdzie mam zapisywać dane ? :/

Totalnie sie juz w tym pogubiłem, i bardzo prosze o pomoc :/
Tylko proszę bez linków do polimorfizmu, bo juz troche czytałem i nadal nie wiem jak wybrnąć z tego problemu :/

0
//cPozycja _pozycja[50];  <-- tak mam zrobić 

Jak już, to tak:

cPozycja* _pozycja[50];

wtedy do tej tablicy możesz przypisywać wskaźniki do obiektów cCzasopismo i cKsiazka. cPozycja powinna mieć też jakąś metodę, która będzie mówić o tym, z jaką klasą pochodną masz do czynienia.

0
0x666 napisał(a)

cPozycja powinna mieć też jakąś metodę, która będzie mówić o tym, z jaką klasą pochodną masz do czynienia.

Tzn. ?

Btw. funkcje LendBook(int) moge wrzucic do klasy np. cKsiazka ?
Ale jak potem zrobić żeby dodać nową książke do tablicy (spisu) ? Mam wywolac metodę z klasy cKsiazka ktora zapisze mi do tablicy rekord typu cKsiazka ?? :/

Kurna no głupawka totalna ;-(

0

Przykład polimorfizmu :

#include <iostream>
using namespace std;
//Polimorfizm - przykład:
class Instrument{
   public :
      virtual void zagraj(){
         cout<<"Gra Instrument"<<endl;;
      }
};

class Gitara : public Instrument{
    public :
       void zagraj(){
          cout<<"Gra gitara"<<endl;
       }
};

class Puzon : public Instrument{
    public :
       void zagraj(){
          cout<<"Gra puzon"<<endl;
       }

};

int main(){
   Instrument * tabOfInstr[10];
   tabOfInstr[0] = new Gitara;
   tabOfInstr[1] = new Puzon;
   tabOfInstr[0]->zagraj(); // zostanie wywolana metoda, ale z klasy pochodnej a nie macierzystej
   tabOfInstr[1]->zagraj(); // zostanie wywolana metoda, ale z klasy pochodnej
   system("pause");
}

Chodzi o to, ze gdy mamy wskażnik/referencje do obiektu klasy maciezystej(Instrument) to możemy pod ten wskaźnik/ref również (bez rzutowania) podpiąć wszystkie obiekty wywiedzione z klasy macierzystej (czyli u nas Gitara, Puson - ma to miejsce w main'ie). I dalszym dobrodziejstwem jest to, że jeśli w klasie Macierzystej jest zdefiniowana jakaś funkcja jako virtualna (u nas zagraj() ), to wywołując ją na referencji/wskaźniku do klasy macierzystej, zostanie sprawdzone, czy przypadkiem wskaznik/ref nie wskazuje na obiekt klasy pochodnej, a jeżeli wskazuje to wywołą tę samą metodę zdefiniowaną w klasie pochodnej. Czyli u Ciebie zrobisz sobie tablice wskaźników/referencji do klasy macierzystej (oPozycja), stworzysz w niej funkcję wirtualną : virtual void wypiszTyp() i dalej analogicznie jak w moim przykladzie. Rozpoznawanie typów (mają wskaznik/ref do klasy macierzystej) może tez odbyć się za pomocą RTTI - ale o tym jak będzie potrzeba :)

0

Tzn. ?

To znaczy, że jak będziesz miał tablicę wskaźników cPozycja, to musisz jakoś zidentyfikować, czy dana pozycja jest książką czy czasopismem. Oczywiście, to ma sens jeżeli będziesz robił coś, co będzie wymagało rzutowania na konkretną klasę.

Btw. funkcje LendBook(int) moge wrzucic do klasy np. cKsiazka ?

Możesz, tylko po co? Co wtedy z czasopismami? Za wypożyczenie powinna odpowiadać obsługa, a nie książka.

0
class cPozycja {
 private:
        bool wypozyczone;
        int data_oddania;
        bool spoznione;
        int kto_pozyczyl;
 public :
  virtual AnsiString GetAutor()=0;
  virtual AnsiString Get()=0;
  };
class cKsiazka : public cPozycja {
 private:
        AnsiString autor;
 public :
        AnsiString Get() { return autor;};
        cKsiazka(AnsiString);
 };
class cCzasopismo : public cPozycja {
 private:
        AnsiString numer;
 public :
        AnsiString Get(){ return numer;};
        cCzasopismo(AnsiString);

 };

Kod programu main.cpp

cPozycja *lista[10];

lista[1] = new cKsiazka("test");
lista[2] = new cCzasopismo("1");

I oto co wyskakuje :

 [C++ Error] main.cpp(28): E2352 Cannot create instance of abstract class 'cKsiazka'
[C++ Error] main.cpp(28): E2353 Class 'cKsiazka' is abstract because of 'cPozycja::GetAutor() = 0'
[C++ Error] main.cpp(29): E2352 Cannot create instance of abstract class 'cCzasopismo'
[C++ Error] main.cpp(29): E2353 Class 'cCzasopismo' is abstract because of 'cPozycja::GetAutor() = 0'

A jak zrobie w klasie cPozycja funkcje virtual bez =0 to znowu wyskakuje komunikat
" Function should return a value"

0

cPozycja powinna zawierać metody wspólne dla obu klas pochodnych, a przecież cCzasopismo nie ma autora.

I oto co wyskakuje :
[...]

Nie możesz tworzyć obiektów klas abstrakcyjnych. Obie klasy muszą implementować/definiować GetAutor i Get.

0

dokładnie ... jeżeli w klasie nadrzędnej masz jakąś metodę abstrakcyjną (...=0;) to w klasach pochodnych należy takie metody zaimplementować, w przeciwnym wypadku klasy pochodne stają się one automatycznie również klasami abstrakcyjnymi (tego nie jestem peiwien, ale na to wygląda) , a jak wiadomo nie można stworzyć instancji klasy abstrakcyjnej.

0

Dobra, jedno juz czaje, ale pozostalo jeszcze jedno.

Mianowicie jak mam klasę cKsiazka a w niej metodę SetAutor(AnsiString Autor){ autor = Autor; }; to jako mogę sie do niej dostać ??

Bo robię tak :

cPozycja *lista[10];

lista[1] = new cKsiazka("jakis tekst");
lista[1]-> i tu wyswietla funkcje z cPozycja

a chciałbym się dostać do funkcj SetAutor ktora znajduje sie w cKlasa, NIE chciałbym natomiast dodawać tej funkcji do cPozycja i robic ze niej f. virtualna, bo jest ona potrzebna tylko w cKsiazka.

0

to musisz w takim razie rzutować ((cKsiazka)lista[1])->SetAutor("Mickiewicz"); noi musisz miec pewność, ze to na pewno jest klasa cKsiążka - najpierw bym to sprawdził, żeby kod był bardziej uniwersalny :)

// dopisane ...
Ogólnie to bym sobie zdefiniował enum'a nP enum cType {KSIAZKA, CZASOPISMO}, jednocześnie dołożył metodę abstrakcyjną : cType getType()=0;
noi w każdej klasie zdefiniował ją jako {return KSIAZKA;} lub {return CZASOPISMO;} noi zanim gdziekolwiek dalej bym potrzebował rzutowania, to bym sprawdził, czy (lista[i]->getType() == KSIAZKA) - w sumie istnieje coś takiego jak RTTI i służy do identyfikacji typów w czasie działania programu i może byłoby ono elegantszym rozwiązaniem, ale jeżeli go nie znasz/nie_chcesz_uzywac to tutaj zawsze będziesz miał kolejną okazje wykorzystania polimorfizmu :):)D:D

0

A nie możesz tak?

lista[0] = new cKsiazka("Tytuł","Autor", /* itd. */);
...
lista[1] = new cCzasopismo("Nazwa",numer, rok, /* itd. */);

[...] a w niej metodę SetAutor(AnsiString Autor)

Obiekty przekazuj przez const referencje:

SetAutor(const AnsiString &Autor){...}

Możesz zrobić tak:

class cPozycja 
{
private:
	bool	wypozyczone;
	int 	data_oddania;
	bool	spoznione;
	int 	kto_pozyczyl;
	bool	jest_ksiazka;
 public:
	bool	JestKsiazka()const { return jest_ksiazka; }
   
    ...
 
    cPozycja(bool ksiazka)
		:jest_ksiazka(ksiazka)
	{
	}
 };
 
 
class cKsiazka : public cPozycja 
{
	...
	cKsiazka(...)
		:cPozycja(true)
	{
	}
}


class cCzasopismo  : public cPozycja 
{
	...
	
	cCzasopismo (...)
		:cPozycja(false)
	{
	}
}

i wtedy:

if(lista[x]->JestKsiazka())
{
   cout<< ((cKsiazka*)lista[x])->Autor()<<endl;
   ...
}
else
{
   cout<< ((cCzasopismo*)lista[x])->Nazwa()<<endl;
   ...
}
0

Ok, dzieki, poradzilem sobie dzieki wam.

Ale teraz znowu jest problem :/

mianowicie, w klasie cPozycja mam metode int GetDataOddania, która pobiera zawartość pola "data_oddania"

I teraz, podczas działania programu, gdy chcę zrobić coś takiego :
int x;
x = _pozycja[i]->GetDataOddania

to wyskakuje "Acces Violatons" :/ co jest nie tak :/

0

A _pozycja[i] wskazuje na coś w ogóle? Przypominam, że to jest tablica wskaźników.

0

hm... no chyba na nic nie wskazuje, bo po uruchomieniu programu nie wprowadzam od razu nowych pozycji.

Zgooglalem troche i widze ze musze to sprwadzac chyba tak : if (_pozycja[i] != NULL) then ....

0

Zrób zmienną, która przechowuje liczbę przypisanych obiektów. W konstruktorze cObsluga ustaw ją na zero.

0

Elegancko, wszystko działa, jeszcze tylko usuwanie z tablicy.
Do tej pory (w starszej wersji programu) robiłem to uzywajac <vector> z STL'a

 vector<cKsiazka> temp;
 for(int i=0;i<=l_ksiazek;i++){
  temp.push_back(_ksiazki[i]);
 }

 temp.erase(temp.begin()+numer);

 for(int i=0;i<temp.size();i++){
  _ksiazki[i] = temp[i];
 }

 l_ksiazek = temp.size()-1;
 temp.clear();

Tyle, że teraz mam dzięki wam dwóm panowie, tablice wskaźników cPozycja *_pozycja[50];
Wiec moje ostatnie juz pytanie :
Jak zastosować ów <vector> do usuwania któregoś elementu z tej tablicy ??

/// ajj.... rozleniwilem sie przez to forum :P

wystarczyl dac : vector <cPozycja*> temp;

0

Do tej pory (w starszej wersji programu) robiłem to uzywajac <vector> z STL'a

[...]

A co stoi na przeszkodzie żeby użyć vectora zamiast normalnych tablic? Szczerze mówiąc, używanie vectora tylko po to, żeby odpowiednio przepisać zawartość tablicy jest... dziwne :|

0

Dziwne czy nie, ale ułatwiło mi realizacje zadania.

Tak czy siak, dziekuje Tobie oraz muciu za pomoc.

0

Żaden problem

  • pozdrawiam

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