TDecompressionStream.Destroy - co o tym sądzicie?
Wyświetlono wersję archiwalną wątku "TDecompressionStream.Destroy - co o tym sądzicie?" z forum pl.comp.lang.delphi
Bartek Dajewski - 19 Cze 2000, 03:00
Cześć,
Ze zbioru ZLIB.PAS (D3):
destructor TCompressionStream.Destroy; begin {...} try {...} finally {...} end; inherited Destroy; end;
Czy to jest prawidłowa sekwencja? Przecież jeżeli wystąpi wyjątek w sekcji try, to nie zostanie wywołany inherited Destroy... Poprawcie mnie jeśli jestem w błędzie. --- Pozdrawiam :-) Bartek
Sławomir Adamski - 19 Cze 2000, 03:00
Witam
destructor TCompressionStream.Destroy; begin {...} try {...} finally {...} end; inherited Destroy; end;
Czy to jest prawidłowa sekwencja? Przecież jeżeli wystąpi wyjątek w sekcji try, to nie zostanie wywołany inherited Destroy... Poprawcie mnie jeśli jestem w błędzie.
Jesteś w błędzie i chyba nieco zmęczony.
Bartek Dajewski - 19 Cze 2000, 03:00
Cześć,
Witam [...] | try | {...} | finally | {...} | end; | inherited Destroy; | end; | Czy to jest prawidłowa sekwencja? Przecież jeżeli wystąpi wyjątek w | sekcji try, to nie zostanie wywołany inherited Destroy... Poprawcie mnie | jeśli jestem w błędzie. Jesteś w błędzie i chyba nieco zmęczony.
:-) Nic nie rozumiem, prawda ? Czuję się świetnie i nie widzę objawów zmęczenia w powyższym pytaniu
zauważyłem to już po wysłaniu i wcześniej nie prostowałem). Wracając do tematu: w razie wystąpienia wyjątku między try a finally program nie dojdzie do instrukcji inherited Destroy, a to niedobrze. Moim zdaniem wywołanie destruktora rodzica powinno być zawsze w części finally, jeżeli istnieje choćby minimalne prawdopodobieństwo wystąpienia wyjątku. Pytam o ogólną zasadę, a nie o konkretny komponent. --- Pozdrawiam :-) Bartek
Sławomir Adamski - 19 Cze 2000, 03:00
Witam
| Witam [...] | try | {...} | finally | {...} | end; | inherited Destroy; | end; | Czy to jest prawidłowa sekwencja? Przecież jeżeli wystąpi wyjątek w | sekcji try, to nie zostanie wywołany inherited Destroy... Poprawcie mnie | jeśli jestem w błędzie. | Jesteś w błędzie i chyba nieco zmęczony.
Nic nie rozumiem, prawda ? Czuję się świetnie i nie widzę objawów zmęczenia w powyższym pytaniu
zauważyłem to już po wysłaniu i wcześniej nie prostowałem). Wracając do tematu: w razie wystąpienia wyjątku między try a finally program nie dojdzie do instrukcji inherited Destroy, a to niedobrze. Moim zdaniem wywołanie destruktora rodzica powinno być zawsze w części finally, jeżeli istnieje choćby minimalne prawdopodobieństwo wystąpienia wyjątku. Pytam o ogólną zasadę, a nie o konkretny komponent.
A co, Twoim zdaniem, przy wystąpieniu wyjątku zostanie wykonane po finally?
Lodek - 19 Cze 2000, 03:00
[...] | try | {...} | finally | {...} | end; | inherited Destroy; | end; Wracając do tematu: w razie wystąpienia wyjątku między try a finally program nie dojdzie do instrukcji inherited Destroy, a to niedobrze.
Panie Bartku, jest Pan jednak zmęczony i to chyba solidnie. Jeśli wystąpi wyjątek pomiędzy try a finally to wykonywanie wejdzie sobie w część finally - end, a potem pójdzie dalej aż do końcowego end. I tyle. Czyli inherited Destroy się wykona. Proszę to sobie sprawdzić na jakimkolwiek innym wyjątku, przykładowo:
try i:=StrToInt(edit1.Text); finally caption:='Ha, jestem tutaj!'; end; caption:=caption+' I tutaj też! Krucafuks, niemożliwe, a jednak stało się!';
Była tu jednak kiedyś dyskusja o tym, co będzie jeśli wyjątek wyskoczy w podsekcji finally - AFAIR stanęło na tym, że będzie ogólna lipa i finally może nie wykonać się do końca.
Krzysztof Szyszka - 20 Cze 2000, 03:00
| [...] | | try | | {...} | | finally | | {...} | | end; | | inherited Destroy; | | end; | Wracając do tematu: w razie wystąpienia wyjątku między try a finally | program nie dojdzie do instrukcji inherited Destroy, a to niedobrze. Panie Bartku, jest Pan jednak zmęczony i to chyba solidnie. Jeśli wystąpi wyjątek pomiędzy try a finally to wykonywanie wejdzie sobie w część finally - end, a potem pójdzie dalej aż do końcowego end. I tyle. Czyli inherited Destroy się wykona. Proszę to sobie sprawdzić na jakimkolwiek innym wyjątku, przykładowo:
try i:=StrToInt(edit1.Text); finally caption:='Ha, jestem tutaj!'; end; caption:=caption+' I tutaj też! Krucafuks, niemożliwe, a jednak stało się!';
Niestety Panie Lodku Krucafuks nie działa :-) i nie powinno działać. Oboje ze Sławkiem robicie Bartkowi wodę z mózgu.
[Do Bartka] Tak, masz rację, twoje rozumowanie jest poprawne, inherited Destroy nie wykona się, bo nastąpi propagacja wyjątku na wyższy poziom.
BTW. Jak na destruktor, to trochę dziwny kod. Tam raczej nie powinno być takich kwiatków :-)
Bartek Dajewski - 20 Cze 2000, 03:00
Cześć,
dyskusyjnych [...]
Niestety Panie Lodku Krucafuks nie działa :-) i nie powinno działać. Oboje ze Sławkiem robicie Bartkowi wodę z mózgu. [Do Bartka] Tak, masz rację, twoje rozumowanie jest poprawne, inherited Destroy nie wykona się, bo nastąpi propagacja wyjątku na wyższy poziom.
Dziękuję. Bardzo dziękuję, bo już zaczynałem przypuszczać, że źle ze mną skoro dla moich adwersarzy błąd w rozumowaniu był tak oczywisty, a ja go nie potrafiłem dostrzec :-) Co prawda pisząc o nich "oboje" możesz się komuś z
BTW. Jak na destruktor, to trochę dziwny kod. Tam raczej nie powinno być takich kwiatków :-)
To naprawdę jest oryginalny kod Borland'a. W dodatku (według moich obserwacji D3) po wyjątku w destruktorze pamięć po obiekcie nie zostanie zwolniona niezależnie od tego, czy będzie tam konstrukcja "try finally end" czy nie. I jak w takim razie pisać klasy, które np. coś sobie buforują, a więc w destruktorze powinny bufory opróżnić, a to może się nie udać ?...
--- Pozdrawiam :-) Bartek
Krzysztof Szyszka - 20 Cze 2000, 03:00
Dziękuję. Bardzo dziękuję, bo już zaczynałem przypuszczać, że źle ze mną skoro dla moich adwersarzy błąd w rozumowaniu był tak oczywisty, a ja go nie potrafiłem dostrzec :-) Co prawda pisząc o nich "oboje" możesz się komuś z
Masz rację, mój błąd. Następnym razem napiszę: flety ;-
| BTW. Jak na destruktor, to trochę dziwny kod. Tam raczej nie powinno | być takich kwiatków :-) To naprawdę jest oryginalny kod Borland'a. W dodatku (według moich obserwacji D3) po wyjątku w destruktorze pamięć po obiekcie nie zostanie zwolniona niezależnie od tego, czy będzie tam konstrukcja "try finally end" czy nie. I jak w takim razie pisać klasy, które np. coś sobie buforują, a więc w destruktorze powinny bufory opróżnić, a to może się nie udać ?...
Poza tym, że dobrze :-), to nie umiem dać Ci ogólnej odpowiedzi na to pytanie.
Lodek - 20 Cze 2000, 03:00
| try | i:=StrToInt(edit1.Text); | finally | caption:='Ha, jestem tutaj!'; | end; | caption:=caption+' I tutaj też! Krucafuks, niemożliwe, a jednak stało | się!'; Niestety Panie Lodku Krucafuks nie działa :-) ...
Poprawiłem to na coś takiego i teraz jest OK, takie troszkę niezbyt ładne, ale działa:
procedure TForm1.Button1Click(Sender: TObject); var i: integer; begin try try i:=StrToInt(edit1.Text); finally caption:='Ha, jestem tutaj!'; end;//tryf finally caption:=caption+' I tutaj też! Krucafuks, niemożliwe, a jednak stało się!'; end;//tryf end;//proc
... i nie powinno działać.
A czy Pan, Panie Krzysiu, mógłby jakoś wyjaśnić, dlaczego nie dizła to poprzednie i dlaczego tak właśnie powinno być? Jak dla mnie tak nie powinno być.
Oboje ze Sławkiem robicie Bartkowi wodę z mózgu.
Przepraszam za wprowadzenie w błąd, byłem tak mocno przekonany, że to co podałem zadziała, że to posłałem. Cholera, konfuzja zupełna. Pardon.
[Do Bartka] Tak, masz rację, twoje rozumowanie jest poprawne, inherited Destroy nie wykona się, bo nastąpi propagacja wyjątku na wyższy poziom.
Kurczę, jak dla mnie to od propagowania wyjątku na wyższy poziom jest instrukcja raise w exception handlerze. Czy może Pan to wszystko jakoś dokładnie wyjaśnić.
BTW. Jak na destruktor, to trochę dziwny kod. Tam raczej nie powinno być takich kwiatków :-)
Panie Bartku, to może niech Pan spróbuje użyć takiego podwójnego try finally (jednego w drugim) i tak, żeby kluczowy błąd ze względu na niszczenie obiektu znajdował się w wewnętrznym try finally. Ale ja teraz po tych rewelacjach to się zakręciłem jak słoik zupełnie.
Sławomir Adamski - 20 Cze 2000, 03:00
dyskusyjnych grup | [...] | | try | | {...} | | finally | | {...} | | end; | | inherited Destroy; | | end; | Wracając do tematu: w razie wystąpienia wyjątku między try a finally | program nie dojdzie do instrukcji inherited Destroy, a to niedobrze. | Panie Bartku, jest Pan jednak zmęczony i to chyba solidnie. Jeśli wystąpi | wyjątek pomiędzy try a finally to wykonywanie wejdzie sobie w część finally | - end, a potem pójdzie dalej aż do końcowego end. I tyle. Czyli inherited | Destroy się wykona. Proszę to sobie sprawdzić na jakimkolwiek innym | wyjątku, przykładowo:
| try | i:=StrToInt(edit1.Text); | finally | caption:='Ha, jestem tutaj!'; | end; | caption:=caption+' I tutaj też! Krucafuks, niemożliwe, a jednak stało | się!';
Niestety Panie Lodku Krucafuks nie działa :-) i nie powinno działać. Oboje ze Sławkiem robicie Bartkowi wodę z mózgu.
Panie Krzysztofie. Wysoce oceniam Pański poziom wypowiedzi i jestem niepomiernie zdziwiony. Widać znów nadzszedł czas, aby Pan mnie czegoś nauczył. Tym razem o wyjątkach. Zróbmy tak: Nowy projekt, na formę przycisk. Teraz nowa forma (tak, druga) i od razu usuwamy ją z listy auto_create. Wracamy do pierwszej formy, w implementacji dopiszemy do uses unit drugiej formy, a do OnClick przycisku wstawmy:
<DELPHI CODE try Form2.Caption := 'coś tam'; finally MessageDlg('mamy finally', mtInformation, [mbOK], 0); end; MessageDlg('i po finally', mtInformation, [mbOK], 0); <?DELPHI CODE Ja widzę oba komunikaty, a Pan?
Krzysztof Szyszka - 20 Cze 2000, 03:00
| Niestety Panie Lodku Krucafuks nie działa :-) i nie powinno działać. | Oboje ze Sławkiem robicie Bartkowi wodę z mózgu. Panie Krzysztofie. Wysoce oceniam Pański poziom wypowiedzi i jestem niepomiernie zdziwiony. Widać znów nadzszedł czas, aby Pan mnie czegoś nauczył. Tym razem o wyjątkach. Zróbmy tak: Nowy projekt, na formę przycisk. Teraz nowa forma (tak, druga) i od razu usuwamy ją z listy auto_create. Wracamy do pierwszej formy, w implementacji dopiszemy do uses unit drugiej formy, a do OnClick przycisku wstawmy:
<DELPHI CODE try Form2.Caption := 'coś tam'; finally MessageDlg('mamy finally', mtInformation, [mbOK], 0); end; MessageDlg('i po finally', mtInformation, [mbOK], 0); <?DELPHI CODE Ja widzę oba komunikaty, a Pan?
Komunikaty, to ja też widzę dwa, za to wyjątku ani jednego ;-) (proszę sobie sprawdzić, że mimo iż Form2 jest Nil, to podstawienie pod Caption nie generuje wyjątku !!!).
PS. Te flety, ...hmm... mógł się Pan wykazać większą inwencją. Chyba jest Pan zmęczony i poziom myślenia coś się Panu obniżył, że nie wspomnę do jakiego punktu.
Umówmy się, że nie czytałem tego zdania. OK ?
P.S. Przyznałem się Bartkowi do błędu ortograficznego i to wszystko. Reszta to gra słów, która z Panami nie ma nic wspólnego.
Krzysztof Szyszka - 20 Cze 2000, 03:00
A czy Pan, Panie Krzysiu, mógłby jakoś wyjaśnić, dlaczego nie dizła to poprzednie i dlaczego tak właśnie powinno być? Jak dla mnie tak nie powinno być. | Oboje ze Sławkiem robicie Bartkowi wodę z mózgu.
Przepraszam za wprowadzenie w błąd, byłem tak mocno przekonany, że to co podałem zadziała, że to posłałem. Cholera, konfuzja zupełna. Pardon.
| [Do Bartka] | Tak, masz rację, twoje rozumowanie jest poprawne, inherited Destroy | nie wykona się, bo nastąpi propagacja wyjątku na wyższy poziom.
Kurczę, jak dla mnie to od propagowania wyjątku na wyższy poziom jest instrukcja raise w exception handlerze. Czy może Pan to wszystko jakoś dokładnie wyjaśnić.
W pańskim rozumowaniu wszystko jest OK :-), tylko że ono nie dotyczy konstrukcji try ... finally end, ale try ... except end.
Konstrukcja try ... finally end NIE GASI zgłoszonego wyjątku, a jedynie pozwala na wykonanie dodatkowych poleceń zawartych między finally i end zanim nastąpi jego propagacja na wyższy poziom. Tu nie ma obsługi wyjątku i dlatego po end nastąpi zerwanie wykonywanie dalszej części kodu (lub wejście do kolejnej sekcji finally, jak w Pan drugim przykładzie).
Panie Bartku, to może niech Pan spróbuje użyć takiego podwójnego try finally (jednego w drugim) i tak, żeby kluczowy błąd ze względu na niszczenie obiektu znajdował się w wewnętrznym try finally. Ale ja teraz po tych rewelacjach to się zakręciłem jak słoik zupełnie.
O tym, że tam jest błąd, to Bartek wie, a to jak go poprawić może zależeć o reszty kodu. Mam nadzieję, że Pana trochę odkręciłem :-)
Krzysztof Swiatkowski - 20 Cze 2000, 03:00
A czy Pan, Panie Krzysiu, mógłby jakoś wyjaśnić, dlaczego nie dizła to poprzednie i dlaczego tak właśnie powinno być? Jak dla mnie tak nie powinno być.
Co prawda nie ja byłem wywołany ale...
try finally jest konstrukcją pozwalającą wykonać jakieś operacje które muszą się wykonać pomimo wyjątku. Do takich operacji należy np zwolnienie pamięci. Nie powoduje to jednak wyłapania wyjatku.
0.begin 1. X := TObject.Create; try 2. StrToInt('Wyjatek'); finally 3. X.Free; 4. ShowMessage('No to czyscimy'); end; 5. ShowMessage('Poza tryf'); 6.end;
w przypadku wystąpienia wyjątku (jak powyżej) sekwencja wykonania jest taka: 0,1,2,[3,4],6 gdyby wpisać poprawną wartość to 0,1,2,[3,4],5,6
jak łatwo zauwazyć zestaw [3,4] wykona się zawsze niezależnie od tego czy wystąpi wyjątek czy nie.
I do tego właśnie słuzy try finally
Kurczę, jak dla mnie to od propagowania wyjątku na wyższy poziom jest instrukcja raise w exception handlerze. Czy może Pan to wszystko jakoś dokładnie wyjaśnić.
cytat z helpa If an exception is raised but not handled in the finally clause, that exception is propagated out of the try...finally statement, and any exception already raised in the try clause is lost. The finally clause should therefore handle all locally raised exceptions, so as not to disturb propagation of other exceptions. ** raise służy do propagowania wyjątku na zewnątrz klauzuli try except. ale tam jest inny sposób obsługi klauzula try except wyłapuje wyjątki a try finally nie. poza tym mozna też danego wyjątku w try except nie wyłapać.
p := nil;
try StrToInt('Wyjątek'); p^ := 'a'; except on E:EConvertError do begin
end end;
podsumowując kod w klauzuli except wywoła się tylko w przypadku wystapienia wyjatku a kod w klauzuli finally wywoła się zawsze! albo prawie zawsze o ile wewnątrz też wystąpi jakiś wyjątek.
I tak dodatkowo. W destruktorze nie powinna sie pojawić instrukcja która powodowała by wyjątek. W destruktorze się zasadniczo nic nie powinno robić. Od D4 w wzwyż można do robienia czegoś wykorzystać funkcję BeforeDestruction. jak w niej się coś wywali to destruktor sie po prostu nie wywoła.
Hopbit
Bartek Dajewski - 20 Cze 2000, 03:00
Cześć,
[...]
<DELPHI CODE try Form2.Caption := 'coś tam'; finally MessageDlg('mamy finally', mtInformation, [mbOK], 0); end; MessageDlg('i po finally', mtInformation, [mbOK], 0); </DELPHI CODE Ja widzę oba komunikaty, a Pan?
Widzę oba, ale to jest zupełnie inny przypadek. Ja pisałem o wyjątku miedzy try a finally. Jeżeli po podstawieniu do Caption wpisać np. abort, będzie widać tylko jeden komunikat. --- Pozdrawiam :-) Bartek
Bartek Dajewski - 20 Cze 2000, 03:00
Cześć,
[...]
| try | i:=StrToInt(edit1.Text); | finally | caption:='Ha, jestem tutaj!'; | end; | caption:=caption+' I tutaj też! Krucafuks, niemożliwe, a jednak stało | się!'; | Niestety Panie Lodku Krucafuks nie działa :-) ... | ... i nie powinno działać.
A czy Pan, Panie Krzysiu, mógłby jakoś wyjaśnić, dlaczego nie dizła to poprzednie i dlaczego tak właśnie powinno być? Jak dla mnie tak nie powinno być.
Z HELP-a: "If an exception is raised in the try statement list, control is transferred to the finally statement list, and once the finally statement list completes execution, the exception is re-raised" - czyli, że "gwarantowane" są tylko instrukcje między finally a end (finally statement list), a następne już nie. [...]
Panie Bartku, to może niech Pan spróbuje użyć takiego podwójnego try finally (jednego w drugim) i tak, żeby kluczowy błąd ze względu na niszczenie obiektu znajdował się w wewnętrznym try finally. Ale ja teraz po tych rewelacjach to się zakręciłem jak słoik zupełnie.
Nie chodziło mi o to jak sobie poradzić z tym konkretnym komponentem, tylko raczej o ogólną zasadę poprawnego pisania destruktorów. A teraz - po odkryciu, że po wyjątku w destruktorze nie jest (!) zwalniana pamięć po komponencie to już sam nie wiem o co pytać i tylko liczę na to, że moja metoda sprawdzania jest błędna, a jeżeli nie, to, że w kolejnych wersjach Delphi błąd ten został usunięty. Może to ktoś sprawdzić ?... Ja sprawdzałem tak, że tworzę i usuwam wiele razy ten sam komponent i sprawdzam jego adres. Ten adres się nie zmienia w przypadku bezbłędnego wykonania metody Free, natomiast jeżeli któreś jej wywołanie zostanie zakłócone wyjątkiem - następne przydzielane adresy już są inne i tak aż do następnego wyjątku. Nie umiem sobie tego inaczej wytłumaczyć niż tak, że ta pamięć ciągle jest "zajęta". --- Pozdrawiam :-) Bartek
Robert Keller - 20 Cze 2000, 03:00
Cześć, Ze zbioru ZLIB.PAS (D3):
destructor TCompressionStream.Destroy; begin {...} try {...} finally {...} end; inherited Destroy; end;
Czy to jest prawidłowa sekwencja? Przecież jeżeli wystąpi wyjątek w sekcji try, to nie zostanie wywołany inherited Destroy... Poprawcie mnie jeśli jestem w błędzie. --- Pozdrawiam :-) Bartek
Z tego co wiem prawidłowo powinno być tak :
( Przykład może deczko inny ale zasada ta sama)
function MyFunction: Boolean;
var MyObject: TMyObject;
begin
result := True;
MyObject := TMyObject.Create;
try // Dla sekcji finally try // Dla sekcji except
{ coś tam, coś tam, jakieś mętne obliczenia ... gdy wystąpi wyjątek program przejdzie kolejno do bloku except a potem do finnaly }
except on E.Exception do begin result := False; ShowMessage('O kurka mamy : ' + E.Message); end; end;
finnaly MyObject.Free; // I to zadziała zawsze czy był, czy nie był wyjątek. end;
// A w tej część zachodzą tzw. REGUŁY HAZARDU.
end;
Jeśli jest tu coś nie tak to piszcie i palcie mnie na stosie za herezję.
PS. Dajcie jakiś przykład na zkompresowanie przy użyciu Zlib pliku "C:\Test\readme.txt".Nie mam CD i nie mam pliku zlib.pas
Lodek - 20 Cze 2000, 03:00
Ok, bardzo Panom dziękuję za "odkręcenie" mnie w kewstii wyjątków i except \ finally. Panie Bartku, to myślę, że nie trzeba załamywać rąk, tylko tak
jakiś handler na wypadek, gdyby destruktor się nie wykonał, lub wykorzystać OnBeforeDestroy. Jeszcze się może całkiem nie odkręciłem, ale tak mi się wydaje. Jeśli finally gdzieś wewnątrz nie zgasi ewentualnego wyjątku to i tak złapie go except i tam będzie można rozprawić się z sytuacją.
Lodek - 20 Cze 2000, 03:00
[ciach]
| PS. Te flety, ...hmm... mógł się Pan wykazać większą inwencją. Chyba jest | Pan zmęczony i poziom myślenia coś się Panu obniżył, że nie wspomnę do | jakiego punktu. Umówmy się, że nie czytałem tego zdania. OK ?
Hmmm, ..... no to może najpierw wróćmy do kwestii umówienia się czy myśmy czytali Pańskie dictum, a potem dopiero będzie Pan - pardon - mędrkował o umawianiu się w kolejnej kwestii, dobrze? Wie Pan, prawie każdemu zdarza się czasem odwalić małą chamówę, ale żeby się jeszcze przy tym upierać?
Lodek - 20 Cze 2000, 03:00
Masz rację, mój błąd. Następnym razem napiszę: flety ;-
Hmmm ....
No, to może niech Pan jednak tak nie pisze, bo się pogniewamy.
Krzysztof Szyszka - 20 Cze 2000, 03:00
Hmmm, ..... no to może najpierw wróćmy do kwestii umówienia się czy myśmy czytali Pańskie dictum, a potem dopiero będzie Pan - pardon - mędrkował o umawianiu się w kolejnej kwestii, dobrze? Wie Pan, prawie każdemu zdarza się czasem odwalić małą chamówę, ale żeby się jeszcze przy tym upierać?
Czy wyście obaj ze Sławkiem powariowali na punkcie tych fletów, czy ze mną jest już naprawdę coś nie tak. To nie było ani o Was, ani do Was !!!
Bartek wytknął mi błąd, że o dwóch facetach nie pisze się "oboje" tylko "obaj" i przyznałem mu rację. Do tego pozwoliłem sobie na takie słowne skojarzenie,
proso, bo prościej. I tam było wyraźnie zaznaczone, że to jest żart. Jak teraz mam się z tego żartu już drugi raz tłumaczyć, to chyba powinienem pojechać na urlop i przestać sobie żartować na tym forum, bo mi się wkrótce wszyscy poobrażają.
Obydwu urażonych Panów niniejszym przepraszam, choć nawet przez myśl mi nie przeszło "odwalać małej chamówy", jak Pan to ładnie ujął, bo i po co miałbym to robić udzielając Panu jednocześnie rzeczowej odpowiedzi.
P.S. Obój, instrument muzyczny dęty drewniany, o wysokiej skali, dźwięku nieco nosowym. Flet, instrument muzyczny dęty drewniany, o wysokiej skali i miękkim dźwięku.
Fiedzia - 20 Cze 2000, 03:00
| Masz rację, mój błąd. | Następnym razem napiszę: flety ;- Hmmm ....
No, to może niech Pan jednak tak nie pisze, bo się pogniewamy.
Widzę, że nie zrozumiałeś dowcipu - oboje i flety to instrumenty muzyczne.
Pozdrawiam
Bartek Dajewski - 21 Cze 2000, 03:00
Cześć,
[...]
po wyjątku w destruktorze nie jest (!) zwalniana pamięć po komponencie
Niestety - nie myliłem się: jeżeli podczas wykonywania destruktora wystąpi wyjątek, to nie zostanie wywołana procedura _ClassDestroy a w konsekwencji metoda FreeInstance, a więc pamięć nie zostanie zwolniona :-((( Sprawdziłem to wczoraj u kumpla na D4 i u siebie na D3. W takim razie pytania: - czy ktoś to może sprawdzić na D5 ? - czy ktoś ma pomysł na prawidłową metodę pisania komponentów, które w destruktorze muszą jeszcze wykonać jakieś "ryzykowne" operacje. To nie jest wcale takie nietypowe, bo dotyczy np. wszystkich komponentów, które funkcjonują na zasadzie bufora danych i przy ich niszczeniu muszą coś zrobić z zawartością bufora. Jedynie konstrukcja: try {...} except end; daje gwarancję zwolnienia pamięci, ale jest opisana w HELP-ie jako niezalecana (choć nie potrafię tego fragmentu teraz znaleźć), a poza tym powoduje wygaszenie każdego wyjątku bez komunikatu o nim. Jak się nie obrócić d*a z tyłu :-(.
Destruktor w D3 i D4 wygląda tak: destructor Klasa.Destroy; begin {kod wpisany przez człowieka} // <auto= kod generowany automatycznie przez kompilator if ShouldFreeInstance then FreeInstance; // </auto end; gdzie ShouldFreeInstance jest wymyśloną nazwą niejawnego parametru przekazywanego do destruktora, a informującego o tym, czy pamięć ma być zwolniona. Przy wywołaniu zmienna.Destroy lub Free ma on wartość True, a przy wywołaniu inherited Destroy - wartość False.
... a (na mój rozum) powinno być tak: destructor Klasa.Destroy; begin // <auto try // </auto {kod wpisany przez człowieka} // <auto finally if ShouldFreeInstance then FreeInstance; end; // </auto end; Tylko, że samemu nie da się tego zrobić - to musi być wbudowane w kompilator. --- Pozdrawiam :-) Bartek
Bartek Dajewski - 21 Cze 2000, 03:00
Cześć,
[...]
Jeśli jest tu coś nie tak to piszcie i palcie mnie na stosie za herezję.
Wszystko prawda, ale jest kłopot z komponentami, które wewnątrz destruktora mogą wygenerować wyjątek - chodzi o zwalnianie pamięci. Nieco szerzej o tym w tym samym wątku list z 20.06.2000 17:20 i następne.
PS. Dajcie jakiś przykład na zkompresowanie przy użyciu Zlib pliku "C:\Test\readme.txt".Nie mam CD i nie mam pliku zlib.pas
Piszę z pamięci: var src, dst : TFileStream; cmpr : TCompressionStream; begin src := nil; dst := nil; cmpr := nil; try src := TFileStream.Create('C:\Test\readme.txt', fmReadOnly); dst := TFileStream.Create('spakowany.cośtam', fmCreate); cmpr := TCompressionStream.Create(dst, rodzaj_kompresji{fast, max itp.}); cmpr.CopyFrom(src, 0); finally cmpr.Free; dst.Free; src.Free; end; end; --- Pozdrawiam :-) Bartek
Lodek - 21 Cze 2000, 03:00
PS. Dajcie jakiś przykład na zkompresowanie przy użyciu Zlib pliku "C:\Test\readme.txt".Nie mam CD i nie mam pliku zlib.pas
Leci na priva grzeczniutki zlibek.
Bartek Dajewski - 21 Cze 2000, 03:00
Cześć,
Nie mam CD i nie mam pliku zlib.pas
Pod adresem <ftp://ftp.freesoftware.com/pub/infozip/zlib/znajdziesz pełny zestaw źródeł (i to nowszych niż na moim Delphi-CD) i dokumentacji. To jest FreeWare. Gdybyś miał jakiś problem ze ściągnięciem - daj znać, mogę podrzucić na PRIV. ZLIB.PAS ma 16Kb, ZLIB.DCU (do D3) ma 48Kb, a zlib.zip ma 219Kb. ZLIB.PAS nie zawiera źródeł kompresujących i dekompresujących - tylko definicję TCompression/DecompressionStream i korzysta z gotowych funkcji
ZLIB.ZIP zawiera wszystko, również przykłady dla różnych platform i języków programowania. --- Pozdrawiam :-) Bartek
Krzysztof Szyszka - 21 Cze 2000, 03:00
Destruktor w D3 i D4 wygląda tak: destructor Klasa.Destroy; begin {kod wpisany przez człowieka} // <auto= kod generowany automatycznie przez kompilator if ShouldFreeInstance then FreeInstance; // </auto end; gdzie ShouldFreeInstance jest wymyśloną nazwą niejawnego parametru przekazywanego do destruktora, a informującego o tym, czy pamięć ma być zwolniona. Przy wywołaniu zmienna.Destroy lub Free ma on wartość True, a przy wywołaniu inherited Destroy - wartość False.
To jest jakieś kuriozum i ja bałbym się czegoś takiego używać w swoim programie. Destruktor to jest destruktor, który ma bez obcyndalania się zwolnić całą pamięć zaalokowaną przez dany obiekt. Koniec filozofii na temat destruktorów. Jeżeli mogą się z tym wiązać jakieś inne problemy, to należy rozwiązać je albo w innym miejscu, albo w inny sposób, albo
Bartek Dajewski - 21 Cze 2000, 03:00
Cześć,
dyskusyjnych [...]
| gdzie ShouldFreeInstance jest wymyśloną nazwą niejawnego parametru | przekazywanego do destruktora, a informującego o tym, czy pamięć ma być | zwolniona. Przy wywołaniu zmienna.Destroy lub Free ma on wartość True, a | przy wywołaniu inherited Destroy - wartość False. To jest jakieś kuriozum i ja bałbym się czegoś takiego używać w swoim programie. Destruktor to jest destruktor, który ma bez obcyndalania się zwolnić całą pamięć zaalokowaną przez dany obiekt. Koniec filozofii na temat destruktorów. Jeżeli mogą się z tym wiązać jakieś inne problemy, to należy rozwiązać je albo w innym miejscu, albo w inny sposób, albo
Jeżeli pisząc kuriozum masz na myśli "if ShouldFreeInstance then ...", to jesteś w błędzie. Używasz tego w programie i to nagminnie, a teraz się bój ;-). Zajrzyj do okienka CPU Window przy uruchamianiu krokowym a sam się przekonasz. Taka jest strategia działania destruktorów odkąd pamiętam i to tak samo w paskalu jak w C. Destruktor musi wiedzieć czy ma zwolnić pamięć i musi (!) jej czasem nie zwalniać. Dlaczego? A dlatego, że pisząc zmienna.Destroy pośrednio wywołujesz wiele destruktorów z klas "rodziców", a tylko raz FreeMem. To jest tak samo jak z konstruktorem: Klasa.Create to wszystkie odziedziczone konstruktory, ale tylko jeden GetMem. W dodatku konstruktor klasy nadrzędnej nie wie jaki jest jej aktualny rozmiar, bo każda klasa potomna mogła sobie dorzucić jakieś zmienne. Kompilator przekazuje do wszystkich metod niejawne parametry. Gdyby próbować zapis obiektowy zastąpić klasycznym i w ten sposób ujawnić wszystkie niejawne parametry, to wyglądałoby to mniej więcej tak: zmienna.Destroy; =Destroy(zmienna, True); inherited Destroy; =Destroy(Self, False); i nie ma na to rady - tak musi być. Mnie się tylko nie podoba, że wywołanie FreeInstance nie jest w jakiejś (niejawnej) klamrze typu finally .. end; To samo dotyczy również zwalniania pamięci po Stringach zadeklarowanych wewnątrz klasy. W normalnych warunkach są zwalniane przez destruktor, ale w przypadku wystąpienia wyjątku - pamięć nie będzie zwolniona. Im dokładniej temu się przyglądam tym czarniej to widzę. Wniosek: nie wolno dopuszczać do wystąpienia wyjątku wewnątrz destruktora!!! Tylko co zrobić, jeżeli taki wyjątek wystąpi z przyczyn niezależnych ?... :-( --- Pozdrawiam :-) Bartek
Krzysztof Swiatkowski - 21 Cze 2000, 03:00
- czy ktoś ma pomysł na prawidłową metodę pisania komponentów, które w destruktorze muszą jeszcze wykonać jakieś "ryzykowne" operacje. To nie jest wcale takie nietypowe, bo dotyczy np. wszystkich komponentów, które funkcjonują na zasadzie bufora danych i przy ich niszczeniu muszą coś zrobić z zawartością bufora. Jedynie konstrukcja:
Przy niszczeniu jest już trochę późno na robienie czegokolwiek. Spróbuj z funkcją BeforeDestruction.
try {...} except end; daje gwarancję zwolnienia pamięci, ale jest opisana w HELP-ie jako niezalecana (choć nie potrafię tego fragmentu teraz znaleźć), a poza tym powoduje wygaszenie każdego wyjątku bez komunikatu o nim. Jak się nie obrócić d*a z tyłu :-(.
try
except ShowException(ExceptObject, ExceptAddr); // albo { ExceptionErrorMessage(ExceptObject, ExceptAddr, pC, 255); OutputDebugString(pC); } end;
... a (na mój rozum) powinno być tak: destructor Klasa.Destroy; begin // <auto try // </auto {kod wpisany przez człowieka} // <auto finally if ShouldFreeInstance then FreeInstance; end; // </auto end;
A czemu nie: try //blah blah finally inherited Destroy; end;
Hopbit
Krzysztof Szyszka - 21 Cze 2000, 03:00
Jeżeli pisząc kuriozum masz na myśli "if ShouldFreeInstance then ...", to jesteś w błędzie. Używasz tego w programie i to nagminnie, a teraz się bój ;-).
Nigdy nie bałem się rzeczowej krytyki i nadal nie zamierzam sie bać:-)
Zajrzyj do okienka CPU Window przy uruchamianiu krokowym a sam się przekonasz. Taka jest strategia działania destruktorów odkąd pamiętam i to tak samo w paskalu jak w C. Destruktor musi wiedzieć czy ma zwolnić pamięć i musi (!) jej czasem nie zwalniać. Dlaczego? A dlatego, że pisząc zmienna.Destroy pośrednio wywołujesz wiele destruktorów z klas "rodziców", a tylko raz FreeMem.
Wszystko się zgadza, tylko to dotyczy szczegółów implementacji dziedziczenia, na które ja nie mam żadnego wpływu i co więcej, nawet nie muszę ich znać żeby tworzyć poprawne konstruktory/destruktory. Ja zrozumiałem, że ty chcesz taką metodę wprowadzić przy pisaniu destruktora własnego obiektu.
To jest tak samo jak z konstruktorem: Klasa.Create to wszystkie odziedziczone konstruktory, ale tylko jeden GetMem. W dodatku konstruktor klasy nadrzędnej nie wie jaki jest jej aktualny rozmiar, bo każda klasa potomna mogła sobie dorzucić jakieś zmienne. Kompilator przekazuje do wszystkich metod niejawne parametry. Gdyby próbować zapis obiektowy zastąpić klasycznym i w ten sposób ujawnić wszystkie niejawne parametry, to wyglądałoby to mniej więcej tak: zmienna.Destroy; =Destroy(zmienna, True); inherited Destroy; =Destroy(Self, False); i nie ma na to rady - tak musi być. Mnie się tylko nie podoba, że wywołanie FreeInstance nie jest w jakiejś (niejawnej) klamrze typu finally .. end;
To jest cena obiektowości. Przecież nawet wywołanie dowolnej metody każdego obiektu wiąże się z niejawnym przekazaniem wskaźnika do tego obiektu jako pierwszy parametr wywołania. Nie wiem, czemu miałoby mi się to podobac lub nie podobać.
To samo dotyczy również zwalniania pamięci po Stringach zadeklarowanych wewnątrz klasy. W normalnych warunkach są zwalniane przez destruktor, ale w przypadku wystąpienia wyjątku - pamięć nie będzie zwolniona. Im dokładniej temu się przyglądam tym czarniej to widzę. Wniosek: nie wolno dopuszczać do wystąpienia wyjątku wewnątrz destruktora!!!
No i w tym momencie dochodzisz (trochę inną drogą) do takich wniosków, jakie zarówno ja, jak i Hobbit próbujemy Ci przekazać.
Tylko co zrobić, jeżeli taki wyjątek wystąpi z przyczyn niezależnych ?... :-(
by nie było już przyczyn niezależnych :-). Jak nic nie skopiesz w programie, to samo zwolnienie pamięci zawsze się uda. A jak skopiesz, to przyczyna wyjątku i tak będzie gdzie indziej.
Lodek - 21 Cze 2000, 03:00
[ciach]
P.S. Obój, instrument muzyczny dęty drewniany, o wysokiej skali, dźwięku nieco nosowym. Flet, instrument muzyczny dęty drewniany, o wysokiej skali i miękkim
dźwięku.
[ciach]
Panie Krzysiu, wszystko wyjaśnione. Przepraszam, że się uniosłem. Jakoś nie zwróciłem uwagi na uwagę Pana Bartka co do "obaj" i stąd lipa. Ciekawe, że Pan Sławek zareagował tak jak ja.
Znacie ten kawał o Jasiu i Pani psycholog (czy jakoś tak)?
Lodek - 21 Cze 2000, 03:00
z zawartością bufora. Jedynie konstrukcja: try {...} except end; daje gwarancję zwolnienia pamięci, ale jest opisana w HELP-ie jako niezalecana (choć nie potrafię tego fragmentu teraz znaleźć), a poza tym powoduje wygaszenie każdego wyjątku bez komunikatu o nim. Jak się nie obrócić d*a z tyłu :-(.
Cytauję z helpa, pardon że aż tyle, ale to właściwie jest chyba odpowiedź na to co zacytowałem wyżej:
When an exception is raised, control is transferred to the innermost exception handler that can handle exceptions of the given class. The search for an exception handler starts with the most recently entered and not yet exited try...except statement. If that try...except statement cannot handle exceptions of the given class, the next most recently entered try...except statement is examined. This propagation of the exception continues until an appropriate handler is found, or there are no more active try...except statements. In the latter case a runtime error occurs and the application is terminated.
Tu się trochę zdziwiłem: "application is terminated" ?!?!? Hmmmm ... No, dobra, tak więc jednym rozwiązaniem jest wsadzenie try except w inny try except. W tym bardziej wewnętrznym reagujemy na krytyczne błędym, a w tym zewnętrznym na resztę błędów. To tak na mój chłopski rozum. Drugie rozwiązanie to:
To determine whether the exception block of a try...except statement can handle a particular exception, the on...do exception handlers are examined in order of appearance. The first exception handler that lists the exception class or a base class of the exception is considered a match. If an exception block contains an else part, and if none of the on...do exception handlers match the exception, the else part is considered a match. An exception block that contains only a statement list is considered a match for any exception.
... i wsadzić obsługę krytycznego błędu na początku bloku except end i dopisać obsługę reszty w else. Tak to widzę.
Bartek Dajewski - 21 Cze 2000, 03:00
Cześć,
[...]
Wszystko się zgadza, tylko to dotyczy szczegółów implementacji dziedziczenia, na które ja nie mam żadnego wpływu i co więcej, nawet nie muszę ich znać żeby tworzyć poprawne konstruktory/destruktory. Ja zrozumiałem, że ty chcesz taką metodę wprowadzić przy pisaniu destruktora własnego obiektu.
Różnica w naszej ocenie sytuacji bierze się chyba stąd, że dla mnie wystąpienie wyjątku w destruktorze powinno się dać "naprawić", a według Ciebie (jeżeli dobrze zrozumiałem) już samo wystąpienie wyjątku jest niedopuszczalne i należy doprowadzić do sytuacji, kiedy w ogóle nie będzie miało miejsca. No cóż - z faktami się nie dyskutuje i wygląda na to, że tak samo ja Ty myślą chłopcy z Borland'a. Od tej pory obiecuję, że na pytanie "Co sądzisz o obsłudze wyjątków w destruktorach w Delphi?" będę odpowiadał "Mam na ten temat swoje zdanie, ale je potępiam!". [...]
| Wniosek: nie wolno dopuszczać do | wystąpienia wyjątku wewnątrz destruktora!!! No i w tym momencie dochodzisz (trochę inną drogą) do takich wniosków, jakie zarówno ja, jak i Hobbit próbujemy Ci przekazać.
Ale ten wniosek mnie smuci :-( A właściwie bardziej to, że nie ma o tym nic w HELP-ie. Wszystko pięknie opisane o konstrukcjach try...finally...end, ale o tym, że w destruktorze nie ma sensu ich stosować to już cisza. Może to zbyt mocne słowa, ale w destruktorze taka konstrukcja pozwala tylko ratować co się da, podczas gdy w "zwykłych" procedurach da się uratować wszystko. To niestety powoduje, że pisząc jakiś komponent musisz o nim wiedzieć prawie wszystko, żeby po nim dziedziczyć, a nie tylko tyle ile jest potrzebne do zrealizowania jakiegoś zadania. A to się moim zdaniem kłóci z założeniami programowania obiektowego i nieco zmniejsza korzyści z niego płynące. A tak było pięknie, kiedy o tym nie wiedziałem.... --- Pozdrawiam :-) Bartek PS. <Erratajest: Hobbit ma być: Hopbit </Errata
Bartek Dajewski - 21 Cze 2000, 03:00
Cześć,
| - czy ktoś ma pomysł na prawidłową metodę pisania komponentów, które w | destruktorze muszą jeszcze wykonać jakieś "ryzykowne" operacje. To nie jest | wcale takie nietypowe, bo dotyczy np. wszystkich komponentów, które | funkcjonują na zasadzie bufora danych i przy ich niszczeniu muszą coś zrobić | z zawartością bufora. Jedynie konstrukcja:
Przy niszczeniu jest już trochę późno na robienie czegokolwiek.
A niby dlaczego? W destruktorze jest jak najlepsze miejsce na zrobienie porządku po sobie i dokończenie tego, co do tej pory nie zostało dokończone (tak przynajmniej myślałem). Jeżeli ktoś mi uzasadni dlaczego destruktor nie powinien kończyć niedokończonych zadań komponentu, to uznam, że zachowanie Delphi jest poprawne, a błąd jest w moim rozumowaniu. Na razie jednak uważam, że pozostawianie zajętej pamięci, którego nie da się uniknąć stosując dostępne mechanizmy jest błędem niezależnie od tego czy jest to spowodowane wyjątkiem czy nie. Inaczej mówiąc chodzi o to, że Delphi dostarcza doskonałych konstrukcji do przechwytywania błędów po to, żeby pisać idiotoodporne programy, a samo - w automatycznie generowanym kodzie - z tych mechanizmów nie korzysta. Wiem, że można to wszystko zrobić inaczej, ale dlaczego nie jest nigdzie
?
Spróbuj z funkcją BeforeDestruction.
Nie mogę - mam D3. Ale rzeczywiście to jest jakieś rozwiązanie dla szczęśliwych posidaczy nowszych wersji. Postaram się sprawdzić kiedy to się wywołuje i czy rozwiązuje problem.
| try | {...} | except | end; | daje gwarancję zwolnienia pamięci, ale jest opisana w HELP-ie jako | niezalecana (choć nie potrafię tego fragmentu teraz znaleźć), a poza tym | powoduje wygaszenie każdego wyjątku bez komunikatu o nim. Jak się nie | obrócić d*a z tyłu :-(. try
except ShowException(ExceptObject, ExceptAddr); // albo { ExceptionErrorMessage(ExceptObject, ExceptAddr, pC, 255); OutputDebugString(pC); } end;
OK, ale to załatwia tylko sprawę komunikatu na ekranie, a i tak wyjątek jest gaszony i dalsza jego propagacja ustaje. Miałem na myśli "komunikat" dla programu w sensie wyjątku, który można przechwycić w kodzie i odpowiednio zareagować, a nie dla biednego użytkownika, który i tak nic z niego nie zrozumie ;-)
A czemu nie: try //blah blah finally inherited Destroy; end;
Jak najbardziej tak, ale dla uproszczenia nie umieściłem tego w moim przykładzie, bo chciałem w nim tylko zaznaczyć co powinien automatycznie wygenerować kompilator, a co człowiek. A tak w ogóle, to w świetle tego co było pisane taki przykład to schizofrenia ;-). Albo zakładam, że wyjątek w destruktorze może wystąpić i tworzę "sekcje ochronne", albo wiem, że nie wystąpi i ich nie tworzę. Tylko co mi po tych sekcjach, jeżeli wiem, że przy wyjątku i tak nie zostanie zwolniona pamięć (nie chodzi mi o wywołanie inherided Destroy tylko o zwolnienie pamięci)? Wniosek <powtarzam się: W Delphi nie wolno dopuścić do wystąpienia wyjątku w destruktorze, bo powoduje to albo zaśmiecanie pamięci albo zablokowanie propagacji tego wyjątku tuż po jego wystąpieniu </powtarzam się. Dla mnie jedno i drugie rozwiązanie jest złe. Ciekawostką jest fakt, że w konstruktorze jest kod obsługi wyjątku, który wywołuje destruktor i zwolnienie pamięci ale wygląda na to, że nie jest używany - zamiast niego jest wstawione "JMP _HandleAnyException" (mogę się mylić - tylko przeglądałem procedure _ClassCreate w SYSTEM.PAS). --- Pozdrawiam :-) Bartek
Bartek Dajewski - 21 Cze 2000, 03:00
Cześć,
[...]
This propagation of the exception continues until an appropriate handler is found, or there are no more active try...except statements. In the latter case a runtime error occurs and the application is terminated. Tu się trochę zdziwiłem: "application is terminated" ?!?!?
Ja też. Mam nadzieję, że to pomyłka i należy rozumieć, że zostaje przerwane wykonywanie bieżącego ... hmmmm ... nie wiem jak to zgrabnie i krótko przetłumaczyc: "normal flow of execution in an application". --- Pozdrawiam :-) Bartek
Bartek Dajewski - 21 Cze 2000, 03:00
Cześć,
[...]
Znacie ten kawał o Jasiu i Pani psycholog (czy jakoś tak)?
Nie i chętnie poczytam (na otarcie łez wywołanych meritum tego wątku). Byle było choć trochę krótsze niż słynny już "Thriller rolniczy" ;-) (albo na PRIV). --- Pozdrawiam :-) Bartek
Sławomir Adamski - 21 Cze 2000, 03:00
Witam
dyskusyjnych
[ciach, bo mi wstyd, że strzeliłem taka plamę]
| Ja widzę oba komunikaty, a Pan? Komunikaty, to ja też widzę dwa, za to wyjątku ani jednego ;-) (proszę sobie sprawdzić, że mimo iż Form2 jest Nil, to podstawienie pod Caption nie generuje wyjątku !!!).
No tak, ale swego wstydu nie żałuję. Sporo się dowiedziałem od Pana i innych dyskutantów.
Sławomir Adamski - 21 Cze 2000, 03:00
Witam
Czy wyście obaj ze Sławkiem powariowali na punkcie tych fletów, czy ze mną jest już naprawdę coś nie tak. To nie było ani o Was, ani do Was !!! Bartek wytknął mi błąd, że o dwóch facetach nie pisze się "oboje" tylko "obaj" i przyznałem mu rację. Do tego pozwoliłem sobie na takie słowne skojarzenie,
(też instrumenty)
No dobra, wychodzi na to, że to ja mam brudne i włochate myśli. Przyzwyczajenie drugą naturą człowieka. Za moich młodych czasów za takie coś dostawało się po ryju, a nie odpowiedź.
Krzysztof Szyszka - 21 Cze 2000, 03:00
Różnica w naszej ocenie sytuacji bierze się chyba stąd, że dla mnie wystąpienie wyjątku w destruktorze powinno się dać "naprawić", a według Ciebie (jeżeli dobrze zrozumiałem) już samo wystąpienie wyjątku jest niedopuszczalne i należy doprowadzić do sytuacji, kiedy w ogóle nie będzie miało miejsca. No cóż - z faktami się nie dyskutuje i wygląda na to, że tak samo ja Ty myślą chłopcy z Borland'a.
ROTFL. To raczej ja przyjąłem rozwiązania chłopców z Borlanda za własne :-)
Od tej pory obiecuję, że na pytanie "Co sądzisz o obsłudze wyjątków w destruktorach w Delphi?" będę odpowiadał "Mam na ten temat swoje zdanie, ale je potępiam!".
ROTFL. To prawie jak moja maksyma: "Mam na ten temat własne zdanie, ale się z nim nie zgadzam!"
[...] | Wniosek: nie wolno dopuszczać do | wystąpienia wyjątku wewnątrz destruktora!!! | No i w tym momencie dochodzisz (trochę inną drogą) do takich wniosków, jakie zarówno ja, jak i Hobbit próbujemy Ci przekazać.
Ale ten wniosek mnie smuci :-( A właściwie bardziej to, że nie ma o tym nic w HELP-ie. Wszystko pięknie opisane o konstrukcjach try...finally...end, ale o tym, że w destruktorze nie ma sensu ich stosować to już cisza. Może to zbyt mocne słowa, ale w destruktorze taka konstrukcja pozwala tylko ratować co się da, podczas gdy w "zwykłych" procedurach da się uratować wszystko.
Zupełnie niepotrzebnie :-). Nie należy ratować, co najwyżej naprawiać, jeśli w danym momencie potrafisz (try except end), ewentualnie zabezpieczyć sobie tyły, gdyby się coś nie udało (try finally end). Resztę należy olać !!!
To niestety powoduje, że pisząc jakiś komponent musisz o nim wiedzieć prawie wszystko, żeby po nim dziedziczyć, a nie tylko tyle ile jest potrzebne do zrealizowania jakiegoś zadania. A to się moim zdaniem kłóci z założeniami programowania obiektowego i nieco zmniejsza korzyści z niego płynące. A tak było pięknie, kiedy o tym nie wiedziałem....
Na otarcie łez powiem Ci, że jak ja stawiałem pierwsze kroki w Delphi, to też przez pierwsze trzy miesiące próbowałem walczyć z wyjątkami, dopóki nie zrozumiałem, że tak naprawdę, to one działają na moją korzyść i właściwie prawie nigdy nie muszę ich obsługiwać !!! Od tego momentu programowanie w Delphi stało się przyjemnością, a kontrukcji try ... except end używam bardzo, bardzo rzadko, za to try ... finally end dość często, właściwie to już automatycznie i ma to ścisły związek z tym co jest PRZED try, a nie MIĘDZY try i finally.
przestaniesz mieć takie dylematy, o jakich teraz piszesz. Ręczę Ci, że każdy
oraz by sam zachowywał się prawidłowo w otoczeniu innych klas Delphi.
Tak naprawdę, to każda nowa klasa jest jedynie drobnym trybikiem w potężnej maszynie Delphi, która kręci się według własnych reguł i jeśli nie chcesz by Twój trybik trzeszczał, to dostosuj się do reguł tej maszynki. Chyba, że wolisz płynąć pod prąd.
PS. <Erratajest: Hobbit ma być: Hopbit </Errata
Nie dam się drugi raz sprowokować :-), nic nie napisze. Zobacz co rozpętałeś poprzednim razem.
Krzysztof Szyszka - 21 Cze 2000, 03:00
Cześć, [...] | This propagation of the exception continues until an | appropriate handler is found, or there are no more active try...except | statements. In the latter case a runtime error occurs and the application | is terminated. | Tu się trochę zdziwiłem: "application is terminated" ?!?!?
Ja też. Mam nadzieję, że to pomyłka i należy rozumieć, że zostaje przerwane wykonywanie bieżącego ... hmmmm ... nie wiem jak to zgrabnie i krótko przetłumaczyc: "normal flow of execution in an application".
Nie ma się co dziwić, tak istotnie jest, tylko na codzień tego nie widać, bo cała aplikacja kręci się w jednej głównej pętli, w której następuje obsługa wszystkich wyjątków, które nie zostały wcześnej obsłużone.
Jak wygenerujesz sobie wyjątek w pierwszej instrukcji modułu projektu, to reszta programu się nie wykona.
Krzysztof Szyszka - 21 Cze 2000, 03:00
No dobra, wychodzi na to, że to ja mam brudne i włochate myśli. Przyzwyczajenie drugą naturą człowieka. Za moich młodych czasów za takie coś dostawało się po ryju, a nie odpowiedź.
Jak Panu bardzo na tym zależy, to mogę dać. :-) Może na najbliższym zlocie ?
Krzysztof Szyszka - 21 Cze 2000, 03:00
Panie Krzysiu, wszystko wyjaśnione. Przepraszam, że się uniosłem. Jakoś nie zwróciłem uwagi na uwagę Pana Bartka co do "obaj" i stąd lipa. Ciekawe, że Pan Sławek zareagował tak jak ja.
(..............................................) Kometarz wycięty przez kreator samokontroli, boś sie Pan zrobił drażliwy jak stado rekinów :-)
Lodek - 21 Cze 2000, 03:00
| Znacie ten kawał o Jasiu i Pani psycholog (czy jakoś tak)? Nie i chętnie poczytam (na otarcie łez wywołanych meritum tego wątku). Byle było choć trochę krótsze niż słynny już "Thriller rolniczy" ;-) (albo na PRIV).
Kurczę, usłyszałem to w weekend, bedąc na poprawinach po weselu kumpla, więc umysł i pamięć były nie te, ale powiedzmy, że leci to jakoś tak (Panie proszę o nie czytanie, chociaż nie jest to [AŻ] TAKI dowcip [który Panowie sobie opowiadają, jak się nieco rozbawią]):
Na uczelni na zajęciach z psychologii Pani Profesor zadała studentom jako zadanie domowe (czy pracę semstralną) opracowanie na wybrany z listy temat. Jasio wybrał temat "Psychoseksualne aspekty zachowania kobiet w zależności od ich stanu cywilnego".
"Idą ulicą trzy kobiety, każda trzyma w ręku loda. Pierwsza liże swojego loda, druga ssie, trzecia - gryzie. Ktora z nich jest mężatką?"
Po przeczytaniu pracy Pani Profesor poprosiła Jasia do siebie i mówi:
- Jasiu, myślę, że potraktowałeś temat zbyt powierzchownie, żeby nie powiedzieć trywialnie lub wręcz zupełnie prostacko i nietrafnie. Co chciałeś właściwie przekazać w swojej pracy?
A Jaś na to:
- Pani Profesor, bez ogródek! Która była mężatką?
- (profesorka cośtam odpowiada, zawile i z uzasadnieniami)
. . . . . . . . . . . . . . . . . . . . . . . . . .
- Nie, Pani profesor! Mężątką była ta, która miała obrączkę na palcu, ale Pani odpowiedź ... to jest właśnie temat mojego opracowania.
Aha, a Thriller Roliczy? Nie podobał się Panu? Co? IMHO Był super i na czasie. Przynajmniej jak na moje możliwości, chociaż teraz trochę bym to inaczej zrobił.
Przy okazji tego buga w Wordzie, o którym ostatnio pisali w Enterze to za jakiś czas, jak będzie chwila, napiszę na listę thriller p.t. "Elektryczny Kot Bojownik".
Lodek - 21 Cze 2000, 03:00
[...] | This propagation of the exception continues until an | appropriate handler is found, or there are no more active try...except | statements. In the latter case a runtime error occurs and the application | is terminated. | Tu się trochę zdziwiłem: "application is terminated" ?!?!?
Ja też. Mam nadzieję, że to pomyłka i należy rozumieć, że zostaje przerwane wykonywanie bieżącego ... hmmmm ... nie wiem jak to zgrabnie i krótko przetłumaczyc: "normal flow of execution in an application".
No, fano! ale Pan tu wyciął, a to chyba jest rozwiązanie Pana problemu - po prostu dać try except osadzony w innym try except. W tym wewnętrznym wyłapywać problemy w sprawach destruktora (o ile dobrze rozumiem problem), a w tym zewnętrznym resztę. Fakt, że łyso, ale co zrobić innego, hę?
Sławomir Adamski - 21 Cze 2000, 03:00
dyskusyjnych | No dobra, wychodzi na to, że to ja mam brudne i włochate myśli. | Przyzwyczajenie drugą naturą człowieka. Za moich młodych czasów za takie coś | dostawało się po ryju, a nie odpowiedź. Jak Panu bardzo na tym zależy, to mogę dać. :-) Może na najbliższym zlocie ?
Pańskie poczucie humoru jest zniewalające. Ale postaram się stawić.
Lodek - 22 Cze 2000, 03:00
(..............................................) Kometarz wycięty przez kreator samokontroli, boś sie Pan zrobił drażliwy jak stado rekinów :-)
aluzji, ot co i tyle! A Pan tu o rekinach prawisz i w ogóle. Pańska aluzja
Bartek i stąd zamieszanie.
Nic więcej nie odpisuję tu, bo właśnie na dyskusjach dostałem wyrazy niezrozumienienia nie tylko mojej odpowiedzi, ale również tego co było wcześniej w wątku, więc ... cóż, każdemu się zdarza.
Bartek Dajewski - 23 Cze 2000, 03:00
Cześć,
| Znacie ten kawał o Jasiu i Pani psycholog (czy jakoś tak)? | Nie i chętnie poczytam (na otarcie łez wywołanych meritum tego wątku). Byle | było choć trochę krótsze niż słynny już "Thriller rolniczy" ;-) (albo na | PRIV).
[... kawał o Jasiu] :-)) Dobre. Znałem to, ale w innej wersji, więc się nie połapałem po pytaniu o Jasia i Panią psycholog.
Aha, a Thriller Roliczy? Nie podobał się Panu? Co? IMHO Był super i na czasie. Przynajmniej jak na moje możliwości, chociaż teraz trochę bym to inaczej zrobił.
No nie, to nie tak!!! Podobał mi się! Pozwoliłem sobie nawet pobrać z www zbiór thriller_rolniczy.zip, żeby mieć co poczytać, kiedy będzie mi smutno. A chodziło mi o to, że gdyby rozmiar tego kawału był porównywalny z thrillerem, to mógłby przekraczać oczekiwania użytkowników grupy związane z
--- Pozdrawiam :-) Bartek
Bartek Dajewski - 23 Cze 2000, 03:00
Cześć,
[...] No, fano! ale Pan tu wyciął, a to chyba jest rozwiązanie Pana problemu - po prostu dać try except osadzony w innym try except. W tym wewnętrznym wyłapywać problemy w sprawach destruktora (o ile dobrze rozumiem problem), a w tym zewnętrznym resztę. Fakt, że łyso, ale co zrobić innego, hę?
Wyciąłem, bo chciałem odpowiedzieć później - po dokładniejszym zbadaniu problemu. Niestety rozwiązanie nadal ma tę samą ułomność: jeżeli w destruktorze wystąpi wyjątek i nie zostanie zaraz wygaszony przez except - nie zostanie zwolniona pamięć po komponencie ani po Stringach, które zawierał (pewnie po Variantach też). I wtedy mamy taką dziwną sytuację - np. wykonały się wszystkie destruktory, a pamięć nadal jest zajęta.
Badałem, badałem... a oto co wybadałem (wszystko dotyczy D3 i D4. Do D5 nie miałem dostępu): Jak się nietrudno domyślić zaraz po destruktorze na tapetę trafił konstruktor. I tu, ku mojej radości, stwierdziłem, że kod niejawny (1) zawiera coś w rodzaju konstrukcji try...except...end. Żeby było śmieszniej jest to rozwiązane trochę inaczej niż zwykle w tego rodzaju konstrukcjach, bo całość jest w sprytny sposób przeniesiona do innej procedury (_ClassCreate), a w konstruktorze jest tylko jej wywołanie w dodatku warunkowe. Ale mniejsza o to, działa to tak, jakby było wpisane w jednym miejscu. W efekcie konstruktor najwyższego poziomu wygląda tak (2): dla Delphi 3: constructor Klasa.Create; begin <auto NewInstance; try </auto <wpisał człowiek {tu jest miejsce dla wszystkich linii wpisanych w implementacji konstruktora} {na przykład: inherited Create} </wpisał człowiek <auto except Free; raise; end; </auto end;
Dla Delphi 4: {początek taki sam} <auto except if Self <nil then begin Destroy; {wywołany tak, żeby nie wywoływał _ClassDestroy} {... czyli nie zwalniał pamięci} _ClassDestroy; end; raise; end; </auto
Konstruktor wywołany przez inherited Create nie zawiera tych części, które umieściłem w klamrach <auto, </auto. Tak więc ten sam kod może w zależności od sposobu wywołania zawierać lub nie zawierać konstrukcji try...except...end. Trochę to zamotane, ale to jest tak, jakby przy użyciu if-a "włączać" całą konstrukcję, albo tylko linie zawarte między try a except.
I to mi się podoba i właśnie czegoś takiego oczekiwałem po kodzie wygenerowanym przez Delphi. Nadal natomiast nie rozumiem dlaczego ten sam mechanizm nie funkcjonuje w destruktorze... Przy okazji załączam listę (pewnie niekompletną) klas, których destruktory zawierają try...cośtam, albo raise: TThreadList, TCustomForm, TDataModule, TCompressionStream, TCustomWinSocket, TDataSet, TField, TBDECallback, TBlobStream, TBitmapCanvas, TOutlineNode.
zaakceptowane przez Borland. A więc jak to jest: można dopuszczać do wystąpienia wyjątku w destruktorze czy nie? O ile jestem w stanie przyjąć, że nie można, to w dalszym ciągu mam zastrzeżenia do firmy, że nie opisała tego wszystkiego w HELP-ie i to WIELKIMI LITERAMI.
(1) kod niejawny - dla uproszczenia tak sobie nazwałem to, co automatycznie generuje kompilator, a co nie ma bezpośredniego odpowiednika we wpisanych liniach programu. (2) konstruktor najwyższego poziomu - czyli zdefiniowany w klasie, która jest w tej chwili tworzona. Chodzi o wywołania typu Klasa.Create, a nie dotyczy konstruktorów wywołanych przez inherited Create z wnętrza innych konstruktorów. --- Pozdrawiam :-) Bartek PS. Nadal ponawiam prośbę o sprawdzenie tego dla D5. Sam spróbuję (jak będę miał trochę czasu) sprawdzić to na BCB, ale może mi się nie udać, bo mam tylko wersję Trial (tę z płyty Delphi 3), a ona ma ograniczenia na datę systemową.
Bartek Dajewski - 23 Cze 2000, 03:00
Cześć,
[...] | Od tej pory obiecuję, że na pytanie | "Co sądzisz o obsłudze wyjątków w destruktorach w Delphi?" będę odpowiadał | "Mam na ten temat swoje zdanie, ale je potępiam!". ROTFL. To prawie jak moja maksyma: "Mam na ten temat własne zdanie, ale się z nim nie zgadzam!"
Bo to właśnie miało być to samo tylko trochę dosadniej powiedziane :-)
[...]
| w destruktorze taka konstrukcja pozwala tylko ratować | co się da, podczas gdy w "zwykłych" procedurach da się uratować wszystko. Nie należy ratować, co najwyżej naprawiać, jeśli w danym momencie potrafisz (try except end), ewentualnie zabezpieczyć sobie tyły, gdyby się coś nie udało (try finally end). Resztę należy olać !!!
Rzeczywiście ratować to źle dobrane słowo. Miałem na myśli to co nazwałeś zabezpieczaniem tyłów. (Wybaczcie, że tak "obficie" cytuję, ale nie umiałem tak poskracać wcześniejszych wypowiedzi, żeby pozostawić związek między pytaniem i odpowiedzią.)
Na otarcie łez powiem Ci, że jak ja stawiałem pierwsze kroki w Delphi, to też przez pierwsze trzy miesiące próbowałem walczyć z wyjątkami, dopóki nie zrozumiałem, że tak naprawdę, to one działają na moją korzyść i właściwie prawie nigdy nie muszę ich obsługiwać !!! Od tego momentu programowanie w Delphi stało się przyjemnością, a kontrukcji try ... except end używam bardzo, bardzo rzadko, za to try ... finally end dość często, właściwie to już automatycznie i ma to ścisły związek z tym co jest PRZED try, a nie MIĘDZY try i finally. przestaniesz mieć takie dylematy, o jakich teraz piszesz. Ręczę Ci, że każdy oraz by sam zachowywał się prawidłowo w otoczeniu innych klas Delphi. Tak naprawdę, to każda nowa klasa jest jedynie drobnym trybikiem w potężnej maszynie Delphi, która kręci się według własnych reguł i jeśli nie chcesz by Twój trybik trzeszczał, to dostosuj się do reguł tej maszynki. Chyba, że wolisz płynąć pod prąd.
Rozumiem, że to nie herezja i sam też podpisuję się pod tym obiema rękami. Ja nie chcę walczyć z wyjątkami, tylko wiedzieć, że wystąpiły. A cały czas uważam, że sytuacja, w której wszystkie destruktory się wykonały, a pamięć jest ciągle zajęta jest niedobra. Tak jak Ty w ogóle (prawie) nie używam try..except, ale z testów wyszło, że to jedyna metoda doprowadzenia do zwolnienia pamięci po komponencie. Jeśli Cię jeszcze nie znudził cały ten problem i moje przydługie wywody - zajrzyj do dzisiejszego listu w tym samym wątku: "TCompressionStream.Destroy - co o tym sądzicie? :-(wnioski)". Tam opisałem o konstruktorach, destruktorach i wyjątkach to, co utwierdza mnie w przekonaniu, że trochę racji mam i że niektóre Borland-owe komponenty też płyną trochę pod prąd. Po prostu jest pewien rozdźwięk między obsługą wyjątku w konstruktorze i w destruktorze, a skoro taka obsługa w ogóle jest - to znaczy, że Borland o tym myślał i wymyślił coś, co <megalomania IMHO mogłoby być zrobione lepiej </megalomania.
PS. <Erratajest: Hobbit ma być: Hopbit </Errata Nie dam się drugi raz sprowokować :-), nic nie napisze. Zobacz co rozpętałeś poprzednim razem.
ale ... ja .... naprawdę ..... nie .... chciałem ..... ;-( Następnym razem będę bardziej uważał ;-) --- Pozdrawiam :-) Bartek
Krzysztof Szyszka - 23 Cze 2000, 03:00
I to mi się podoba i właśnie czegoś takiego oczekiwałem po kodzie wygenerowanym przez Delphi. Nadal natomiast nie rozumiem dlaczego ten sam mechanizm nie funkcjonuje w destruktorze... Przy okazji załączam listę (pewnie niekompletną) klas, których destruktory zawierają try...cośtam, albo raise: TThreadList, TCustomForm, TDataModule, TCompressionStream, TCustomWinSocket, TDataSet, TField, TBDECallback, TBlobStream, TBitmapCanvas, TOutlineNode.
zaakceptowane przez Borland. A więc jak to jest: można dopuszczać do wystąpienia wyjątku w destruktorze czy nie? O ile jestem w stanie przyjąć, że nie można, to w dalszym ciągu mam zastrzeżenia do firmy, że nie opisała tego wszystkiego w HELP-ie i to WIELKIMI LITERAMI.
Daj spokój temu śledztwu, bo dostaniesz obłędu ;-)
bo wolno, skoro program się kompiluje, ale _paktyka_ jest taka, że tam jest już za późno na naprawianie błędów, a konstrukcję try ... finally end stosuje się właściwie mechanicznie wszędzie tam, gdy coś działa na zasadzie begin/end i takie jej zastosowanie masz TCustomForm, TDataModule, innych nie sprawdzałem.
Jeżeli wykonało się BeginWrite, to do pary _musi_ wykonać się EndWrite i nie należy do tego dorabiać żadnej filozofi (nawet jeśli to występuje w destruktorze), bo taki sposób pisania jest właściwie automatyczny i po tym (między innym) można poznać dobrą szkołę programowania w Delphi.
P.S. Cały czas mam wrażenie, że zamiast szukać przyczyny występowania błędu w destruktorze próbujesz niwelować jego skutki. Nie obraź się, ale dla mnie to jest takie leczenie sy** przy pomocy pudru :-)
Lodek - 23 Cze 2000, 03:00
No nie, to nie tak!!! Podobał mi się! Pozwoliłem sobie nawet pobrać z www zbiór thriller_rolniczy.zip, żeby mieć co poczytać, kiedy będzie mi
smutno.
Ha, wiedziałem, że w końcu dostąpię sławy, chwały i poklasku! Nowy Hitchcock!
W czasuie kiedy pisałem stronkę z linkiem do screenów w związku Cat-Warrior-gate tak się nakręciłem, że aż wynotowałem sobie główne moje wątki, to jest materiał na taki zakręcony tekst, że normalnie ... Trzeba
Lodek - 23 Cze 2000, 03:00
Kurczę, to jest nieźle zakręcone, wsadzę to do archiwum i jak będę pisał coś co w zamierzeniach ma chodzić absolutnie przez 7:7\24:24 to sobie to obadam dokładnie.
Lodek - 23 Cze 2000, 03:00
Wie Pan, Panie Bartku, ja nie mam jakiegoś dużego doświadczenia w pisaniu
zabrałem za jedną rzecz, bo pisanie w kółko tych samych rzeczy mnie denerwuje, chociaż ma swoje dobre strony %*). No i powiem Panu, że ja często robię rzecz naganną, tzn. piszę komponent właściwie jakoś tak, że on nie zawiera innych obiektów, ma właściwie tylko metody - takie coś chyba jak interface'y (nie znam się na interface'ach, ale to jest chyba coś takiego). No, poza tym, że są pola, ale nieobiektowe: tylko typy proste, w tym pointery (wiem, że to nieeleganckie). Przykładowo wygląda to tak, że aplikacja używająca komponentu musi sama stworzyć dajmy na to TStringList, następnie podawać ten (tą?) TStringList do mojego komponentu jako parametr metody, a następnie sama zadbać o zwolnienie TStringList. Podobnie jest z eventami - po prosty komponent jest tylko od tego, żeby miał OnCośtam, a w OnCośtam, na zewnątrz komponentu oprogramowuje się jakieś wstawianie do TStringList, wyświetlanie czegośtam etc.
Wiem, że to może być kłopotliwe, jest trochę może i nieeleganckie, ale w sumie jakoś się sprawdza.
Czy nie sądzicie, że studia są beznadziejne? określenie dowolnej współrzędnej do wolnego obrazka w mozilli jak uniknąć kasuj.php?del_id=$x ? autoryzacja (trochę długie :)) jak dodać ukośnik To co z ta gra ? Tomcat w zastosowaniu produkcyjnym Nauka - w jakim środowisku strona grupy is_int() nie działa Ruch na grupie.... FPC i wstawki asmowe Panie Lodku, Panowie ... java 5-10 razy wolniejsza od php Upt ?
Zbieranina wiadomości z for dyskusyjnych ; Indeks Linki,
|
|