=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=