piątek, 27 marca 2009

C++ i zasady: Potłuc o kant

Znalazłem takiego wpisa, którego autorem jest niejaki Łukasz Milewski. Znałem kogoś o tym nazwisku, pracował ze mną w poprzedniej firmie, choć mam wątpliwości, czy to ta sama osoba, bo tam się pisało wyłącznie w Javie. A ten widać jest specjalistą od C++. Trochę oczywiście nie do końca specjalistą.

Przeczytałem sobie te zasady i w najlepszym razie zaliczyłbym je do "intermediate C++", choć od pewnego czasu zacząłem snuć teorię "lewicowych poglądów w programowaniu". Wbrew pozorom bowiem, lewicowość nie jest rzeczą zarezerwowaną dla polityki, czy obyczajowości - w programowaniu istnieje również, a jakże.

Przykładowym "lewicowym" poglądem w programowaniu w C++ jest wymaganie, aby wszystkie metody klasy były wirtualne. Ale nie uprzedzajmy faktów... :)


Destruktory zawsze są wirtualne - niezależnie od przeznaczenia klasy. Co z tego, że po klasie nie będzie można dziedziczyć? Musisz się niepotrzebnie zastanawiać nad tym czy może kiedyś semantyka ulegnie zmianie i będzie trzeba dodać virtual. Dodanie virtual nic nie kosztuje i niczego nie psuje


Ależ oczywiście, że kosztuje, i to wbrew pozorom wcale nie mało.

Dodanie co najmniej jednej metody wirtualnej (również destruktora) powoduje konieczność utworzenia obiektu charakterystycznego klasy. Oczywiście jeśli destruktor jest trywialny, to obiekt charakterystyczny będzie wygenerowany raz na hierarchię (kompilatorowi wolno współdzielić większe części obiektów charakterystycznych spokrewnionych klas i dzieje się tak np. gdy klasa pochodna nie przesłoni żadnej metody wirtualnej z klasy bazowej) - ale to nie zmienia faktu. A jeśli robimy wirtualne destruktory do wszystkich klas, to zwykle też do małych struktur, nie przeznaczonych do dziedziczenia. Tu więc nie będzie możliwości współdzielenia obiektów charakterystycznych.

Prawda, oczywiście, że destruktor wirtualny ma znaczenie tylko wtedy, gdy używamy "delete". Wtedy bowiem istnieje kwestia tego, czy należy prowadzić wywołanie wirtualne, czy też nie. Obiekt bowiem można usunąć na dwa sposoby:


  1. zostaje usunięty jego obiekt nadrzędny
  2. na wskaźniku do niego zostanie wywołane delete


Nie ma innych sposobów. Inne sposoby są co najwyżej pochodną powyższych (np. dla zmiennej lokalnej obiektem nadrzędnym jest środowisko funkcji). Jeśli więc mówimy o klasie (strukturze), na której wskaźniku nigdy nie ma być wołane delete, to wirtualność destruktora nic nie znaczy.

Byłoby więc znacznie bardziej z sensem, gdyby zastrzeżenie było takie, że należy zrobić zawsze jedną z dwóch rzeczy:

  • destruktor musi być wirtualny oraz nierzucający (throw())
  • klasa ma sprywatyzowany operator delete


Przecież to takie proste. Deklaracja operatora delete podlega dziedziczeniu, więc nie będzie można usunąć delete'm także żadnej jego pochodnej. Po prostu kaplica. Jest to więc w praktyce, można powiedzieć, częściowy zakaz dziedziczenia: nie będzie można usunąć obiektu tego typu. Oczywiście to nie znaczy, że nie będzie go można utworzyć (choć oczywiście można również podobnie zablokować operator new).

W praktyce jednak w zupełności wystarczy jedna rzecz: wymóg, aby destruktor był wirtualny w każdej klasie, która posiada metody wirtualne. Większość kompilatorów oczywiście stosuje do tego odpowiednie ostrzeżenie. I jest to naprawdę w zupełności wystarczające rozwiązanie problemu: niewirtualny destruktor w klasie posiadającej metody wirtualne jest czymś mającym mało sensu (optymalizacja???), natomiast jeśli nie mamy metod wirtualnych - nie będzie to dziedziczenie w sensie obiektowym, a jedynie dziedziczenie w sensie C++ (tzn. przejęcie w klasie pochodnej definicji składowych z klasy bazowej).

Na szczęście faktycznie nic nie kosztuje wywołania destruktora w razie usunięcia jego obiektu nadrzędnego. Tam również w ogóle jakiekolwiek wywołanie zostanie pominięte, jeśli destruktor jest trywialny. Ale nie zmienia to faktu, że pozostaje koszt obiektu charakterystycznego, a także koszt obciążenia wprowadzanego do klas, które z tej klasy dziedziczą.

Przypominam, że C++ zawsze był tworzony zgodnie z zasadą "nie płać za to, czego nie używasz". Gdyby więc rzeczywiście jawne zadeklarowanie destruktora wirtualnego nic nie kosztowało, to w ogóle w języku nie istniałoby pojęcie wirtualnego destruktora - kompilator sam by pamiętał o ustawieniu destruktora na wirtualny w razie potrzeby (albo nawet w ogóle zawsze robił wszystkie destruktory wirtualne i tylko pomijałby wywołanie wirtualne, gdyby nie było takiej potrzeby). Na to się nie zdecydowano, prawdopodobnie dlatego, że kompilator mógłby nie umieć tego stwierdzić.

Niestety to założenie nie dotyczy wywoływania metod - w ogóle (tzn. nie ma takiej możliwości, żeby wywołanie metody wirtualnej uprościć do zwykłego wywołania lub inline). Przecież nigdy nie wiadomo, czy z danej klasy ktoś gdzieś nie odziedziczył. A tu proszę:


Wszystkie metody powinny być zawsze wirtualne. Jeżeli nie musisz pamiętać o virtual to bardzo dobrze... masz czas na inne rzeczy. (UPDATE: wszystkie w sensie, te od implementacji. Metody interfejsu raczej nie powinny być wirtualne).


Widzę, że facet czegoś tu nie rozumie.

Po pierwsze, jeśli jakakolwiek metoda jest wirtualna, to zostanie ona wywołana poprzez wywołanie wirtualne (czyli: z obiektu wyciągamy obiekt charakterystyczny, z niego z kolei wskaźnik na funkcję będącą implementacją metody i dopiero to jest wołane - oczywiście o jakimkolwiek inline zapomnij). To jest co najmniej dwukrotny koszt samego wywołania.

Po drugie, jeśli już chcemy tak dzielić na metody interfejsu i metody implementacji, to miejmy na uwadze, że w C++ istnieją następujące elementy w kwestii metod klasy:


  1. definicja implementacji. Jest to funkcja, która stanowi zestaw instrukcji, jaki zostanie wykonany w ramach wywołania funkcji
  2. definicja slotu. Jest to deklaracja, którą będzie można wypełnić swoją implementacją funkcji
  3. definicja interfejsu. Jest to deklaracja, która pozwala na wywołanie (w celu wykonania określonych czynności)


Możemy sobie te definicje odpowiednio upraszczać. Przykładowo, pojedyncza funkcja składa się wyłącznie z interfejsu i implementacji. Tak samo się dzieje w przypadku, gdy mamy zwykłą metodę klasy. W obu przypadkach zaś mamy do czynienia z podpięciem implementacji bezpośrednio pod interfejs. Fakt, że i w C++ i w innych językach obiektowych nie dostarczono oddzielnych definicji do implementacji i slotu, to oczywiście pewne ułatwienie dla użytkownika, ale zarazem powoduje, że wielu ludzi nie nie rozróżnia tych pojęć.

