Wydawnictwo Helion ul. Chopina 6 44-100 Gliwice tel. (32)230-98-63 e-mail: [email protected]PRZYK£ADOWY ROZDZIA£ PRZYK£ADOWY ROZDZIA£ IDZ DO IDZ DO ZAMÓW DRUKOWANY KATALOG ZAMÓW DRUKOWANY KATALOG KATALOG KSI¥¯EK KATALOG KSI¥¯EK TWÓJ KOSZYK TWÓJ KOSZYK CENNIK I INFORMACJE CENNIK I INFORMACJE ZAMÓW INFORMACJE O NOWOCIACH ZAMÓW INFORMACJE O NOWOCIACH ZAMÓW CENNIK ZAMÓW CENNI K CZYTELNIA CZYTELNIA FRAGMENTY KSI¥¯EK ONLINE FRAGMENTY KSI¥¯EK ONLINE SPIS TRECI SPIS TRECI DODAJ DO KOSZYKA DODAJ DO KOSZYKA KATALOG ONLINE KATALOG ONLINE C++. Strategie i taktyki. Vademecum profesjonalisty Autor: Robert B. Murray T³umaczenie: Przemys³aw Steæ ISBN: 83-7361-323-4 Tytu³ orygina³u: C++ Strategies and Tactics Format: B5, stron: 240 Poznanie ruchów figur szachowych to dopiero pierwszy krok w nauce tej gry. Aby j¹ opanowaæ, trzeba zrozumieæ strategie i taktyki, które wp³ywaj¹ na ka¿dy ruch. To samo dotyczy jêzyka C++. Znajomoæ w³aciwych strategii pomaga unikaæ pu³apek i pracowaæ o wiele skuteczniej. Rob Murray dziel¹c siê swoim dowiadczeniem pomaga programistom C++ wykonaæ nastêpny krok w kierunku tworzenia wydajnych aplikacji. Licznie wystêpuj¹ce w ca³ej ksi¹¿ce przyk³ady kodu maj¹ na celu zilustrowanie przydatnych strategii programistycznych i ostrzec przed nabyciem niebezpiecznych nawyków. Aby dodatkowo u³atwiæ przyswajanie nowych umiejêtnoci, ka¿dy rozdzia³ koñczy siê list¹ poruszonych w nim kluczowych zagadnieñ oraz pytaniami maj¹cymi spowodowaæ przemylenia i dyskusje. Ksi¹¿ka przedstawia miêdzy innymi: • Tworzenie w³aciwych abstrakcji dla projektu i przekszta³canie abstrakcji w klasy C++ • Mechanizmy dziedziczenia pojedynczego i wielokrotnego • Metody tworzenia klas • Szczegó³owy opis mechanizmu szablonów • Wskazówki dotycz¹ce stosowania wyj¹tków • Metody tworzenia kodu nadaj¹cego siê do wielokrotnego wykorzystania • Przenoszenie programów z jêzyka C do C++ Robert B. Murray jest wicedyrektorem ds. in¿ynierii oprogramowania w firmie Quantitative Data Systems dostarczaj¹cej niestandardowych rozwi¹zañ z zakresu oprogramowania dla czo³owych firm. Wczenie pracowa³ w AT&T Bell Labs, gdzie bra³ udzia³ w rozwoju jêzyka C++, jego kompilatorów i bibliotek. Jest pierwszym redaktorem magazynu „The C++ Report”. Od 1987 prowadzi zajêcia dotycz¹ce jêzyka C++ na konferencjach naukowych i technicznych.
20
Embed
C++. Strategie i taktyki. Vademecum profesjonalisty
Poznanie ruchów figur szachowych to dopiero pierwszy krok w nauce tej gry. Aby ją opanować, trzeba zrozumieć strategie i taktyki, które wpływają na każdy ruch. To samo dotyczy języka C++. Znajomość właściwych strategii pomaga unikać pułapek i pracować o wiele skuteczniej. Rob Murray dzieląc się swoim doświadczeniem pomaga programistom C++ wykonać następny krok w kierunku tworzenia wydajnych aplikacji.
Licznie występujące w całej książce przykłady kodu mają na celu zilustrowanie przydatnych strategii programistycznych i ostrzec przed nabyciem niebezpiecznych nawyków. Aby dodatkowo ułatwić przyswajanie nowych umiejętności, każdy rozdział kończy się listą poruszonych w nim kluczowych zagadnień oraz pytaniami mającymi spowodować przemyślenia i dyskusje.
Książka przedstawia między innymi:
* Tworzenie właściwych abstrakcji dla projektu i przekształcanie abstrakcji w klasy C++ * Mechanizmy dziedziczenia pojedynczego i wielokrotnego * Metody tworzenia klas * Szczegółowy opis mechanizmu szablonów * Wskazówki dotyczące stosowania wyjątków * Metody tworzenia kodu nadającego się do wielokrotnego wykorzystania * Przenoszenie programów z języka C do C++
Robert B. Murray jest wicedyrektorem ds. inżynierii oprogramowania w firmie Quantitative Data Systems dostarczającej niestandardowych rozwiązań z zakresu oprogramowania dla czołowych firm. [więcej...]
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
��������!� "�#���� ��3.1. Klasa Lancuch.................................................................................................................60
3.2. Unikanie kopiowania przez zastosowanie liczników użycia ..........................................61
3.3. Zapobieganie powtórnym kompilacjom — „Kot z Cheshire”........................................66
3.4. Stosowanie uchwytów w celu ukrycia szczegółów projektu..........................................68
�������� � )������ ���7.1. Szablon klasy Para ........................................................................................................119
7.2. Kilka szczegółów dotyczących szablonów...................................................................122
Powyższy kod opiera się na fakcie, że Koło jest Kształtem, a więc referencję do Koła
� można przekazać do każdej funkcji posiadającej parametr typu �����!. Oznacza to,
że nie możemy w przyszłości zmodyfikować tej klasy, usuwając z niej dziedziczenie
i oczekiwać, że istniejący już kod będzie działał! Byłaby to niezgodna modyfikacja in-
terfejsu — równoważna usunięciu publicznej funkcji składowej.
�������������������� ����
Dziedziczenie prywatne stosowane jest w przypadku, gdy dziedziczenie nie stanowi ele-
mentu interfejsu, a jedynie implementacyjny szczegół. Użytkownicy nie mogą tworzyć
kodu uzależnionego od takiego dziedziczenia, dzięki czemu zachowujemy możliwość mo-
dyfikacji implementacji polegającej na rezygnacji z używania danej klasy bazowej.
Dziedziczenie prywatne stosowane jest znacznie rzadziej niż dziedziczenie publiczne,
ponieważ realizacja złożenia (czyli wykorzystanie części „klasy bazowej” jako danej
składowej) jest prostsza i działa zazwyczaj równie dobrze. Zamiast dziedziczenia po
klasie bazowej, pojedynczy obiekt tej klasy bazowej umieszczany jest jako składowa
w klasie (dawnej) pochodnej. Takie rozwiązanie nie powinno powodować żadnej utraty
Rozdział 4. � Dziedziczenie 79
Powtórka: Dziedziczenie publiczne, chronione i prywatne
W języku C++ istnieją trzy rodzaje dziedziczenia: ��"���, �������� oraz ���#���. We wszystkichformach dziedziczenia funkcje składowe klas pochodnych mają dostęp do składowych publicz-nych i chronionych klasy bazowej — lecz nie do składowych prywatnych. Te trzy typy dziedziczeniaróżnią się elementami, które są widoczne dla użytkownika klasy pochodnej (a nie twórcy klasypochodnej) oraz okolicznościami, w których użytkownik może niejawnie przekonwertować wskaź-nik do klasy pochodnej na wskaźnik do klasy bazowej.
Najczęstszą formą dziedziczenia jest dziedziczenie publiczne:
Przy zastosowaniu dziedziczenia publicznego, składowe publiczne klasy bazowej pozostają pu-bliczne w klasie pochodnej, a składowe chronione klasy bazowej pozostają chronione w klasiepochodnej:
Przy zastosowaniu dziedziczenia prywatnego, składowe publiczne i chronione klasy bazowej stająsię prywatne w klasie pochodnej. Dostęp do nich mają składowe oraz funkcje i klasy zaprzyjaź-nione klasy pochodnej, lecz nie użytkownicy:
Przy zastosowaniu dziedziczenia prywatnego, składowe publiczne i chronione klasy bazowej stająsię chronione w klasie pochodnej. Klasy pochodne mogą wywoływać funkcje składowe chronio-nej klasy bazowej, a także niejawnie przekonwertować wskaźnik do klasy pochodnej na wskaźnikdo chronionej klasy bazowej.
Składową publiczną prywatnej lub chronionej klasy bazowej można uczynić publiczną w klasie po-chodnej za pomocą tzw. deklaracji dostępu:
Dzięki temu użytkownicy będą mieli możliwość wywoływania dla obiektu typu $���������%���funkcji ������ tak, jak gdyby została ona zadeklarowana w następujący sposób:
Zaimplementowaliśmy funkcję, która, dzięki zastosowaniu wywołań kilku operacji abs-
trakcyjnych (wirtualnych funkcji składowych), działa z każdą klasą, która jest wyprowa-dzona z klasy ���� i poprawnie realizuje te operacje abstrakcyjne, nie posiadając jedno-
cześnie żadnej innej wiedzy na temat tych obiektów. To jest właśnie jedna z głównych
zalet projektowania obiektowego.
Jeśli związek pomiędzy składowymi ������� i �� ����� nie będzie wyraźnie udoku-
mentowany i rozumiany przez projektantów, znajdzie się ktoś, kto zaimplementuje klasę
pochodną, która nie będzie zgodna z modelem abstrakcyjnym, np.:
Autor klasy ,������� źle zrozumiał, jak powinna działać funkcja składowa �� �����.
Dlatego klasa ,������� nie jest zgodna z modelem abstrakcyjnym klasy ����.
Rozważmy, co się stanie, jeśli dla obiektu typu ,������� poruszającego się z prędkością
100 km/h wywołamy funkcję ��� ���. Funkcja ��� ��� wykona następujące wywołanie
�'���������-�'�����������
Rozdział 4. � Dziedziczenie 85
które w tym przypadku spowoduje wywołanie funkcji
0������������������-4>>��
co z kolei wywoła funkcję
�����+������������-A>>��
Po wywołaniu funkcji ��� ��� nasz ,������� będzie jechał z prędkością 100 km/h
w przeciwnym kierunku! Z pewnością programista nie to miał na myśli. Pomimo że kod
spełnia ograniczenia dotyczące typów narzucane przez język — kompilacja przebiega
bez problemów — to jego działanie nie jest prawidłowe, ponieważ klasa ,������� nie
jest zgodna z modelem abstrakcyjnym klasy ����.
�%�&������������ � ��������
Nasza pierwotna klasa ���� zawiera deklarację funkcji składowej �� �����. Umie-
ściliśmy ją w tej klasie, ponieważ �� ����� jest operacją, która jest pojęciowo po-
prawna dla wszystkich pojazdów. Oczekujemy, że wersja tej funkcji występująca w kla-
sie bazowej zostanie przesłonięta w każdej klasie pochodnej.
W jaki sposób powinniśmy zaimplementować funkcję �������� �����? Nie prze-
widujemy w ogóle tworzenia obiektów typu ����. Klasa ���� jest za to klasą bazową,
która opisuje pojęcia wspólne dla zbioru klas pochodnych. W zamierzeniu klasa ����ma być używana wyłącznie jako klasa bazowa, a funkcja �� ����� zostanie przesło-
nięta w każdej klasie pochodnej. Nie spodziewamy się więc, żeby ktoś kiedykolwiek
wywołał funkcję �������� �����. Jedno podejście mogłoby polegać na zdefinio-
waniu wersji, która w przypadku wywołania wyświetli komunikat o błędzie:
86 C++. Strategie i taktyki. Vademecum profesjonalisty
Powtórka: Funkcje czysto wirtualne i abstrakcyjne klasy bazowe
Wirtualna funkcja składowa, w której w deklaracji po liście argumentów występuje wyrażenie -(.:
������K���������� �������7���&�>���
jest tzw. funkcją czysto wirtualną. Nie trzeba podawać żadnej definicji funkcji czysto wirtualnej
/��%��. Każda klasa, która deklaruje lub dziedziczy funkcję czysto wirtualną jest abstrakcyjną klasąbazową. Próba utworzenia obiektu abstrakcyjnej klasy bazowej spowoduje błąd podczas kompilacji.
Jeśli w klasie wyprowadzonej z klasy / funkcja /��% zostanie przesłonięta, to ta klasa będzie jużklasą konkretną (nieabstrakcyjną):
������0��� �����K����������7�����
Abstrakcyjna klasa bazowa służy do deklarowania interfejsu bez deklarowania pełnego zbioruimplementacji dla tego interfejsu. Taki interfejs określa operacje abstrakcyjne realizowane przezwszystkie obiekty wyprowadzone z tej klasy — obowiązek zapewnienia implementacji dla tychoperacji abstrakcyjnych spoczywa już na klasach pochodnych. Na przykład:
Destruktor ������2���� będą wywoływać destruktory każdej klasy wyprowadzonejz klasy ����. Ponieważ definicja tego destruktora musi istnieć (w przeciwnym razie otrzy-mamy błędy modułu ładującego), deklarowanie go jako czysto wirtualnego nie ma sensu.
�'�(�����)*������*������ ����������������������$
Sposób obsługi dziedziczenia przez język C++ zawiera kilka sztuczek. Przyjrzyjmy się im:
�� ����!��"������������#��$������������
Podczas korzystania z mechanizmu dziedziczenia należy zawsze pamiętać o elementach,które nie są dziedziczone po klasie bazowej:
� Konstruktory (w tym konstruktor kopiujący). Jeśli nie zadeklarujemy konstruktorakopiującego, automatycznie zostanie utworzony konstruktor kopiujący, którybędzie wywoływać konstruktory kopiujące niestatycznych danych składowychoraz klas bazowych.
� Destruktor. Jeśli nie zadeklarujemy destruktora, a dowolna z niestatycznych danychskładowych lub klas bazowych posiada destruktor, to automatycznie zostanieutworzony destruktor, który będzie wywoływać destruktory niestatycznych danychskładowych oraz klas bazowych. Destruktor ten będzie wirtualny, jeśli dowolnaz klas bazowych posiada destruktor wirtualny.
� Operator przypisania. Jeśli nie zadeklarujemy operatora przypisania, automatyczniezostanie utworzony operator przypisania, który będzie wywoływać operatoryprzypisania niestatycznych danych składowych oraz klas bazowych.
� Ukryte funkcje składowe. Jeśli funkcja składowa klasy bazowej nie jest przesłoniętaw klasie pochodnej, a w tej klasie pochodnej zadeklarowana jest funkcja o tej samejnazwie, lecz o różnych argumentach, to funkcja występująca w klasie bazowejbędzie ukryta. Na przykład: