Wednesday, October 31, 2007

Dwa pudełka

Trzymam w dłoniach dwa pudełka,
dostałem je od Pana Boga.
On powiedział:
"Wszystkie swoje smutki włóż do czarnego,
zaś wszystkie radości do złotego".

Posłuchałem Jego słów i napełniłem oba pudełka
moimi smutkami i radościami.

Chociaż złote pudełko co dzień stawało
się coraz cięższe, czarne ważyło ciągle tyle samo.

Chciałem dowiedzieć się, dlaczego tak jest,
z ciekawości więc otworzyłem to czarne
i zobaczyłem w dnie pudełka dziurę -
moje smutki wypadły przez nią.

Pokazałem dziurę Panu Bogu i głośno
się zastanawiałem:
"Ciekawe, gdzie się podziały
wszystkie me smutki".

Pan uśmiechnął się do mnie czule.
"Moje dziecko, wszystkie twe smutki
są tutaj, ze Mną".

Boże, dlaczego dałeś mi te pudełka:
Spytałem: "Panie jedno złote,
a drugie czarne i dziurawe?"

"Moje dziecko, złote jest po to,
by policzyć dane ci łaski,
czarne jest po to, byś pozbył się smutków".

Monday, October 29, 2007

Serializacja w C++

Serializowanie to metoda na zapisanie pełnego stanu naszej klasy.

class samochod {
void zapisz (ofstream & s); // Metoda przyjmuje wyjściowy strumień plikowy jako parametr
void odtworz(ifstream & const s); // stały: bo nie zmianiamy jego zawartości
}

void Samochod::zapisz (ofstream & s) {
s << marka << endl;
s << rejestracja << endl;
}

Klasa typu POD:
-> Nie ma konstruktora
-> Nie jest wirtualna
-> Nie ma elementów składowych innych niż stanadrowe (nie może być string, vector itd...)

Jeżeli klasa nie jest typu POD wtedy nie da się zapisać jej za pomocą "adres -> sizeof(class)" ponieważ posiada elementy dynamiczne.

#include

void Samochod::odtworz (ifstream & s) {
getline(s, marka);
getline(s, rejestracja);
}

main() {
ifstream s("plik.txt");
s.open("plik.txt");

s.close
// Tutaj samo się zamknie (robi to destruktor)
}

Sunday, October 14, 2007

ZHR : Za mało czasu na śmierć

W związku ze śmiercią ks. prałata hm. Zdzisława Peszkowskiego naszła mnie pewna refleksja. Nie pojechałem na Turbacz ponieważ przyjechał do mnie na weekend chrzestny, którego nie widziałem naprawdę dawno. W oczekiwaniu na przyjazd mojego chrzestnego do rodzinnego miasta zdążyłem spędzić trochę czasu z qmplami z Kręgu, pozałatwiać kilka spraw związanych ze studiami, wpaść w odwiedziny do Ciechocinka w celu wyjaśnienia co i jak ze śpiewnikiem, nacieszyć się nowo dostanym laptopem i kilka innych mniej lub bardziej czasochłonnych spraw. Pewna refleksja:

Starając się działać aktywnie w HOPRe, pełniąc służbę w Kręgu, użerając się z całą kretyńską biurokracją ZHRu, pisząc projekty dla ZHR, pomagając innym w sprawach informatycznych gdzie powinno się w ramach pełnionej służby ... gdzie kalendarz imprez harcerskich pęka w szwach... gdzie trzeba znaleźć jeszcze czas na bycie mężem, synem, wnukiem, studentem, chrześniakiem, przyjacielem... czy jest jeszcze czas aby zatrzymać się. Czy harcerza na czas ba czyjąś śmierć ? Czy strażnik ma czas na śmierć innego harcerza ? Czy harcerz w ogóle ma czas na coś poza harcerstwem ? Czy idee nadal służą nam czy to my zaczynamy służyć ideom ? Czy nie zatracamy się ... w naszym harcerstwie.... nie mając już czasu na normalne życie ?

................................

Tuesday, October 9, 2007

Podstawy subversion (SVN)

No tak, pewnie (podobnie jak ja) słyszałeś 2,500 razy aby tego używać w swoich projektach ale może wciąż zastanawiasz się po co, i jak. Sp19róbujemy coś z tym zrobić.

Co robi SVN ?
W dużym skrócie SVN przetrzymuje pliki, które każesz mu przetrzymywać jednak w taki sposób, że jest w stanie otworzyć dowolny z tych plików w dowolnej z jego kiedykolwiek istniejących wersji. Jeżeli edytowałeś plik 100 razy i wprowadziłeś w nim 100 zmian. To SVN pozwoli Ci wrócić do dowolnej ze zmian (przywrócić plik w postaci z przed zmiany 39 jak i z postaci jaką przybrał po zmianie 76).