Metoda wirtualna składa się bowiem z trzech części: interfejsu, podpiętego do niego slotu, a dopiero do slotu podpiętej implementacji. To mniej więcej tak, jak byśmy w definicji metody zrobili tylko przekierowanie wywołania do slotu, oraz dostarczyli informację o tym, co jest wpięte do slotu w bieżącej klasie (chyba że to jest metoda abstrakcyjna, to wtedy do slotu nic nie jest wpięte).

Jeśli więc wprowadzamy sobie taki slot, to znaczy, że przewidujemy tutaj możliwość wpięcia sobie później przez kogoś innego w to miejsce własnej implementacji.

Więc nie ma czegoś takiego jak "metody interfejsu" i "metody implementacji". Interfejsem jest tutaj to, za pomocą czego można funkcję wołać, a implementacją jest to, co ma się dziać w przypadku wywołania. Tyle. Oczywiście, że istnienie publicznej metody wirtualnej powoduje równocześnie, że taka metoda staje się równocześnie trzema rzeczami (a przynajmniej dwoma, gdyby była abstrakcyjna): interfejsem wystawionym do wołania oraz slotem do podpięcia dowolnej implementacji.

Wiem, istnieje taka szkoła, żeby wszystkie metody wirtualne robić jako protected; jest to nawet niezła szkoła, aczkolwiek nie zawsze istnieje konieczność trzymania się tego wymagania. Oczywiście jest to o tyle dobra szkoła, że oddziela dwie rzeczy, które nie mają nic ze sobą wspólnego: interfejs do wywoływania oraz slot do podpinania. Różne elementy kodu mogą bowiem używać tego czegoś w różny sposób: jedni do podpinania, inni do wywoływania. Ustawienie metody wirtualnej jako protected i dorobienie do niej metody publicznej, która wywołuje ową metodę wirtualną, pozwala rozdzielić te dwa sposoby użycia tej metody, które tak naprawdę nie mają ze sobą nic wspólnego. W ten sposób twórca klasy bazowej ma możliwość wprowadzania np. dodatkowej kontroli do wywołania z poziomu interfejsu, nie ingerując w to, co ktoś (może w poprzedniej wersji) zrobił w klasie pochodnej.

Ale nie o tym była mowa. Ale w świetle tego wychodzi to coraz bardziej bez sensu. Jeśli bowiem stwierdzimy, że ok, dotyczy to tylko metod protected, to już z góry zakładamy, że będziemy robić metody protected z interfejsem zewnętrznym, który będzie tych metod używał. A przecież jeśli ktoś w ogóle nie robi metod klasy jako wirtualne, to tym bardziej nie będzie robił takiego sztucznego podziału!

Pamiętajmy też o dwóch szczegółach: po pierwsze, w C++ nie ma final, a po drugie, C++ zwykle nie jest kompilowany na maszynę wirtualną działającą jako JIT (jeśli już: rozwiązanie to ma sens tylko w aplikacjach uruchamianych na serwerze aplikacji, a do tego C++ jest rzadko stosowany). W pierwszym przypadku powoduje to, że każde wywołanie wirtualne będzie wywołaniem pośrednim (bo nigdy nie można założyć, czy ktoś czasem nie podał obiektu klasy pochodnej i nie przesłonił podanej metody), w drugim, że żadne wywołanie pośrednie nie będzie mogło być uproszczone.

To ostatnie zresztą ukazuje w pełni swoje wady, gdy dojdziemy do uruchamiania programu. Zbyt duża ilość metod wirtualnych w ogóle w aplikacji potwornie wydłuża czas uruchamiania programu (były w KDE prowadzone badania na ten temat). Spowodowane jest to koniecznością renumeracji adresów funkcji. Nie ma to żadnego związku z ilością funkcji, natomiast ma ścisły związek z ilością wskaźników do funkcji zapisanych na twardo w danych programu po kompilacji. Przykładowo, jeśli wyprowadzamy nową klasę B z klasy A, która ma 10 metod wirtualnych, przesłaniamy w klasie B tylko jedną metodę wirtualną. Ponieważ przesłaniamy co najmniej jedną metodę, to kompilator musi stworzyć dla klasy B oddzielny obiekt charakterystyczny, w którym 9 wskaźników do implementajci metod wirtualnych jest identyczne z tym z klasy A. To już powoduje powielenie istniejących 9 wskaźników dwukrotnie. Co się stanie, gdy takich klas, jak B, będzie więcej?

W Javie tego problemu nie ma dlatego, że zwykle kompilatory do tego języka wykonują kompilację JIT (oczywiście to powoduje równocześnie, że Java nie nadaje się do zastosowań innych, niż serwery aplikacji). To oznacza, że pewne części faktycznej kompilacji wykonują się po uruchomieniu, a to z kolei oznacza, że taka czynność może zostać wykonana przy znajomości całej hierarchii WSZYSTKICH klas, jakie wchodzą w skład uruchomionego procesu, a co za tym idzie, JIT wie na pewno, czy wywołanie danej metody na rzecz danej referencji jest wywołaniem efektywnie wirtualnym, czy nie. No i oczywiście istnieje też final, którego użycie wprost deklaruje, że nie może to wywołanie być wirtualne.

Pamiętajmy też, że C++ to jest język "wieloparadygmatowy", a nie "obiektowy". W C++ złożone struktury można programować na różne sposoby i przykładanie zbyt wielkiej wagi akurat do programowania obiektowego w C++ ma mało sensu.

Jeśli więc ktoś robi metodę wirtualną, to przewiduje podmianę implementacji - jeśli ktoś nie przewiduje podmiany implementacji w tym miejscu, to po co ma robić metodę wirtualną? Jest to kwestia designu - możesz chcieć, aby była, możesz nie chcieć. A że moja klasa będzie mniej użyteczna przez to, że dana metoda jest niewirtualna?

Momencik. Przecież jeśli ja piszę klasę i jej metody, to ja decyduję o tym, jak wygląda jej call flow. Dlaczego mam pozwalać każdemu kto popadnie na to, żeby się, za przeproszeniem, wpierdalał w mój kod? Jak mam zapewnić poprawność mojego kodu i zagwarantować właściwe zachowanie się programów używających mojej klasy, jeśli ktoś będzie się chciał wciąć w każde możliwe wywołanie w mojej klasie i zrobić mi burdel?

Przypominam również, że np. w Javie każda metoda jest wirtualna, ALE:

  • Istnieje final, które zabrania przesłaniania metody. Taka metoda zachowuje się jak zwykła metoda, jeśli jest wołana z klasy, w której jest dodane final (jeśli dodano to klasy, to wszystkie metody)
  • W Javie "private" implikuje "final".


Mówiąc bardziej ściśle: w C++ nie ma możliwości ani zakazania dziedziczenia, ani zakazania przesłonięcia metody. Java zatem pozwala, mając wszystkie metody wirtualne, na to, żeby wciąż przesłanianie metod było ograniczone tylko do wybranego zakresu (inna sprawa, czy w praktyce ktoś z tego w ogóle korzysta, ale pomińmy ten temat). Wymóg robienia wszystkich metod wirtualnymi w przypadku C++ odpowiada więc w Javie nieużywaniu final (a tym samym oczywiście private). Możemy to również porównać do polimorfizmu parametrycznego i wtedy dojdziemy do prawdziwego kuriozum: niech wszystkie argumenty funkcji i pola klas będą typami-parametrami wzorca...

Mnie, jako twórcy klasy, naprawdę szczerze wisi i powiewa, czy moja klasa będzie użyteczna. Ważne, żeby była używana w takim zakresie, w jakim ją przewidziałem do używania. Wymaganie, abym pisał klasę tak, żeby ktoś mógł sobie później kombinować, jak jej używać, jest po prostu nieprzydatne. O tym, że potem za to się płaci, już nie wspominam.


Nie używaj wyrażenia warunkowego (to jest to ? : ). Jeżeli będziesz musiał kiedyś dodać zagnieżdżony warunek, to gdy będziesz potem czytał takie zagnieżdżone instrukcje warunkowe prawdopodobnie stracisz dużo czasu.


Ależ oczywiście, że używaj. Nie mówiąc o tym, że jest to jedyna możliwość warunkowego zainicjalizowania zmiennej referencyjnej. Poza tym, nie zapominajmy w ogóle, że jest to wyrażenie - a jako takie potrafi sporo rzeczy, w odróżnieniu od instrukcji.

No, chyba że ktoś woli coś takiego:


template<class Value>
const Value& if_then_else( bool b, const Value& ifso, const Value& ifnot )
{ return b ? ifso : ifnot; }



Uważaj na niekompletne typu (tylko zapowiedziane). Niektórzy chcą być sprytni i zamiast include dają zapowiedź klasy. To może prowadzić do problemów, więc ja na to uważam. Typowym problemem jest zwolnienie pamięci po obiekcie wskazywanym przez wskaźnik niekompletnego typu. Jeżeli destruktor tego typu jest nietrywialny to nie zostaje wywołany. Tak na prawdę nie musisz nawet wiedzieć o tej sytuacji, jeżeli zawsze dostarczasz kompilatorowi wszystkich informacji jakich potrzebuje - w szczególności jeżeli nie oszukujesz go przez zapowiedź zamiast include.


I kolejna rzecz - gość w ogóle nie ma pojęcia, po co istnieją typy niekompletne. A właśnie dobra znajomość zagadnienia typów niekompletnych pozwala umiejętnie rozwikłać wzajemne zależności między dwoma modułami, jak również mocno oszczędzić sobie kompilacji. Co więcej, uważam że spartolili sprawę w bibliotece standardowej C++ poprzez niedodanie nagłówka <stringfwd>. Problem polega na tym, że "zapowiedzianej" wersji nie da się zrobić, bo string to jest typedef na basic_string<char>, więc trzeba zrobić zapowiedź basic_string i tylko tam można podać parametry domyślne. A przydałby się taki nagłówek choćby do tego, żeby fstream::open mogło przyjmować string. Przyjmowałoby się go przez const referencję, więc typ niekompletny by się nadawał.

Typy niekompletne jest to narzędzie do ucinania niepotrzebnych zależności między modułami, a tym samym oszczędzenie mnóstwa czasu kompilacji, pomijając już przypadki, gdy jest to konieczne. Fakt, że delete na tym działa i to działa źle uważam za defekt C++ i być może zostanie on usunięty - ale póki co ostrzeżenia kompilatora spełniają swoją rolę. Jedyne co poza tym robi to różnicę, to fakt, że nie da się użyć żadnego szczegółu typu, który jest niekompletny, ale przecież próba wykonania takiej operacji kończy się błędem kompilacji.

Nie jest wcale oczywiste, jak ten błąd kompilacji należy poprawić. Może należy zaciągnąć pełną definicję typu. A może raczej należy przenieść użycie tego typu do innego pliku, a w tym wstawić tylko typ niekompletny.

Jedno jest pewne - ci, którzy używają typów niekompletnych, to nie ci, którzy chcą być sprytni, tylko ci, którzy pewnie nieraz spędzili długie godziny na rozplątywaniu zależności.


Jedna klasa - dwa pliki. Zawsze rób dwa pliki - z interfejsem i z implementacją. Nawet jeżeli używasz szablonów. Wyjątkiem mogą być malutkie klasy, o których wiadomo, że nie urosną. Ludzie mają to do siebie, że łatwiej im analizować mniejsze kawałki (dlatego nie jest zalecane uczenie się poprzez wyłożenie mnóstwa książek na biurko - mózg boli na sam widok tej sterty).


Nie rozumiem tego zalecenia.

Ja nie tylko nie zawsze robię dwa pliki, ale nawet czasem na cztery klasy robię dwa pliki nagłówkowe i trzy pliki z implementacją, bądź trzy pliki nagłówkowe i jeden plik z implementacją. Niekiedy zdarza mi się pisać pełną definicję metody w klasie, najczęściej jednak nie widzę w tym sensu. Po prostu implementacje metod umieszczam w taki sposób, żeby łączyło je wspólne przeznaczenie. Jest rzeczą całkowicie normalną, że z dwóch różnych klas można wyróżnić trzy grupy metod i to całkowicie w poprzek klas.

A w przypadku szablonów to jest po prostu kuriozum. Przecież i tak wszystkie definicje w przypadku szablonów muszą być w pliku nagłówkowym. Ciekawe, że to tych "wspaniałych zaleceń" jakoś nie stosują się twórcy biblioteki standardowej C++.


Nie używaj delete. Dokładnie tak. Zamiast tego lepiej zastosować inteligentne wskaźniki (np. boost::shared_ptr). Po pierwsze nie trzeba pamiętać o zwolnieniu pamięci - ale trzeba określić kiedy powinna być zwolniona, dla własnej wiadomości. Po drugie delete ma w C++ dwie formy delete i delete [], o czym niestety wszyscy zapominają. Po trzecie delete nie sprawdza czy typ jest kompletnym. Po czwarte przy użyciu delete trzeba się niepotrzebnie zastanawiać czy ktoś nie potrzebuje jeszcze danego zasobu - kolejna możliwość pomyłki i okazja do zmarnowania odrobiny czasu.


No nie do końca. Pomijam już oczywiste pytanie "co w przypadku, gdy sam robię dla siebie jakiś specyficzny smart-pointer". Po drugie, nie wiem czy autor wie, że smart-pointery typu właśnie shared_ptr (dziś już std::tr1::shared_ptr) dają bardzo duży overhead na operacje zmiany zawartości wskaźnika. Pamiętajmy, że shared_ptr to jest wskaźnik "z przypinką" i jego implementacja jest dość złożona (znacznie prościej jest zrobić intruzyjny smart-pointer, ale intruzyjność oznacza, że trzeba odpowiednio zmodyfikować definicję klasy, która ma być trzymana przez taki smart-pointer, czyli inaczej, nie każdej klasy obiekt taki smart-pointer może trzymać). A ja, tak się składa, że robiłem pomiary czasowe programu z użyciem shared_ptr i obciążenie na jego przypisanie było o rząd wielkości większe, niż ta sama seria wykonana ręcznie z surowym wskaźnikiem. Pod względem wydajności bije go na głowę nawet odśmiecacz Boehma.

I znów ten typ niekompletny. Powtarzam: wystarczy sam fakt, że kompilator ostrzega.

I jasne, trzeba się zastanawiać, czy ktoś nie używa obiektu. Ciekawe. A w przypadku shared_ptr to już nie trzeba. Problem właśnie polega na tym, że jeśli wprowadzamy współdzielenie dostępu do obiektu, to jeszcze najczęściej dochodzą do tego kwestie związane z możliwością "podglądania" obiektu (czyli słabymi wskaźnikami). Bo sam fakt, że istnieje współdzielenie jeszcze nic nie znaczy; nadal istnieje odpowiedzialność za zniszczenie obiektu. Techniki reference-count są znane już od dawna przecież i w wielu miejscach stosowane - a i tak jakoś tak się dziwnie składa, że co rusz gdzieś trzeba sztucznie podkręcać licznik referencji, bo coś-tam się nie zgadza. Współdzielenie dostępu do obiektu nie jest niestety najczęstszym przypadkiem sposobu operowania obiektem; najczęściej tak naprawdę właściciel jest jeden, to właśnie współdzielenie dostępu do obiektu jest przypadkiem szczególnym.

Współdzielenie możliwości usunięcia obiektu to nie jest jedyny aspekt współdzielonego obiektu. Równie istotną sprawą jest możliwość modyfikacji obiektu i synchronizacja powodowanych tych zmian. Możliwość domyślnego współdzielenia właścicielstwa do obiektu (co oferują nam języki z gc) zwalnia nas zatem z konieczności ustalania nadzorcy obiektu, spychając jedynie jednak problem w inne miejsce.


Dokładnie poznaj wybrane inteligentne wskaźniki - często mają wady, które wynikają z samej konstrukcji. Typowym błędem są cykle utworzone przez takie wskaźniki. Wówczas jest wyciek pamięci. Dlatego ten element biblioteki zawsze trzeba dobrze poznać.


Ha! A więc: używaj zawsze shared_ptr... ale spodziewaj się problemów. :)

To ja mam lepszą radę: nie używaj shared_ptr :) A tak na poważnie: staraj się w miarę możliwości nigdy nie współdzielić obiektów. Jeśli już jednak istnieje taka potrzeba, nadal obowiązkowo utrzymuj hierarchię obiektów. Jeśli np. dwa obiekty muszą współdzielić inny, to niech to będą dwa obiekty, które nie wymieniają się żadnymi innymi danymi. Jeśli te dwa obiekty mają współdzielić więcej niż jeden obiekt, to niech zarządzaniem tymi obiektami zajmie się oddzielny obiekt. Również obiekty uczestniczące we współdzieleniu nie mogą same być współdzielone.


Nie używaj std::auto_ptr. Jeżeli poznałeś dobrze auto_ptr to wiesz dlaczego ;-P Ich polityka własności powoduje, że nietrudno o błąd. W szczególności można kopiować obiekty typu auto_ptr, ale wówczas oryginał traci ważność(!) - jest zerowany. Dodam tylko, że algorytmy STL-a używają kopiowania dość sporo...


LOL :)

Oczywiście, w C++0x należy już zrezygnować z auto_ptr na rzecz scoped_ptr, ale to nie znaczy, że auto_ptr jest bezużyteczny. Co więcej, na kompilatorze, który nie jest jeszcze zgodny (wystarczająco) z C++0x jedyny dostępny z takim fajnym ficzerem. Oczywiście, że posiada on wadę polegającą na niemożności współdzielenia, ale ta wada wychodzi już na etapie kompilacji, ponieważ obiekty auto_ptr nie spełniają koncepcji "kopiowalny" (wymaganej przez większość elementów STL). Wskaźnik auto_ptr jest za to wyśmienity do:

  • przytrzymania obiektów dynamicznych na czas, aż wszystkie operacje, które mogą potencjalnie rzucić wyjątkiem, zostaną zakończone
  • zwracania nowo uworzonych obiektów jako rezultat wywołania (zapobiegają niebezpieczeństwu wycieku pamięci w przypadku zignorowania wartości zwracanej)



Unikaj dziedziczenia wirtualnego. Gdy jest to możliwe, bo pominięcie virtual w tym przypadku może być czasami groźne w skutkach. Chodzi o to, że hierarchia dziedziczenia nie powinna być grafem (który nie jest jednocześnie drzewem). Jeżeli już jest to trzeba bardzo uważać na operator= definiowany automatycznie. Polecam spróbować jak to działa, to zobaczysz dlaczego lepiej tak nie robić.


Super :) Polecam do przeczytania następujący artykuł (mój oczywiście, obcych nigdy nie polecam :D): Multiple inheritance considered nice, w którym objaśniam dokładnie, kiedy dziedziczenie w C++ jest dziedziczeniem z punktu widzenia obiektowego. Jak się okazuje, dziedziczenie wirtualne ma tutaj spory udział, co więcej, właśnie dziedziczenie wielorakie, w którym jest ta sama klasa wprowadzona pośrednio przez dwie oddzielne klasy, podane równocześnie do dziedziczenia, to jest właśnie dziedziczenie niedozwolone z obiektowego punktu widzenia.

Mam więc znacznie prostszą poradę i to w sam raz w stylu Łukasza: unikaj dziedziczenia wielorakiego. Szczególnie, gdy nie rozumiesz, o co chodzi.

A tu trafia nam się ciekawostka dwóch kolejnych:


Asercje są dobre. Warto używać asercji. Pozwalają sprawdzić czy wywołanie metody faktycznie spełnia zadane warunki wstępne (ang. preconditions). Niejednokrotnie dzięki temu mechanizmowi wykrywałem błędy, które przeszłyby testy modułowe. Typową sztuczką jest assert(warunek && "Komunikat błędu") aby dostać trochę sensowniejszy komunikat niż tylko numer linii i nazwę pliku z błędem. Niestety nie można odpowiedniego tekstu tworzyć dynamicznie.


Słusznie.


Asercje są złe. Niestety asercje z C++ są złe. W przypadku błędnego warunku przerywają program. Wyobraź sobie, że grasz w grę komputerową i nagle aplikacja zostaje przerwana z komunikatem "assertion failed". Dlatego warto stworzyć własną funkcję asercji, która będzie rzucała wyjątek - przynajmniej można go potem złapać. Dodatkowo można dać dokładniejsze informacje o błędzie (np. że index = 5 a powinno być <4


No bo na tym właśnie polega rola asercji, żeby przerywać program i nie dopuścić do jego dalszego wykonywania się!

Asercje i wyjątki. Jasne. To chyba trzeba się w Javie wykąpać.

Asercja jest to sprawdzenie KRYTYCZNEGO warunku wstępnego funkcji. Krytycznego, to znaczy takiego, że jeśli taki warunek nie jest spełniony, to dalsze wykonywanie się programu nie powinno być możliwe. Dlatego asercjami nie można sobie szastać. Asercje służą właśnie do wysypania programu, bo mają oznaczać błąd, z którego nie można się już "odzyskać".

Jeśli ktoś mówi o tym, że miałbym grać w grę i nagle dostać komunikat "assertion failed", to chyba nie rozumie, że jeśli stosuje się asercje, to stosuje się również dwie oddzielne konfiguracje kodu, zwane "debug" i "release". I asercje tylko w trybie "debug" powodują wysyp programu, bo uważa się to wtedy za niegroźne, podczas gdy w trybie release sprawdzanie tego warunku jest ignorowane. Jeśli uważamy, że pozostawienie tego warunku jest zbyt ryzykowne również w trybie release, to w trybie release powinniśmy wykonać ODDZIELNE sprawdzenie i tym razem możemy się ewentualnie posłużyć wyjątkiem (oraz zaplanować na tę okoliczność sensowne recovery). W przypadku gry, dajmy na to, skoro już podano ten przykład, należy spróbować zebrać wzystkie możliwe informacje o bieżącym stanie, gdzieś go zrzucić, utworzyć środowisko od nowa, nałożyć zmiany i pozwolić kontynuować grę - użytkownik najwyżej zauważy spowolnienie, ale mimo wszystko przynajmniej może dalej grać.

A teraz czas na doświadczenia na żywo:


Innym razem nie było tak wesoło. Zostałem zapytany o slicing. Nie bezpośrednio lecz przez przykład w C++. W skrócie była klasa A i jej pochodna B. Była zdefiniowana funkcja foo z argumentem typu A. Ta funkcja w main była wywołana z argumentem typu B. Pytanie brzmiało co się stanie? Zastanów się...


Odpowiedź jest oczywiście banalnie prosta - przekazanie przez wartość obiektu klasy pochodnej B spowoduje utworzenie obiektu tymczasowego klasy A w celu przekazania go dalej przez wartość. Oczywiście, jeśli przekażemy tam obiekt klasy A, to do funkcji zostanie przekazany również obiekt tymczasowy. O ile ja sobie przypominam, jest to również konstrukcja, do której kompilator powinien rzucać ostrzeżenie. Zresztą nie jest to jedyny taki przypadek - dokładnie ta sama sytuacja istnieje w przypadku typów ze sobą nie powiązanych, mających jednak zdefiniowaną konwersję (albo domyślną, albo zdefiniowaną przez operator konwersji, albo przez jednoargumentowy konstruktor nie-explicit).

I do tego celu potrzeba było aż eksperymentów, żeby to stwierdzić? Ciekawe.

C++ nie jest pod tym względem wcale szczególny - tak jest w każdym języku, który posiada konwersje między typami wartości. Problem w tym szczególnym przypadku nie polegał na tym, że przekazano obiekt klasy pochodnej (bo skąd twórca klasy A mógł wiedzieć, że ktoś zrobi klasę B?). Problem polegał na przekazaniu klasy A przez wartość. A, jak to już gdzieś wspominałem, typy wartościowe nie mogą podlegać hierarchiom obiektowym. Oczywiście klasę, jeśli ma być typem wartościowym, najlepiej zabezpieczyć w podany sposób (czyli dając explicit do konstruktora kopiującego). Ważne jest jednak, żeby wiedzieć nie tylko to, że tak o po prostu przypadkiem nadzialiśmy się na "beznadziejne" mechanizmy w C++. Ważne jest, aby wiedzieć o podziale na typy wartościowe i obiektowe, że ten podział naprawdę istnieje, i że istnieją reguły używania jednych i drugich - opisałem to w innym artykule pt. Valuables and objectives.

I odpowiem od razu - oczywiście, operator = ma również tą samą wadę, a co gorsza, operatora= nie można zrobić explicit. Ale jest na to rada: należy zadeklarować alternatywny operator=, z typem template, który wywoła błąd. W C++0x jest nawet dodatkowe ułatwienie, przez co nie trzeba sztucznie wywoływać błędu:


A& operator=( const A& a ) = default;

template <class X>
A& operator=( const X& x ) = delete;



Ja, w odróżnieniu od Łukasza, z kolei, mogę dać gwarancję na poprawność mojego rozumowania. Zasada jest zresztą prosta: każdy konstruktor jednoargumentowy bez explicit jest równocześnie konstruktorem konwertującym, który sam z siebie ustanawia definicję konwersji z każdym, kto pasuje do typu argumentu (w przypadku konstruktora kopiującego - również domyślnego - oznacza to, że ustanawia ją z każdym obiektem pochodnym, ponieważ przyjmuje referencję do niego jako typ argumentu, a referencje i wskaźniki do klas pochodnych domyślnie konwertują się na te od klasy bazowej). Konstruktor konwertujący jest to jedna z możliwych zdefiniowanych konwersji; poza tym istnieją jeszcze konwersje "wbudowane" oraz konwersje definiowane operatorem konwersji (czyli operator X() zdefiniowany w klasie Y pozwala konwertować obiekt klasy Y na obiekt klasy X). Mógłbym nawet podać jakieś referencje w standardzie, ale nie chce mi się szukać :D.

Przestrzegam ogólnie przed takimi zasadami. To, co zacytowałem tu powyżej to trzymanie się sztywnego schematu podyktowanego faktem, że człowiek się na tym czy tamtym potknął. Tymczasem prawda może być zupełnie inna; potknięcie wynika niekoniecznie stąd, że zastosowane konstrukcję niezgodnie z przeznaczeniem. Wynika stąd, że nie do końca rozumie się określony mechanizm i nie wie się, że można go w określony sposób wykorzystać. Akurat C++ jest takim językiem, że bardzo wiele z jego właściwości można używać nie tylko niezgodnie z przeznaczeniem, ale nawet w sposób, o jakim się jego twórcom nie śniło. Jest to dla mnie zresztą dość ciekawa cecha wspólna z całkowicie odmiennym od niego językiem Tcl i chyba najważniejszy powód, dla którego lubię używać obu tych języków.

To nie znaczy, że C++ ma być językiem bez zasad. To znaczy, że aby używać określonych ficzerów z C++ należy mieć do tego zawsze jakąś konkretną logiczną "podkładkę". Taka podkładka wystarczy, aby zasady, podobne do powyższych, można było właśnie potłuc o kant.

wtorek, 24 marca 2009

Yellow Pages: Korea Północna a sprawa Amerykańska

Komentarz Wprost:


Zatrzymanie amerykańskich dziennikarek to manifestacja siły ze strony Korei Północnej. Ciężko bowiem inaczej interpretować sytuację, kiedy strażnicy graniczni z jednego państwa aresztują na terenie sąsiedniego państwa przedstawicieli mediów.


[UWAGA: W tekście stosuję "własną" translację koreańskiego pisma na litery łacińskie: hangug [han guk] (한국), zwson [dzooson] (조선); reguły niedługo gdzieś przedstawię :)]

Ha! Ale komentator nie zauważył jeszcze jednego szczegółu. W jaki sposób żołnierze z Zwsonu weszli sobie swobodnie na teren Chin i kogoś aresztowali? Przecież to oczywiste - ktoś im musiał na to, nawet po cichu, pozwolić.

Czy świat naprawdę nie widzi, że "Korea Północna" jest po cichu wspierana przez Chiny? Przecież gdyby nie wsparcie finansowe z Chin, ten śmieszno-straszny reżim już dawno rozleciałby się w kibinimatri (z dokładnie takich zresztą powodów rozleciał się komunizm w Polsce i ZSRR). Proszę sobie przejrzeć dostępne na sieci (choćby Wikipedii) materiały nt. Zwsonu i dojdziemy do wniosku, że taki reżim w normalnych warunkach powinien się rozlecieć sam z siebie.

Jeśli więc się nie rozleciał, to istnieją tylko dwie możliwości: albo musi mieć przeogromne ilości zasobów mineralnych, albo musi skądś mieć sporą kasę. KRLD niby zasoby mineralne ma, ale ma ich w porównaniu z resztą świata (choćby samymi Chinami) niewiele.

Przecież ten skansen jest z jakiegoś powodu utrzymywany. Z jakiegoś powodu jest to komuś na rękę. Przecież gdyby Chinom nie było to na rękę, to "jednym piernięciem" przywłaszczyliby sobie całość (mieliby za to zresztą dozgonną wdzięczność Amerykanów). Dlaczego Hangug (Korea Południowa) nie próbuje odzyskać terenu, do którego oficjalnie rości sobie prawa? I zaznaczam, że mówię o państwie, które utrzymuje jedną z największych armii świata (porównywalną z Izraelem i US). Wiemy chyba dobrze, że ze zniknięcia reżimu Zwsonu bardzo by się ucieszyły i Stany i Hangug. Mimo to, nie podejmą zbrojnej interwencji. Dlaczego?

Sporo ciekawych rzeczy w tym temacie można się dowiedzieć z Wikipedii. Na przykład:


In a 2003 event dubbed the "Pong Su incident", a North Korean cargo ship allegedly attempting to smuggle heroin into Australia was seized by Australian officials, strengthening Australian and United States' suspicions that Pyongyang engages in international drug smuggling. The North Korean government denied any involvement.


Może to i drobny incydent. A może jednak wierzchołek góry lodowej.

Strażnicy z Zwsonu, którzy aresztowali dziennikarki, nie dostali żadnego "pozwolenia" na tą operację. Oni dostali rozkaz, to chyba oczywiste. Ale moim zdaniem równie oczywista jest odpowiedź, na czyj rozkaz zrobił to Kim.

Za pomocą tej operacji Chinom udało się postawić Amerykanów w głupiej sytuacji. Po pierwsze, Amerykanie, i tak już uwikłani w dwie wojny, muszą zacząć trzecią - alternatywą jest być chłopcem do bicia. Po drugie, oficjalnie Chiny niby nie mają z tym nic wspólnego (terefere), więc interwencja w Chinach i tak by nic nie dała. Po trzecie, oficjalnie to w ogóle reżim z Pionianu jest ten "zły", a Chiny to "ten dobry", co robi ze Stanami interesy.

Cokolwiek Amerykanie by podjęli, trzeba pamiętać, że stoją za tym Chiny, i cała ta interwencja może skończyć się wojną Stanów z Chinami, w której może i Hangug wesprze Amerykanów, ale pewnie Rosja jeszcze wesprze Chiny. Byłoby trochę szkoda zaczynać to tak wcześnie - lepiej by było poczekać, aż Rosja się całkiem rozleci, a Chińczycy przeniosą się na "wymarłe" tereny z Rosji. Wszystko zależy od tego, czy Chinom będzie się opłacało uruchamiać całą machinę teraz, czy trzeba będzie trochę poczekać. Bo że uruchomią prędzej, czy później, to pewne.

sobota, 7 marca 2009

Yellow Pages III: Zamieszanie wokół eutanazji

Cytatów nie będzie. Może uzupełnię później.

W mediach znów rozpętała się burza w sprawie, którą określa się jako sprawę "eutanazji". Jest to oczywiście jeden z przekrętów medialnych i nie największy wcale z dowodów na to, jak bardzo prasa się wysługuje ideologom.

Póki co, zapamiętałem dwie sprawy: jedna we Włoszech, gdzie w końcu odłączono od aparatury osobę w stanie wegetacyjnym, a ostatnio i jedna w Polsce. W obu tych sprawach odbyło się starcie dwóch obozów: "Atanastów", którzy chcieliby każdemu człowiekowi przedłużać życie w nieskończoność, oraz "Eutanastów", którzy chcieliby jak najszybciej pozbyć się uciążliwych chorych i niedołężnych ludzi. Ludzie normalni, nie należący do żadnego z tych obozów, jak to zwykle, nie mają nic do powiedzenia. A przynajmniej nikt w prasie się za nimi nie opowiada.

Nie zamierzam polemizować z tymi idiotami, eutanastami. To są po prostu zakamuflowani mordercy (lub samobójcy).

Nie zamierzam również polemizować z tymi idiotami, atanastami. Przedłużanie każdemu człowiekowi życia w nieskończoność jest sprzeczna z prawem naturalnym. Jest to więc kolejna lewicowa ideologia.

Co ciekawe, dokładnie te argumenty wytaczają przeciwko sobie te dwa obozy. Jedyne co względem tego, co pokazuje prasa, jest nienormalne, to opowiadanie się za tymi dwoma poglądami jednocześnie. Jak to się im udało, żeby postawić jednoznaczną sprzeczność jednego z drugim? Bardzo prosto: należało tylko wyznaczyć obszar, który sam w sobie jest sprzecznością i nie mówić o jego istnieniu.

Normalnych ludzi bowiem eutanaści oskarżają o atanazję, a atanaści o eutanazję. Jeśli więc posługiwać się warunkami stworzonymi przez prasę, to możesz być albo eutanastą, albo atanastą - a twór będący jednym i drugim stoi sam ze sobą w logicznej sprzeczności, więc istnieć nie może. Sprytnie, nie?

A polega to na takim zdefiniowaniu eutanazji i atanazji (tak, przyznaję się, pojęcie wymyśliłem sam, bo nie znalazłem w otaczającej mowie niczego podobnego; słowo to powstało z greckich "a-" - zaprzeczenia i "thanatos" - śmierć), żeby oba te pojęcia przywłaszczały sobie część poglądów wyznawanych przez ludzi normalnych. Pozwala to przeciągać normalnych ludzi do obozu eutanastów lub atanastów.

I żeby było jasne - ideologii eutanastów nie definiują eutanaści. Ideologię eutanastów definiują atanaści, a ideologię atanastów - eutanaści. Nie jest to bowiem żadne uczciwe postawienie sprawy na zasadzie: "jestem kimś takim i wyznaję takie-a-takie poglądy", nawet żeby były one bardzo skrajne. Jest to działanie typu "przyprawiamy sobie nawzajem gęby". To właśnie normalni ludzie są przez eutanastów oskarżani o atanazję, a przez atanastów o eutanazję.

Więc jeśli już stawiać sprawę jasno, to należy raczej powiedzieć, jak atanazja jest definiowana (przez eutanastów), a jak eutanazja (przez atanastów).

Eutanazja zatem jest to wszystko to, co ma prowadzić do śmierci człowieka (nawet jeśli ta śmierć nastąpiła z przyczyn naturalnych). Eutanastą jest zatem każdy człowiek, który uważa, że jakiś człowiek powinien umrzeć. Do przejawów eutanazji zatem zalicza się zarówno zabijanie człowieka, którym trzeba się opiekować, zabijanie człowieka, który cierpi z powodu ciężkiej choroby (żeby się "nie męczył"), jak również odcięcie od aparatury człowieka, który jest już rośliną (umarł jego mózg).

Atanazja natomiast (czyli zestaw poglądów potępianych przez eutanastów) jest to obrona życia za wszelką cenę, również obrona życia zupełnie bez sensu, podtrzymywanie pewnych oznak życia (najczęściej bicia serca), które wcale nie oznaczają, że człowiek jest żywy i świadomy itd.

Oczywiście eutanaści myślą o sobie zupełnie co innego - oni uważają, że bronią prawa do śmierci, podobnie zresztą atanaści są święcie przekonani, że są obrońcami życia. Oba te obozy mają tylko jedną wspólną cechę - żadna z nich nie stawia na pierwszym miejscu człowieka. W obu człowiek jest rzeczą, na której się eksperymentuje, która nie ma nic do powiedzenia. Paradoskalnie, obie te ideologie są lewicowe i obie są antyliberalne - prawo do śmierci odbiera prawo do życia i odwrotnie. Normalni ludzie, dla których liczą się wartości liberalne, i dla których każdy człowiek powinien mieć prawo do życia i prawo do śmierci równocześnie, są sprzecznością samą w sobie (względem tego, co widzimy w prasie).

Winnymi całego wielkiego zamieszania są jednak atanaści. Eutanaści bowiem są, przynajmniej w Polsce, jedną ze skrajnie lewicowych ideologii, na które w Polsce jest, delikatnie mówiąc, umiarkowane zapotrzebowanie i które zyskują u ludzi niewielki posłuch. Trochę inaczej jest w tzw. "starej unii", ale prawdę mówiąc więcej się o tym mówi, niż jest w tym prawdy. Nawet zresztą w całej Europie nikt eutanastów nie traktuje wcale aż tak poważnie, jak by to wynikało z doniesień prasowych (zdominowanych zresztą przez środowiska lewicowe - i nie jestem pierwszym, który to stwierdził, co więcej, ja to tylko powtarzam po innych). Każdy Anglik lub Francuz, pełen górnolotnych frazesów o tolerancji, w przypływie szczerości (np. po dużej ilości napojów "wysokoskokowych") może wam powiedzieć, co się "naprawdę" myśli o tych ideologiach i ludziach. I tak samo nikt nie myśli poważnie, żeby eutanazja mogła stać się prawem. Przyczyna jest oczywista - zgódź się na eutanazję, to potem ciebie "coś" spotka.

Atanaści jednak rozpętali przeciwko tym nieszkodliwym idiotom taką nagonkę, że łatę eutanasty przykleili i prawdziwym eutanastom i normalnym ludziom. Okazuje się, że eutanastą jest każdy, kto ośmieli się w jakikolwiek sposób "przyczynić" do śmierci człowieka, choćby przez nic nie robienie. A jeśli już trafiło się, że ktoś jest podtrzymywany przez aparaturę - to już kaplica. Oczywiście aparatura może dzisiaj już podtrzymywać życie człowieka praktycznie w nieskończoność. Co prawda nie doszło jeszcze dzisiaj do czegoś takiego, że ktoś przeżył już 100 lat, od 30 lat jest podtrzymywany sztucznie przez aparaturę. Byłby to bardzo ciekawy przypadek, bo ktoś mógłby zadać pytanie: przepraszam bardzo, wszyscy jego kumple, których żadna aparatura nie podtrzymywała, już dawno wyciągnęli nogi - to ile jeszcze do jasnej cholery czasu mam go podtrzymywać???

Ja mam tylko nadzieję na jedno: że w którymś momencie duch tego sztucznie podtrzymywanego człowieka objawi się tym zażartym obrońcom życia i ich opierdoli. I każe natychmiast odłączyć jego sztucznie ożywiane zwłoki od aparatury. Przydałoby się to, zwłaszcza że atanaści zwykle są związani z "środowiskami chrześcijańskimi", więc to by był na nich dobry hak. Niestety na to akurat nie mamy wpływu.

Ale o - właśnie tutaj wyraziłem pogląd ludzi "normalnych", które mogą spowodować, że atanaści natychmiast przyczepią mi łatkę eutanasty. No bo jak to - odłączyć od aparatury? To jest zabójstwo, morderstwo, obrzydlistwo!

W ten właśnie sposób atanaści wpychają normalnych ludzi w objęcia eutanastów. Każdy normalny człowiek wie bowiem, że sztuczne podtrzymywanie życia człowieka, który jest już biologicznie martwy, to mniej więcej coś podobnego, jak hodowanie rybek w akwarium - tyle tylko, że znacznie bardziej kosztowne i zdecydowanie nieetyczne, skoro jest to zabawa ludzkimi zwłokami.

Dochodzi do tego również kwestia kosztów. Ja rozumiem, że być może kobiecie, która straciła męża, jest ciężko z tym, że nie miała środków na jego leczenie - ale nie rozumiem, dlaczego nie mógł jej ktoś po prostu pomóc. Najlepiej, żeby zresztą ci atanaści zajęli się właśnie taką działalnością. Jeśli chce się, żeby życie człowieka beznadziejnie chorego ktoś podtrzymywał, to niech dadzą na to środki.

Co ciekawe - i to właśnie pokazuje, że sprawa "eutanazji" z Włoch nie jest prostą sprawą - nie chodziło tutaj tylko o kwestię odłączenia od aparatury. Znalazła się bowiem fundacja, która chciała przejąć finansowanie przedłużania życia tej kobiety. W tej sytuacji ja bym nie protestował - jeśli ktoś chce sam finansować owe przedłużanie agonii, jego sprawa. Protestuję jedynie przeciwko żądaniu, aby ktoś finansował coś, czego finansować nie chce, tylko dlatego, że "tak jest etycznie". Natomiast w podanej sytuacji nastąpiło odłączenie od aparatury, mimo że znalazł się ktoś, kto chciał finansować przedłużanie życia. Świadczy to o tym, że odłączenie od aparatury nastąpiło z przyczyn czysto ideologicznych.

I tu właśnie wyjaśnię, dlaczego ten artykuł znalazł się pod "Yellow Pages".

Nikt, ale to absolutnie nikt z relacjonujących owo zdarzenie w prase nie przytoczył jednej, bardzo istotnej informacji: czy owa kobieta miała orzeczoną przez lekarza śmierć mózgu. Nikt właściwie nie powiedział, na ile ta kobieta miała badany mózg i na ile jest on uszkodzony i czy będzie mogła odzyskać pełną sprawność. Inaczej mówiąc, poza ogólnikowym stwierdzeniem, że ta kobieta jest już warzywem, nie dowiedzieliśmy się nic na temat przyczyn, dla których odłączono ją od aparatury.

Może w przyszłości będą jakieś metody pozwalające takie osoby wyleczyć, odgryzają się atanaści. Jasne. Moim zdaniem nastąpi to dopiero w czasach, gdy wyhoduje się sztuczny mózg (i wszystkie inne organy) i będzie można mu ten mózg przeszczepić. Póki co, na taki wynalazek się nie zanosi. Co więcej, nawet jeśli, załóżmy teoretycznie, znajdzie się w przyszłości sposób na wyleczenie tej osoby, to wciąż nie wiadomo, czy ktoś będzie mógł za to leczenie zapłacić!

To prawda, że dzisiaj kryteria określające możliwość przeżycia człowieka pozwalają przeżyć większej ilości osób - ale przecież dzieje się tak również dlatego, że dziś można wyleczyć choroby, na które dawniej ludzie umierali. To prawda, że prof. Stephen Hawking w XVI wieku dawno by wyciągnął nogi - ale nie mówimy o możliwościach przeżycia w jakiejś nieokreślonej przyszłości, gdy takie przeżycie będzie możliwe, tylko o możliwości przeżycia tu i teraz. Pan prof. Hawking mógł przeżyć nie tylko dlatego, że istniała aparatura, która mogła mu przedłużać życie. Również dlatego, że mimo paraliżu ciała ten człowiek mógł pracować i nadal mieć osiągnięcia naukowe, więc koszty utrzymania go przez aparaturę mógł pokryć sam bez niczyjej łaski.

Powiedzmy więc może sobie szczerze i jasno - zabójstwem jest odebranie życia człowiekowi zdrowemu, poruszającego się (nawet w ograniczonym zakresie) o własnych siłach, człowiekowi świadomemu. Natomiast jeśli ktoś z przyczyn naturalnych umiera - to trudno, widać przyszedł na niego czas. Jeśli się mu podtrzymuje życie, to fajnie, może pożyje potem jeszcze jakiś czas. Natomiast nikt nie może mieć pretensji, że ktoś komuś życia nie uratował lub nie przedłużył!

Ja rozumiem, że ktoś może potem mieć wyrzuty sumienia, że komuś nie pomógł, ale tak naprawdę to jest wynik szantażu ze strony atanastów. Odmawianie komuś prawa do życia dla nich to zabójstwo, a nieudzielenie pomocy w celu uratowania życia jest dla nich odmówieniem prawa do życia.

Problem w tym, że gdyby pójść jeszcze dalej, to okazałoby się, że skoro ja mam komuś ratować życie, a jeśli tego nie zrobię, to odmawiam mu prawa do życia - to znaczy, że owo prawo do życia nadaję mu w tym momencie właśnie ja! I w tym momencie jest drobny problem - na jakiej podstawie ktoś ma mi mówić, czy ja mam owo prawo do życia komuś dać, czy nie? Jeśli los zdarzył, że mam władzę nad życiem i śmiercią określonego człowieka, to dlaczego ktoś ma mi dyktować, co mam z tym prawem zrobić? Chcecie, to szukajcie - nie ma żadnego prawa naturalnego, ani boskiego, które nakazuje coś konkretnego robić w tej sytuacji.

Zwłaszcza właśnie atanaści mówią, że tylko Bóg jest panem życia i śmierci, więc tylko on ma prawo je odbierać lub dawać. Skoro tak, to znaczy, że jeśli ktoś umiera, to dlatego, że Bóg tak chciał, prawda? Czyli udzielenie mu w ogóle pomocy byłoby sprzeczne z wolą Boga!

