=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= From: lsd@ikp.pl To: obeer@obeer.com Date: Friday, January 05, 2001, 6:07:11 AM Subject: Przypomnienie z POB do kolosa Files: --====----====----====----====----====----====----====----====----====----===-- Ok zacznijmy wyklad. Tamto co ci dalem to jest szajs musialem byc z deka zdumpowany.Wiec zaczynamy. Krotkie przypomnienie [censored] a mianowicie glebokie kopiowanie i plytkie kopiowania , a takze polymorfizm ( tu krotki przyklad bo sprawa jest trywialna) a takze dalej jakie bledy najczesciej popelnia [censored] z laborki to tzw. "memory leak" ( czyli z polskiego krotko naduzycie pamieci a co za tym idzie przepelnienie pamieci ) OK: Kopiowanie plytkie: Kopiowania plytkiego tak naprawde zawsze sie uzywa w klasach jak sie definiuje jakie kolwiek metody tak np. class tst { private: char *string; public: tst(char *m_string) { string = m_string ; // To jest kopiowanie plytkie } }; Inicjalizacja metody to jest rowniez kopiowanie plytkie , czyli konstruktor wyglada tu nastepujaco. tst(char *m_string) : string(m_string) {} Nie ma co tu sie rozpisywac bo to jest banal tylko tak sie skomplikowanie nazywa ot i cala filozofia . Cala filozofia dopiero sie pojawia wtedy kiedy mamy doczynienia z kopiowaniem glebokim tu jest wiele popelnianych bledow i nawet Pajak je popelnia ( no cos nobody is perfect).Kopiowanie glebokie dotyczy przekopiowania wszystkich metod classy i to tak zeby nie zostalo absoutnie nic :)) zadnych smieci. W kolosie tym co dostalismy nie bylo do konca sprecyzowane ale przy kopiowaniu glebokim musi byc ZAWSZE ZDEFINIOWANY DESTRUKTOR!!Class'a sama sie zajmie po przekopiowaniu metod z pola prywatnego destrukcja zbednych smieci , jednak nie jest to takie kolorowe jak wyglada bo metody a wlasciwie ich adressy musisz sam wykasowac.Maly przyklad: #include class tst { private: char *name; char *surname; int wiek; public: tst(int m_wiek,char *m_name,char *m_surname) { wiek = m_wiek; name = m_name; // Kopiowanie plytkie surname = m_surname; //Kopiowanie plytkie } ~tst(); // Operator przypisania skopiuje wszystkie elementy classy i wykasuje kopiowane // elementy zeby nie zasmiecac pamieci i nie doprowadzac do zjawiska "memory leak" tst& operator=(const tst& t) { if(this == &t) return (*this); // Sprawdzamy czy przypadkiem nie kopiujemy tego samego elementu wiek = t.wiek; name = strdup(t.name); //strdup jest duplikacja stringu i sie znajduje w naglowku string surname = strdup(t.surname); delete name; delete surname;// teraz mozemy juz po pomyslnym skopiowaniu skasowac zbedne i nieuzywane smieci w pamieci return (*this); } }; int main() { tst t(12,"Jas","Kowalski"); tst t1(13,"Malgosia","Kowalska"); t = t1; return 0; } Drugim sposobem i troche prostrzym kopiowania glebokiego jest uzycie class string z pliku naglowkowego string (nie pomylic ze string.h) czyli operator przypisania class'a wygladala by tak class Osoba { private: int wiek; string imie; string nazwisko; public: Osoba(int w,string i = string(""),string n = string("")) // zainicjowanie string classy musi tak wygladac :)) { wiek = w; imie = i; nazwisko = n; } ~Osoba() {} Osoba& operator=(const Osoba& o) { if(this == &o) return (*this); wiek = o.wiek; imie = o.imie; nazwisko = o.nazwisko; return (*this); } }; Voila i tak wyglada operator przypisania w drugim sposobie .Dlaczego nie uzylem teraz delete'ow ? Poniewaz class'a string sama sie zatroszczy o zwolnienie pamieci po przekopiowaniu i caly problem z glowy i kazdy jest szczesliwy :) Jest jescze kilka innych sposobow np. poprzez zdefiniowanie dodtatkowych bufforow na czas kopiowania i uzycie funkcji systemowej strcpy albo zdefinowanie drugiej classy ktora bedzie dbala o to zeby nie bylo smieci w pamieci ale to poza konkurencja.(Podejrzewam ze gosc zna tylko jedna i to najprostrza) Chyba kazdy juz wie jak przeciazyc operator ostream ale przypomne. Operator ostream jest definiowany nastepujaco: class tst { private: char *name; public: tst(char *m_name) : name(m_name) {} friend ostream& operator<<(ostream &os,const tst& t); }; ostream operator<<(ostream &os , const tst& t) { os << t.name << endl; return os; } Metoda z przeciazanym operatorem ostream MUSI BYC POZA CIALEM CLASSY !! ZAWSZE ! Teraz o zaprzyjaznieniu dwoch klas. Rzecz jest bardzo prosta powniewaz w jednym miejscu trzeba wpisac magiczne slowo i koniec.Przyklad nizej. Przyklad z kolosa byl taki ze nalezy stworzyc klasse modyfikuj i zaprzyjaznic ja z klassa kobieta i osoba. Mamy klasse a w zasadzie czesc klassy Osoba. class Osoba { private: friend class Modyfikuj; / / <- MAGICZNE SLOWO I PO KOLOPOCIE char *nazwisko; public: tst(char *nazwisko) : nazwisko(m_nazwisko) {} }; Teraz w klasie modyfikuj mamy dostep do kazdego elementu w sekcji prywatnej klasy Osoba czyli Chcemy zmodyfikowac np nazwisko nic prostrzego przyklad nizej. class Modyfikuj { public: Modyfikuj() {} void zmien_nazwisko(Osoba &o,char *nazwisko) { o.nazwisko = nazwisko; // <-- zmodyfikowanie stringu } }; I teraz funkcja main ma postac taka int main() { Osoba o("Kowalski"); Modyfikuj m; m.zmien_nazwisko(o,"Ktostam"); // jezeli jescze przeciazymy operator ostream w klassie Osoba to cout << o << endl; // wyswietli Ktostam proste return 0; } Polymorfizm to akurat jest rzecza bardziej zlozona ale glowne rzeczy rzadzace ta definicja to: 1) Virtualna funkcja 2) Dziedziczenie Przyklad bardzo prosty. class tst { public: virtual void show() { cout << "tst::show()" << endl; } }; class tst1 : public tst { public: cout << "tst1::show()" << endl; }; class tst2 : public tst { public: cout << "tst2::show()" << endl; }; void show_tst(tst& t) { t.show(); } //Jak myslisz co sie stanie jak sie wywola w funkcju main rzecz nastepujaca ? int main() { tst1 t1; tst2 t2; show_tst(t1); show_tst(t2); return 0; } Ano dostaniesz napisy tst1::show i tst2::show a jesli chcesz wiedziec po co jest slowo kluczowe virtual to dla testow wyrzuc je w klasie bazowej i zobaczysz co zamiast powyzszego wyniku dostaniesz. [censored] LsD =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=