Po co SVN ?
Podam kilka tylko przypadków w których SVN okazuje się użyteczny:
  1. Masz system operacyjny, w którym jest kilku administratorów. Trzymanie w SVN plików konfiguracyjnych systemu pozwala na natychmiastowe wrócenie do stanu poprzedniego (działania) w momencie w którym po jakiejś zmianie coś przestaje działać - bez zastanawiania się i szukania pomyłki.
  2. Kilku programistów pracuje nad tym samym projektem i istnieje groźba, że będą pracowali nad tym samym fragmentem kodu w jednym czasie.
  3. Szlak trafił nam projekt, ktoś narozrabiał i wywalił pliki, które właśnie są Ci potrzebne. Przywracasz je jednym poleceniem.
  4. Nawet jeżeli coś było tylko epizodem w twoim życiu i dawno usunąłeś te dwa skrypty rubego ze swojego dysku, a używałeś w trakcie svn-a to możesz (nawet po 2 latach) do nich wrócić....
Skrócony opis obsługi SVN:
Oprę się na świetnym tutorialu dostępnym pod adresem: http://artis.imag.fr/~Xavier.Decoret/resources/svn/index.html
Przedstawię tylko i wyłącznie podstawy jednak w pewnym konkretnym ujęciu, które może okazać się dobrym uzupełnieniem powyższego artykułu:
  1. Tworzymy miejsce, w którym będziemy przetrzymywać nasz projekt:
    mkdir /mnt/saveplace/dziubdziub/svn
    svnadmin create --fs-type fsfs /mnt/saveplace/dziubdziub/svn
    Tak przygotowany katalog będzie bazą dla polecenie svn, którym zaczniemy się posługiwać.
  2. Importujemy nasz projekt do svn (robimy to tylko raz ! na początku projektu):
    svn import /home/dziubdziub/poszukiwania file:///
    mnt/saveplace/dziubdziub/svn/poszukiwania -m "Initial import"
    Co właśnie zrobiliśmy ? Otóż kazaliśmy svn-owi (w jemu tylko znany sposób) zaimportować cały projekt z plikami i opisać tą zmianę "Initial import"
  3. Po imporcie musimy wykonać checkout (tylko raz tylko na początku zaraz po zaimportowaniu), który pobierze nasze pliki z svn-a. Możemy wykonać go do innego katalogu. Możemy wykasować istniejący projekt i w jego miejsce pobrać spowrotem te same pliki z svn-a:
    cd
    /home/dziubdziub
    rm -fr poszukiwania
    svn checkout file:///mnt/saveplace/dziubdziub/svn/poszukiwania
  4. Kiedy wykonaliśmy checkout możemy zacząć używać svn-a. Od tej pory w katalogach z projektem istnieją katalogi .svn, które przetrzymują informacje pozwalające nam nie podawać ścieżki file://... ale działać bez niej (dzięki tym katalog svn sam wie gdzie są trzymane repozytoria) Powiedzmy, że nad projektem na tym repozytorium pracuje kilku programistów. Aby mieć najświeższą wersję ZAWSZE przed rozpoczęciem pracy wykonujemy:
    cd /home/dziubdziub/poszukiwania
    svn update
    Dzięki czemu pobierzemy najnowszą wersje plików.
  5. Po wprowadzonej przez siebie zmianach schodzimy do najniższego katalogu i patrzymy co się zmieniło:
    svn status
    ?
    - plik został dodany lub usunięty i nie wiadomo co z nim zrobić
    ! - plik został usunięty
    M - plik został zmodyfikowany
  6. Podejmujemy jedną z kilku akcji względem plików oznaczonych ?:
    svn add sciezka/do/pliku - dodajemy (?)
    svn rm sciezka/do/pliku - usuwamy (!)
  7. Po podjęciu akcji (nie ma żadnych plików oznaczonych ?, ! ale same oznaczone A(dd), D(elete), M(odify) każemy wprowadzić zmiany do repozytorium SVN:
    svn commit -m "Description of changes"
  8. Za każdym razem po wprowadzeniu zmian musimy ponownie (jakby od początku) wykonać svn update ponieważ nasze zmiany zostały nałożone na zmiany innego programisty w svn i trzba pobrać skompilowaną wersję tych wszystkich wspólnych zmian (które łączy svn):
    svn update
Luźne rady:
  1. Komituj każdą najdrobniejszą zmianę. Im więcej i częściej będziesz komitować tym szczegółowiej będziesz mógł określić, która ze zmian Cię intereuje w kodzie (i chcesz ją zatrzymać) a która nie.
  2. Nie warto dodawać do repozytorium plików czy katalogów tymczasowych czy np... plików pochodzących z mechanizmu cachowanie czy logów. Dlatego warto oznaczyć niektóre katalogi aby były ignorowane (zmiany w nich zachodzące dokładnie) przez svn. Powiedzmy, że mamy katalog tmp w którym jest katalog cache i logs, których zawartości nie chcemy brać pod uwagę podczas naszego commitowania. Każdy z katalogów ma swoją listę ignorowanych plików. Robimy to mniej więcej tak:
    cd /home/dziubdziub/poszukiwania/tmp
    svn propedit svn:ignore logs
    Wpisujemy:
    *
    Od teraz w katalogu logs ustawiony jest parametr svn:ignore na * (wszystkie pliki). Analogicznie postępujemy z cache:
    cd /home/dziubdziub/poszukiwania/tmp
    svn propedit svn:ignore cache
    Wpisujemy:
    *
  3. Aby przywrócić jakiś plik wpisz:
    svn revert sciezka/do/pliku
  4. Program svn pozwala nam działać na wirtualnym systemie plików znajdującym się w: /mnt/saveplace/dziubdziub/svn na zasadzie zwykłych poleceń systemowych poprzedzonych svn:
    svn ls file:///mnt/saveplace/dziubdziub/svn/poszukiwania
    svn rm file://...
    svn mkdir file://...
    svn tree file://...
    Pomimo tego, że polecenia:
    ls file:///mnt/saveplace/dziubdziub/svn/poszukiwania
    rm file://...
    mkdir file://...
    t
    ree file://...
    A w katalogu /mnt/saveplace/dziubdziub/svn wcale nie znajdzmyjawnie pokazanych naszych folderów, ale tylko i wyłącznie wewnętrzną strukturę organizacji danych stworzoną przez svnadmin.

Monday, October 1, 2007

OJP 1 zajęcia :)

Dziś na zajęciach z OJP usłyszałem pierwszy raz jak wygląda obiektowe programowanie w ujęciu "ewolucji". Powiedzmy, że robimy bazę danych samochodów:



int : rocznik

int : przebieg

string : rejestracja

string : kolor



Operujemy na 4 tablicach - funkcja, która miałaby operować na samochodzie musiałby przyjmować 4 parametry (porażka)... lub działać na zmiennych (tablicach) globalnych. Sortowanie takich samochodów np. według rocznika czy przebiegu sprawia, że drobny błąd w algorytmie (operującym na 4 tablicach) rozsynchronizuje bazę.



Kolejnym pomysłem jest zamknąć te dane w strukturze samochód:



struct samochod {

int : rocznik
int : przebieg
string : rejestracja
string : kolor

}



Dzięki temu funkcja (np. funkcja info() wyświetlająca informację o samochodzie) może przyjmować już tylko jeden parametr (samochód)... Jednak teraz programista może celowo (lub przypadkowo) zmniejszyć przebieg lub zmienić rocznik - czego byśmy nie chcieli. Nasz obiekt nie jest bezpieczny, nie jest niezmienniczy. Nie możemy zagwarantować, że przebieg się nie zmieniszy, że rocznik się nie zmieni.



Idąc dalej możemy zamknąć wszystko w klasie a dane umieścić w sekcji private.



class samochod {



// Interfejs: co sie składa na samochód

private:
int : rocznik

int : przebieg

string : rejestracja

string : kolor

string : numer_silnika

string : marka



public:

// Implementacja: jak się zachowuje samochód

zmien_przebieg() {

++przebieg;

}
}



Dzięki temu programista nie może zmniejszyć przebiegu:



samochod * mazda_jasia = new samochod();

mazda_jasia->przebieg = 200 km;



Dzięki temu jesteśmy pewnie, że przebieg nigdy się nie zwiększy. Minusem tego jest jednak, że nie możemy odczytać przebiegu:



cout <<>przebieg;



No i mamy problem. Musimy więc dopisać nową metodę.



int function Samochod::pobierz_przebieg() {

return this.przebieg;

}



Dzięki temu mamy pełna kontrolę nad przebiegiem. Jesteśmy pewni, że nie zmniejszy się jednak możemy sprawdzić jego wartość i monotonicznie go zwiększać. Jednak jaki rocznik będzie miał samochód, który jest nowo utworzony ? No właśnie. Często zero, jednak nie można na tym polegać... Musimy więc zainicjować wartości na nowo - robimy to w konstruktorze.