(Dygresja, ale tylko dla chętnych: Oczywiście, że Bóg dopuściłby do śmierci tego człowieka i oczywiście, że człowiek mógłby uratować owo życie. Dopuszcza się obie możliwości. Jeśli by tak nie było, to w ogóle żaden człowiek nie mógłby uratować żadnemu człowiekowi życia, a tym samym nie miałby takiej zasługi. W historii chrześcijaństwa znane są przypadki, gdy człowiek postąpił niezgodnie z wolą Boga, choć oczywiście nie było to sprzeczne z jego postanowieniami i udało mu się postawić na swoim. Było to możliwe tylko dlatego, że Bóg pozwolił człowiekowi postawić na swoim. Nie zmienia to faktu, że jeśli człowiek tak zrobi, to co najwyżej będzie miał dodatkowa zasługę - natomiast nie otrzyma żadnej "kary", jeśli z tej okazji nie skorzysta!)

Czyli zbierając to wszystko do kupy: gdyby używać prostackich objaśnień owej ideologii, to wynika z tego, że ratując komuś życie postępuje się sprzecznie z wolą Boga, ale z kolei nie ratując komuś życia człowiek staje się mordercą. Inaczej mówiąc: jeśli znalazłeś się w sytuacji bycia czyimś panem życia i śmierci, to masz pecha. Chyba, że jesteś jakimś dyktatorem, ale akurat to pozycji wobec Boga wtedy w najmniejszym stopniu nie poprawia.

To jest szantaż, jeśli mamy nazywać rzeczy po imieniu. I nie wiem, czy ma to jakąś określoną nazwę w psychologii, ale na pewno zjawisko jest bardzo dobrze znane. To jest dokładnie taki sam szantaż, jak komentarze pod twoim adresem ze strony ludzi w tramwaju, bo nie chciałeś ustąpić miejsca siedzącego staruszce (a może właśnie ty jesteś skrajnie wyczerpany i słaniasz się ze zmęczenia - nie masz obowiązku nikomu ustępować miejsca!). Tak samo, jak nie wpuszczenie do kolejki matki z dzieckiem na ręku (oczywiście, że niemowlęta narzucają strasznie bezwzględny rygor czasowy rodzicom, ale zdarza się też, że to "dziecko na ręku" ma jakieś 5 lat i wcale jego harmonogram nie odbiega od harmonogramu dorosłego człowieka!). Tak samo, jak odmowa przeprowadzenia staruszki przez ulicę. Jeśli ja komuś w wyżej wymieniony sposób pomogę, jest to tylko moja dobra wola. Mogę chcieć pomóc i mogę nie chcieć. Jeśli odmówię, to jest to moje prawo. Pomoc w tych wypadkach nie jest w żadnym stopniu moim obowiązkiem. Z tego powodu domaganie się od jakiegokolwiek człowieka pomocy w tych przypadkach jest chamstwem. I nie ma żadnego usprawiedliwienia dla takiego postępowania.

I dokładnie tak samo jest w tym przypadku. Powinienem mieć prawo odmówić ratowania życia człowiekowi, jeśli tak zechcę. Powinienem nawet mieć prawo odmówić mu życia po jakimś czasie od jego uratowania, nawet kilku lat. Jeśli to rzeczywiście Bóg jest panem życia i śmierci, to znaczy, że gdyby jego wola była taka, że ten człowiek ma żyć, to sprawiłby cud, żeby ten człowiek żył. A jeśli żadnego takiego cudu nie sprawia, to znaczy, że jego wolą jest śmierć tego człowieka. Tyle.

Przy takim postawieniu sprawy chyba nikt nie powinien mieć wątpliwości, jak ma postąpić w podobnych przypadkach. Jeśli jesteś w sytuacji, że człowiek obok ciebie umiera, a tylko ty możesz uratować mu życie - nie słuchaj tych, którzy ci mówią "nie zastanawiaj się". Właśnie zastanów się. Bo w tym przypadku jesteś panem życia i śmierci. I to niekoniecznie absolutnym - czasem próba uratowania życia przy nieznajomości stopnia wewnętrznych obrażeń może nawet przyspieszyć śmierć.

Jeśli więc jest to, na przykład, wypadek samochodowy - zrób tylko tyle, ile wymaga prawo (tzn. zadzwoń na pogotowie) - a coś więcej to zrób tylko jeśli jesteś pewien, że uratuje mu to życie. Jeśli jesteś prawnym opiekunem kogoś, kto został ci przekazany w wegetującym stanie - to jest twoja sprawa, czy odłączysz go od aparatury, czy nie. Jesteś panem życia i śmierci i ze sposobu skorzystania z tego prawa nie musisz się przed nikim tłumaczyć. Oczywiście mówię tylko o sytuacji, gdy owo prawo zostało nadane przez niespodziewane okoliczności, w których się człowiek znalazł, a nie zostało one samodzielnie przezeń "wypracowane" (na przykład, postrzeliłeś człowieka, i masz szansę uratować go przed wykrwawieniem się). Jak by nie było, sprawa jest prosta: co by było, gdyby nie było ciebie na miejscu i w ogóle nie miałbyś wpływu na całą sytuację? Jeśli by ten człowiek umarł w tym przypadku, to jego śmierć w sytuacji, gdy mogłeś uratować mu życie, jest tylko naturalną koleją rzeczy, której ty nie zaburzyłeś. Tyle.

A eutanazję należy nazywać po imieniu. Eutanazja jest to zabicie człowieka, który żyje "ale co to za życie". Najczęściej jest to śmierć na życzenie człowieka, którego sytuacja życiowa jest beznadziejna. W tym jednym przypadku oczywiście również mam swoje zdanie, to znaczy - jeśli człowiek jest w stanie sam żyć, ale nie chce już dłużej żyć - to niech popełni samobójstwo. Jeśli tak koniecznie chce, to niech mu się nawet wykonanie tego ułatwi: jego wola, to niech ją spełni. To oznacza, że:


  1. jeśli ta beznadziejnie chora osoba chce umrzeć, to należy ewentualnie przygotować jej zestaw do śmierci. Ta osoba musi sama jednak wykonać kluczowy ruch niezbędny do uruchomienia mechanizmu pozbawiającego ją życia.
  2. jeśli śmierci tej osoby chce ktokolwiek inny - niech idzie się wypałować
  3. ludzie, którzy utrzymują życie tej chorej osoby mają prawo zaprzestania jego finansowania. Jeśli to oznacza, że w szpitalu mają tą osobę przedstać utrzymywać przy życiu, to tak mają zrobić.


Na pewno rozmijam się tutaj z poglądami tzw. "środowisk chrześcijańskich" (których bezmózgie ideologie są dokładnie tak samo lewicowe, jak ideologie gejów, lesbijek i eutanastów), natomiast trzymam się jednej zasady: w społeczeństwie uważa się, że człowiek jest właścicielem swojego życia, a w społeczeństwie szanującym zasady każdy człowiek ma prawo dysponować jak chce wszystkim, co do niego należy, a także nikomu nie wolno dysponować jego własnością bez jego zgody. Każdy, kto szanuje liberalne zasady wie, że człowiek ma prawo do życia i prawo do śmierci, w tym również prawo do przedłużania sobie życia w nieskończoność (na ile on lub ktoś inny jest w stanie to finansować) i prawo do odebrania sobie życia. I nikomu nie wolno człowiekowi ani tego prawa odbierać, ani mówić mu, z którego z tych praw ma skorzystać.

Tyle wystarczy, żeby utrzymać społeczeństwo w stanie nie pożerającym sie nawzajem. Do niczego ponad to człowiek nie może być zmuszany. Co ponad to powinien był dopełnić, a nie dopełnił, to będzie się już sam osobiście rozliczał przed Bogiem. Niezależnie od tego, czy w niego wierzy, czy nie.