Samochod::Samochod() {

this.rocznik = 1970;

this.przebieg = 0;

}



Jednak taki samochód nie ma ani marki ani koloru. Możemy więc zrobić inny konstruktor.



Samochod::Samochod(string _rejestracja, string _silnik, unsigned int _rocznik ...) {

rejestracja = _rejestracja;

silnik = _silnik;

rocznik = _rocznik;

}



Jednak kiepskim pomysłem jest wypisywanie w liście wszystkich parametrów. W powyższym przypadku tworzą się kopie obiektów, które są przekazywane przez do metody. Możemy jednak nie przekazywać parametrów i nie przypisywać ich do skałdowych klasy w ciele konstruktora. Do tego słuzy lista inicjalizacyjna:



Samochod::Samochod(string _rejestracja, string _silnik ...):rejestracja(_rejestracja), silnik(_silnik), kolor("czerwony") {

[...] // tutaj ciało metody

}



Mamy teraz przeciążony konstruktor (istnieją i moga istnieć różne funkcje o tych samych nazwach a o różnych parametrach). Możemy np. zrobić dodatkową metodę zwiększ przebieg:



Samochod::zwieksz_przebieg(int ile) {

if (ile > 0) {

przebieg += ile;

}

}



W C++ mamy możliwość dawać parametrow wartości domyslne, co pozwala uiżytkownikom daje mozliwośc na opuszczanie parametrów. Na z 2 metod zwieksz przebieg możemy zrobić 1:



class {

[...]



zwieksz_przebieg(int ile = 1);

-







-

Samochod::zwieksz_przebieg(int ile) {

}



mazda_jasia->zwieksz_przebieg(); // += 1

mazda_jasia->zwieksz_przebieg(12); // += 12



Wartości domyślne (zmienne z wartościami domyślnymi) dajemy na końcu.



Dalej mówiac o konstruktorze. Jeżeli tworzymy knstruktor dla klasy to musimy zadbać, żeby wszystko było w nim zainicjowane.



Dziedziczenie:

------------------------------------------------



Stosujemy kiedy chcemy stworzyć specjalizację klasy, lub dodać jakieś cechy lub zachowania, których nie miała klasa oryginalna.



-------------

samochod < [ samochod_sportowy ] (bool spoiler, unsigned int data_tuningu)

-------------

^

i a kind of

^

--------------

ciezarowka

--------------



Kiedy zaczniemy dodawac do klas pochodnych coraz wiecej metod czy skladowych pokaze, ze nie warto tworzyc jednej wielkiej klasy samochod z ogromna ilosccia skladowych, poniewaz niektore skladowe (spoilery) dotycza tylko i wylacznie samochodwo sportowych, a w samochodach musialby by zaincjalizowane na jakies dizwne bezsensowen wartosci. Jak wyglada dziedziczenie:



class Ciezarowka public Samochod {



private:

// ciezarowka stala sie samochodem. Dodajemy tylko to co chcemy dodać:

int ladownosc; // Tym się różni od samochodu.

}



Jakie można popełnić błędy ? 1) Otóż mazda nie DZIEDZICZY po samochodzie. Jest samochodem ! a nie klasą... Powinna być obiektem klasy samochód ! a nie samochodem !



2) Kompozycja (is a part of) -> jest częścią. Na przykład silnik jest częścią samochodu, ale nie dziedziczy po samochodzie ! Jest natomiast częścią samochodu ! (diamencik).



3)



elipsa <- punkt : połżenie_środka, float : szerokość, float : wysokość, narysuj()...



Koło nie może dziedziczyć po elpisie ! Koło => (szerokość = wysokość). Wszystko jest ok do momentu, kiedy my nie skomplikujemy elipsy.... np po dodaniu Elipsa::Rozciagnij() { szerokosc *= 2) okazuje się, że koło nie jest dobryn przykładem na dziedziczenie z elipsy... bo koła nie wolno dodawać.



Zasada substytucji Miskowa.



-----------------

Zasłanianie !

-----------------



Samochód: function f(int par), f(string par), f(float par);



class SamochodSportowy public Samochod {



funtion f(string par) {

// Ta metoda zaslania WSZYSTKIE funkcje f z Samochod

}



}



Przeciązanie: w jednej klasie te same nazwy różne parametry

Zasłanianie: w klasie pochodnej metoda o tej samej nazwie.



Dwa sposoby na wybrnięcie z problemu:



1) using Samochod::f; <- pierwszy sposób

2) Wywołać Samochod::f() <- w metodzie klasy.



Projekty ! (wymyślić obiekt do hierarchi dziedziczenia)