92

Księgarnia internetowa - pdf.helion.pl · • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność

Embed Size (px)

Citation preview

• Kup książkę• Poleć książkę • Oceń książkę

• Księgarnia internetowa• Lubię to! » Nasza społeczność

Spis treści

Podzi�kowania ..................................................................................... 19

Wst�p .................................................................................................. 21

Rozdzia� 1. Zaczynamy .......................................................................................... 31Nauka C++ — co nas czeka? ............................................................................. 31Pochodzenie j�zyka C++ — krótka historia ........................................................... 32

J�zyk C ........................................................................................................ 32Filozofia programowania w C ......................................................................... 33Zmiana w C++ — programowanie obiektowe .................................................. 34C++ i programowanie uogólnione ................................................................... 35Pochodzenie C++ ......................................................................................... 36

Przeno�no�� i standardy .................................................................................... 37Rozwój j�zyka w liczbach ............................................................................... 38Standardy C++ w niniejszej ksi��ce ................................................................ 39

Mechanika tworzenia programu ........................................................................... 39Pisanie kodu �ród�owego ............................................................................... 40Kompilacja i konsolidacja .............................................................................. 41

Podsumowanie .................................................................................................. 45

Rozdzia� 2. Pierwszy program w C++ ...................................................................... 47C++ — pocz�tek ................................................................................................ 47

Cechy funkcji main() ..................................................................................... 49Komentarze w C++ ....................................................................................... 51Preprocesor i plik iostream ............................................................................ 52Nazwy plików nag�ówkowych .......................................................................... 53Przestrzenie nazw ......................................................................................... 53Wypisywanie danych — cout .......................................................................... 55Formatowanie kodu �ród�owego C++ .............................................................. 57

Instrukcje C++ ................................................................................................... 59Instrukcje deklaracji i zmienne ....................................................................... 60Instrukcja przypisania ................................................................................... 61Nowa sztuczka z cout .................................................................................... 62

6 J�zyk C++. Szko�a programowania

Inne instrukcje C++ ............................................................................................ 62U�ycie obiektu cin ......................................................................................... 63Z��czanie za pomoc� cout ............................................................................. 64cin i cout — klasy po raz pierwszy .................................................................. 64

Funkcje ............................................................................................................. 66U�ycie funkcji zwracaj�cej warto�� ................................................................. 66Odmiany funkcji ............................................................................................ 69Funkcje definiowane przez u�ytkownika .......................................................... 70Funkcje u�ytkownika zwracaj�ce warto�� ........................................................ 73Dyrektywa using w programach z wieloma funkcjami ........................................ 74

Podsumowanie .................................................................................................. 76Pytania sprawdzaj�ce ......................................................................................... 77wiczenia programistyczne ................................................................................. 77

Rozdzia� 3. Dane .................................................................................................... 79Zmienne proste ................................................................................................. 80

Nazwy zmiennych .......................................................................................... 80Typy ca�kowitoliczbowe .................................................................................. 81Typy short, int, long i long long ...................................................................... 82Typy bez znaku ............................................................................................. 87Dobór w�a�ciwego typu ................................................................................. 89Litera�y ca�kowitoliczbowe ............................................................................. 90Jak C++ ustala typ sta�ej? ............................................................................. 91Typ char — znaki i ma�e liczby ca�kowite ........................................................ 92Typ danych bool ......................................................................................... 100

Kwalifikator const ............................................................................................ 100Liczby zmiennoprzecinkowe .............................................................................. 101

Zapis liczb zmiennoprzecinkowych ............................................................... 102Zmiennoprzecinkowe typy danych ................................................................. 103Sta�e zmiennoprzecinkowe .......................................................................... 105Zalety i wady liczb zmiennoprzecinkowych ................................................... 105

Operatory arytmetyczne C++ ............................................................................. 106Kolejno�� dzia�a — priorytety operatorów i ��czno�� .................................... 107Odmiany dzielenia ...................................................................................... 108Operator modulo ........................................................................................ 110Konwersje typów ........................................................................................ 110Automatyczne deklaracje typów w C++11 ..................................................... 116

Podsumowanie ................................................................................................ 117Pytania sprawdzaj�ce ....................................................................................... 117wiczenia programistyczne ............................................................................... 118

Rozdzia� 4. Typy z�o�one ...................................................................................... 121Tablice w skrócie ............................................................................................. 122

Uwagi o programie ...................................................................................... 124Inicjalizacja tablic ....................................................................................... 124Inicjalizacja tablic w C++11 ......................................................................... 125

�acuchy ........................................................................................................ 126��czenie litera�ów napisowych ..................................................................... 127�acuchy w tablicy ...................................................................................... 128Problemy z wprowadzaniem �acuchów znakowych ........................................ 129

Spis tre�ci 7

Wczytywanie �acuchów znakowych wierszami ............................................... 130Mieszanie w danych wej�ciowych �acuchów i liczb ........................................ 134

Klasa string — wprowadzenie ........................................................................... 135Inicjalizacja �acuchów znakowych w C++11 ................................................. 136Przypisanie, konkatenacja i do��czanie ......................................................... 136Inne operacje klasy string ........................................................................... 138Klasa string a wej�cie i wyj�cie .................................................................... 139Inne odmiany litera�ów napisowych .............................................................. 141

Struktury ......................................................................................................... 142U�ycie struktury w programie ....................................................................... 143Inicjalizacja struktur w C++11 ...................................................................... 145Czy w strukturze mo�na u�y� pola typu string? .............................................. 146Inne cechy struktur ..................................................................................... 146Tablice struktur .......................................................................................... 148Pola bitowe ................................................................................................ 149

Unie ............................................................................................................... 149Typy wyliczeniowe ............................................................................................ 151

Ustawianie warto�ci enumeratorów .............................................................. 153Zakresy warto�ci w typach wyliczeniowych .................................................... 153

Wska�niki i ró�ne drobiazgi ............................................................................... 154Deklarowanie i inicjalizacja wska�ników ........................................................ 156Niebezpieczestwa zwi�zane ze wska�nikami ............................................... 158Wska�niki i liczby ........................................................................................ 159U�ycie operatora new do alokowania pami�ci ................................................ 159Zwalnianie pami�ci za pomoc� delete .......................................................... 161U�ycie new do tworzenia tablic dynamicznych ................................................ 162

Wska�niki, tablice i arytmetyka wska�ników ....................................................... 165Podsumowanie informacji o wska�nikach ...................................................... 168Wska�niki i �acuchy ................................................................................... 170U�ycie new do tworzenia struktur dynamicznych ............................................ 174Alokacja pami�ci: automatyczna, statyczna i dynamiczna ............................... 177

Kombinacje typów ............................................................................................ 179Tablice inaczej ................................................................................................. 181

Klasa szablonowa vector ............................................................................. 181Klasa szablonowa array (C++11) ................................................................. 182Porównanie tablic z obiektami vector i array .................................................. 183

Podsumowanie ................................................................................................ 184Pytania sprawdzaj�ce ....................................................................................... 185wiczenia programistyczne ............................................................................... 186

Rozdzia� 5. P�tle i wyra�enia relacyjne ................................................................. 189P�tle for .......................................................................................................... 190

Elementy p�tli for ....................................................................................... 191Wracamy do p�tli for ................................................................................... 196Zmiana wielko�ci kroku ............................................................................... 198P�tla for i �acuchy znakowe ........................................................................ 198Operatory inkrementacji (++) i dekrementacji (--) ........................................... 199Efekty uboczne i punkty odniesienia ............................................................. 200Formy przedrostkowe a formy przyrostkowe .................................................. 201

8 J�zyk C++. Szko�a programowania

Operatory inkrementacji i dekrementacji a wska�niki ..................................... 202Z�o�one operatory przypisania ...................................................................... 203Instrukcje z�o�one, czyli bloki ....................................................................... 203Przecinek jako operator (i pewne sztuczki sk�adniowe) ................................... 205Wyra�enia relacyjne .................................................................................... 208Przypisania, porównania i pomy�ki ................................................................ 208Porównywanie �acuchów w stylu C .............................................................. 210Porównywanie obiektów klasy string ............................................................. 213

P�tla while ...................................................................................................... 213Uwagi o programie ...................................................................................... 215P�tla for a p�tla while ................................................................................. 216Chwileczk� — tworzymy p�tl� opó�nienia ..................................................... 217

P�tla do while .................................................................................................. 219Zakresowe p�tle for (C++11) ............................................................................ 221P�tle i wprowadzanie danych tekstowych ........................................................... 221

Najprostsza wersja cin ................................................................................ 222cin.get(char) na odsiecz .............................................................................. 223Która wersja cin.get() jest lepsza? ............................................................... 224Koniec pliku ............................................................................................... 224Jeszcze inna wersja cin.get() ....................................................................... 227

P�tle zagnie�d�one i dwuwymiarowe tablice ....................................................... 230Inicjalizacja tablic dwuwymiarowych .............................................................. 232Stosowanie tablic dwuwymiarowych ............................................................. 232

Podsumowanie ................................................................................................ 234Pytania sprawdzaj�ce ....................................................................................... 234wiczenia programistyczne ............................................................................... 235

Rozdzia� 6. Instrukcje warunkowe i operatory logiczne .......................................... 237Instrukcja if ..................................................................................................... 237

Instrukcja if else ......................................................................................... 239Formatowanie instrukcji if else .................................................................... 241Konstrukcja if else if else ............................................................................ 241

Wyra�enia logiczne ........................................................................................... 243Logiczny operator alternatywy — || ............................................................... 243Logiczny operator koniunkcji — && .............................................................. 245Ustalanie zakresu za pomoc� operatora && ................................................. 247Operator negacji logicznej — ! ..................................................................... 248O operatorach logicznych ............................................................................ 250Zapis alternatywny ...................................................................................... 251

Biblioteka cctype ............................................................................................. 251Operator ?: ...................................................................................................... 253Instrukcja switch .............................................................................................. 255

U�ycie enumeratorów jako etykiet ................................................................ 258switch versus if else ................................................................................... 259

Instrukcje break i continue ............................................................................... 259Uwagi o programie ...................................................................................... 261

P�tle wczytywania liczb ..................................................................................... 262Uwagi o programie ...................................................................................... 264

Spis tre�ci 9

Proste wej�cie-wyj�cie z pliku ............................................................................. 265Tekstowe wej�cie-wyj�cie i pliki tekstowe ..................................................... 265Zapis do pliku tekstowego ........................................................................... 267Odczyt danych z pliku tekstowego ................................................................ 270

Podsumowanie ................................................................................................ 274Pytania sprawdzaj�ce ....................................................................................... 275wiczenia programistyczne ............................................................................... 276

Rozdzia� 7. Funkcje — sk�adniki programów w C++ .............................................. 279Funkcje w skrócie ............................................................................................ 280

Definiowanie funkcji .................................................................................... 281Prototypowanie i wywo�ywanie funkcji ........................................................... 283

Parametry funkcji i przekazywanie przez warto�� ................................................. 286Wiele parametrów ....................................................................................... 287Jeszcze jedna funkcja dwuargumentowa ....................................................... 289

Funkcje i tablice .............................................................................................. 291Jak wska�niki umo�liwiaj� tworzenie funkcji przetwarzaj�cych tablice? ............ 292Skutki u�ycia tablic jako parametrów ............................................................ 293Dodatkowe przyk�ady funkcji i tablic ............................................................. 295Funkcje korzystaj�ce z zakresów tablic ......................................................... 301Wska�niki i modyfikator const ...................................................................... 302

Funkcje i tablice dwuwymiarowe ........................................................................ 306Funkcje i �acuchy w stylu C ............................................................................. 307

Funkcje z �acuchami w stylu C jako parametrami ......................................... 307Funkcje zwracaj�ce �acuchy w formacie C ................................................... 309

Funkcje i struktury ........................................................................................... 310Przekazywanie i zwracanie struktur ............................................................... 311Inny przyk�ad u�ycia funkcji i struktur ............................................................ 312Przekazywanie adresu struktury ................................................................... 317

Funkcje i obiekty klasy string ............................................................................ 318Funkcje i obiekty typu array ......................................................................... 320Uwagi o programie ...................................................................................... 321

Rekurencja ...................................................................................................... 322Rekurencja w pojedynczym wywo�aniu .......................................................... 322Rekurencja w wielu wywo�aniach .................................................................. 324

Wska�niki na funkcje ........................................................................................ 325Wska�niki na funkcje — podstawy ............................................................... 325Przyk�ad u�ycia wska�ników na funkcje ......................................................... 327Wariacje na temat wska�ników funkcji .......................................................... 329Uproszczenie poprzez typedef ...................................................................... 333

Podsumowanie ................................................................................................ 333Pytania sprawdzaj�ce ....................................................................................... 334wiczenia programistyczne ............................................................................... 336

Rozdzia� 8. Funkcje — zagadnienia zaawansowane ............................................... 339Funkcje inline .................................................................................................. 339Zmienne referencyjne ....................................................................................... 342

Tworzenie zmiennej referencyjnej ................................................................. 342Referencje jako parametry funkcji ................................................................ 345W�a�ciwo�ci referencji ................................................................................. 348

10 J�zyk C++. Szko�a programowania

U�ycie referencji do struktur ........................................................................ 352U�ycie referencji z obiektami ....................................................................... 358Obiekty po raz wtóry — obiekty, dziedziczenie i referencje .............................. 361Kiedy korzysta� z referencji jako parametrów? .............................................. 364

Parametry domy�lne ........................................................................................ 365Uwagi o programie ...................................................................................... 366

Przeci��anie funkcji .......................................................................................... 367Przyk�ad przeci��ania funkcji ........................................................................ 370Kiedy korzysta� z przeci��ania funkcji? ......................................................... 372

Szablony funkcji ............................................................................................... 372Przeci��one szablony .................................................................................. 375Ograniczenia szablonów .............................................................................. 377Specjalizacje jawne ..................................................................................... 377Konkretyzacje i specjalizacje ....................................................................... 380Któr� wersj� funkcji wybierze kompilator? ..................................................... 382Ewolucja szablonów funkcji .......................................................................... 388

Podsumowanie ................................................................................................ 392Pytania sprawdzaj�ce ....................................................................................... 392wiczenia programistyczne ............................................................................... 393

Rozdzia� 9. Model pami�ci i przestrzenie nazw ...................................................... 397Kompilacja roz��czna ....................................................................................... 397Czas �ycia, zasi�g i ��czenie ............................................................................. 403

Zasi�g i ��czenie ........................................................................................ 404Przydzia� automatyczny ................................................................................ 404Zmienne statyczne ..................................................................................... 409Przydzia� statyczny, ��czenie zewn�trzne ....................................................... 411Specyfikatory i kwalifikatory ......................................................................... 419��czenie a funkcje ...................................................................................... 421��czenie j�zykowe ...................................................................................... 422Kategorie przydzia�u a przydzia� dynamiczny .................................................. 423

Przestrzenie nazw ............................................................................................ 429Tradycyjne przestrzenie nazw j�zyka C++ ...................................................... 429Nowe mechanizmy przestrzeni nazw ............................................................. 431Przestrzenie nazw — przyk�ad ...................................................................... 438Przysz�o�� przestrzeni nazw ......................................................................... 441

Podsumowanie ................................................................................................ 442Pytania sprawdzaj�ce ....................................................................................... 442wiczenia programistyczne ............................................................................... 445

Rozdzia� 10. Obiekty i klasy ................................................................................... 447Programowanie proceduralne a programowanie obiektowe .................................. 448Klasy a abstrakcje ........................................................................................... 449

Czym jest typ? ............................................................................................ 449Klasy w j�zyku C++ ..................................................................................... 450Implementowanie metod klas ...................................................................... 455Stosowanie klas ......................................................................................... 459Zmiany implementacji ................................................................................. 461Podsumowanie poznanych wiadomo�ci ........................................................ 462

Spis tre�ci 11

Konstruktory i destruktory ................................................................................. 463Deklarowanie i definiowanie konstruktorów ................................................... 464Stosowanie konstruktorów .......................................................................... 465Konstruktory domy�lne ............................................................................... 466Destruktory ................................................................................................ 467Ulepszenia klasy Stock ............................................................................... 468Konstruktory i destruktory — podsumowanie ................................................ 475

To�samo�� obiektu — wska�nik this ................................................................. 476Tablice obiektów .............................................................................................. 482Zasi�g klasy .................................................................................................... 485

Sta�e zasi�gu klasy ..................................................................................... 486Wyliczenia z w�asnym zasi�giem (C++11) ..................................................... 487

Abstrakcyjne typy danych .................................................................................. 488Podsumowanie ................................................................................................ 492Pytania sprawdzaj�ce ....................................................................................... 493wiczenia programistyczne ............................................................................... 493

Rozdzia� 11. Stosowanie klas ................................................................................. 497Przeci��anie operatorów ................................................................................... 498Raz, dwa, trzy — próba przeci��enia operatora .................................................. 499

Dodatkowy operator dodawania ................................................................... 502Ograniczenia przeci��ania operatorów .......................................................... 505Jeszcze o przeci��aniu operatorów ............................................................... 506

Przyjaciele najwa�niejsi .................................................................................... 509Deklarowanie przyja�ni ................................................................................ 510Typowa przyja� — przeci��anie operatora << .............................................. 512

Przeci��anie operatorów — metody kontra funkcje niesk�adowe .......................... 518Przeci��ania ci�g dalszy — klasa Vector ............................................................ 519

Sk�adowa koduj�ca stan obiektu .................................................................. 526Przeci��anie operatorów arytmetycznych dla klasy Vector ............................... 528Nota implementacyjna ................................................................................ 530Wektorowe b��dzenie losowe ....................................................................... 530

Automatyczne konwersje i rzutowanie typów klas ................................................ 534O programie ............................................................................................... 539Funkcje konwersji ....................................................................................... 539Konwersja a zaprzyja�nienie ........................................................................ 544

Podsumowanie ................................................................................................ 547Pytania sprawdzaj�ce ....................................................................................... 549wiczenia programistyczne ............................................................................... 549

Rozdzia� 12. Klasy a dynamiczny przydzia� pami�ci .................................................. 553Klasy a pami�� dynamiczna .............................................................................. 554

Powtórka z pami�ci dynamicznej i statyczne sk�adowe klas ............................ 554Specjalne metody klasy .............................................................................. 562W czym tkwi problem z konstruktorem kopiuj�cym w Stringbad? ..................... 565Kolejne s�abo�ci Stringbad: operatory przypisania ......................................... 568

Nowa, ulepszona klasa — String ....................................................................... 571Nowa wersja konstruktora domy�lnego ......................................................... 572Porównywanie ci�gów ................................................................................. 573

12 J�zyk C++. Szko�a programowania

Indeksowanie ci�gu .................................................................................... 574Statyczne metody klasy ............................................................................... 575Dalsze przeci��anie operatora przypisania .................................................... 576

O czym nale�y pami�ta�, stosuj�c new w konstruktorach? .................................. 581Zalecenia i przestrogi .................................................................................. 582Kopiowanie obiektów sk�adowa po sk�adowej ............................................... 583

S�ów par� o zwracaniu obiektów ........................................................................ 584Zwracanie niemodyfikowalnej (const) referencji obiektu ................................. 584Zwracanie modyfikowalnej (bez const) referencji do obiektu ........................... 585Zwracanie obiektu przez warto�� .................................................................. 585Zwracanie przez warto�� obiektu niemodyfikowalnego (const) ........................ 586

Wska�niki obiektów .......................................................................................... 587Jeszcze o new i delete ................................................................................ 589Wska�niki obiektów — podsumowanie ......................................................... 590Jeszcze o miejscowej wersji new .................................................................. 592

Powtórka z poznanych technik ........................................................................... 596Przeci��anie operatora << ........................................................................... 596Funkcje konwersji ....................................................................................... 597Klasy wykorzystuj�ce new w konstruktorach .................................................. 597

Symulacja kolejki ............................................................................................. 598Klasa kolejki .............................................................................................. 598Klasa klienta .............................................................................................. 609Symulacja bankomatu ................................................................................. 612

Podsumowanie ................................................................................................ 616Pytania sprawdzaj�ce ....................................................................................... 617wiczenia programistyczne ............................................................................... 619

Rozdzia� 13. Klasy i dziedziczenie ........................................................................... 623Prosta klasa bazowa ........................................................................................ 624

Dziedziczenie ............................................................................................. 626Konstruktory — zagadnienia zwi�zane z poziomem dost�pu ........................... 628Korzystanie z klasy pochodnej ..................................................................... 631Relacje mi�dzy klas� pochodn� a bazow� ..................................................... 633

Dziedziczenie — relacja jest-czym� .................................................................... 635Polimorficzne dziedziczenie publiczne ................................................................ 636

Tworzenie klas Brass oraz BrassPlus ........................................................... 637Wi�zanie statyczne i dynamiczne ....................................................................... 648

Zgodno�� typów wska�nikowych i referencyjnych ........................................... 648Metody wirtualne i wi�zanie dynamiczne ....................................................... 650Co trzeba wiedzie� o metodach wirtualnych? ................................................. 653

Kontrola dost�pu — poziom chroniony .............................................................. 656Abstrakcyjne klasy bazowe ............................................................................... 657

Stosowanie abstrakcyjnych klas bazowych .................................................... 659Filozofia abstrakcyjnych klas bazowych ......................................................... 665

Dziedziczenie i dynamiczny przydzia� pami�ci ...................................................... 665Przypadek pierwszy — klasa pochodna bez dynamicznego przydzia�u pami�ci ... 665Przypadek drugi — klasa pochodna z dynamicznym przydzia�em pami�ci ..... 666Przyk�ad dziedziczenia z wykorzystaniem dynamicznego przydzia�u

pami�ci oraz funkcji zaprzyja�nionych ......................................................... 668

Spis tre�ci 13

Projektowanie klas — przegl�d zagadnie .......................................................... 673Metody automatycznie generowane przez kompilator ..................................... 673Inne metody ............................................................................................... 675Dziedziczenie publiczne ............................................................................... 678Metody klasy — podsumowanie .................................................................. 682

Podsumowanie ................................................................................................ 683Pytania sprawdzaj�ce ....................................................................................... 683wiczenia programistyczne ............................................................................... 684

Rozdzia� 14. Wielokrotne u�ycie kodu w C++ .......................................................... 687Klasy ze sk�adowymi w postaci obiektów ........................................................... 688

Krótka charakterystyka klasy valarray ........................................................... 688Projekt klasy Student .................................................................................. 689Przyk�adowa klasa Student ........................................................................... 691

Dziedziczenie prywatne ..................................................................................... 697Nowa wersja klasy Student .......................................................................... 697

Dziedziczenie wielokrotne ................................................................................. 706Podwójne egzemplarze klasy Worker ............................................................ 711Podwójne metody ....................................................................................... 714Przegl�d zagadnie zwi�zanych z dziedziczeniem wielokrotnym ....................... 723

Szablony klas .................................................................................................. 724Definiowanie szablonu klasy ........................................................................ 724Korzystanie z szablonu klasy ....................................................................... 727Analiza szablonu klasy ................................................................................ 729Szablon tablicy i argumenty pozatypowe szablonu ......................................... 734Elastyczno�� szablonów .............................................................................. 736Specjalizacja szablonu ................................................................................ 739Szablony jako sk�adowe .............................................................................. 742Szablony jako parametry ............................................................................. 744Szablony klas i zaprzyja�nienie .................................................................... 746Szablonowe aliasy typów (C++11) ................................................................ 752

Podsumowanie ................................................................................................ 753Pytania sprawdzaj�ce ....................................................................................... 755wiczenia programistyczne ............................................................................... 757

Rozdzia� 15. Zaprzyja�nienie, wyj�tki i nie tylko ...................................................... 763Zaprzyja�nienie ................................................................................................ 763

Klasy zaprzyja�nione ................................................................................... 764Zaprzyja�nione metody klas ......................................................................... 768Inne relacje przyja�ni ................................................................................... 771

Klasy zagnie�d�one .......................................................................................... 773Dost�p do klas zagnie�d�onych ................................................................... 774Zagnie�d�anie w szablonie .......................................................................... 776

Wyj�tki ............................................................................................................ 779Wywo�ywanie funkcji abort() ......................................................................... 779Zwracanie kodu b��du ................................................................................. 780Mechanizm wyj�tków .................................................................................. 782Wyj�tki w postaci obiektów .......................................................................... 784Specyfikacje wyj�tków a C++11 ................................................................... 788Rozwijanie stosu ........................................................................................ 789

14 J�zyk C++. Szko�a programowania

Inne w�a�ciwo�ci wyj�tków .......................................................................... 793Klasa exception .......................................................................................... 796Wyj�tki, klasy i dziedziczenie ....................................................................... 799Problemy z wyj�tkami .................................................................................. 804Ostro�nie z wyj�tkami ................................................................................. 807

RTTI ................................................................................................................ 808Po co nam RTTI? ........................................................................................ 808Jak dzia�a RTTI? ......................................................................................... 809

Operatory rzutowania typu ................................................................................ 816Podsumowanie ................................................................................................ 820Pytania sprawdzaj�ce ....................................................................................... 820wiczenia programistyczne ............................................................................... 822

Rozdzia� 16. Klasa string oraz biblioteka STL .......................................................... 823Klasa string ..................................................................................................... 823

Tworzenie obiektu string ............................................................................. 824Wprowadzanie danych do obiektów string ..................................................... 828U�ywanie obiektów string ............................................................................ 830Co jeszcze oferuje klasa string? ................................................................... 835Warianty klasy string ................................................................................... 837

Szablony klas inteligentnych wska�ników ........................................................... 837Stosowanie inteligentnych wska�ników ......................................................... 838Wi�cej o inteligentnych wska�nikach ............................................................ 841Wy�szo�� unique_ptr nad auto_ptr ............................................................... 844Wybór inteligentnego wska�nika ................................................................... 845

Biblioteka STL ................................................................................................. 847Szablon klasy vector ................................................................................... 847Metody klasy vector .................................................................................... 849Inne mo�liwo�ci klasy vector ........................................................................ 853Zakresowe p�tle for (C++11) ....................................................................... 857

Programowanie uogólnione ............................................................................... 858Do czego potrzebne s� iteratory? ................................................................. 858Rodzaje iteratorów ...................................................................................... 862Hierarchia iteratorów .................................................................................. 865Poj�cia, u�ci�lenia i modele ........................................................................ 866Rodzaje kontenerów ................................................................................... 872Kontenery asocjacyjne ................................................................................ 881Nieuporz�dkowane kontenery asocjacyjne (C++11) ....................................... 887

Obiekty funkcyjne (funktory) .............................................................................. 887Poj�cia zwi�zane z funktorami ..................................................................... 888Funktory predefiniowane ............................................................................. 891Funktory adaptowalne i adaptatory funkcji .................................................... 892

Algorytmy ........................................................................................................ 895Grupy algorytmów ....................................................................................... 895Ogólne w�a�ciwo�ci algorytmów ................................................................... 896Biblioteka STL i klasa string ........................................................................ 897Funkcje a metody kontenerów ..................................................................... 898U�ywanie biblioteki STL ............................................................................... 899

Spis tre�ci 15

Inne biblioteki .................................................................................................. 903Klasy vector, valarray i array ........................................................................ 903Szablon initializer_list (C++11) .................................................................... 908Stosowanie szablonu initializer_list .............................................................. 910Uwagi do programu ..................................................................................... 911

Podsumowanie ................................................................................................ 911Pytania sprawdzaj�ce ....................................................................................... 913wiczenia programistyczne ............................................................................... 914

Rozdzia� 17. Obs�uga wej�cia, wyj�cia oraz plików .................................................. 917Ogólna charakterystyka obs�ugi wej�cia-wyj�cia w j�zyku C++ .............................. 918

Strumienie i bufory ..................................................................................... 919Strumienie i bufory a plik iostream ............................................................... 921Przekierowanie ........................................................................................... 923

Realizacja operacji wyj�cia z wykorzystaniem obiektu cout ................................... 924Przeci��ony operator << .............................................................................. 924Inne metody klasy ostream ......................................................................... 927Opró�nianie bufora wyj�ciowego ................................................................... 930Formatowanie danych wyj�ciowych za pomoc� obiektu cout ........................... 931

Realizacja operacji wej�cia z wykorzystaniem obiektu cin ......................................... 945Jak operator >> obiektu cin „widzi” dane wej�ciowe? .................................... 947Stany strumienia ........................................................................................ 949Inne metody klasy istream .......................................................................... 953Pozosta�e metody klasy istream ................................................................... 960

Wej�cie-wyj�cie plikowe .................................................................................... 964Proste operacje wej�cia-wyj�cia plikowego .................................................... 965Kontrola strumienia i metoda is_open() ........................................................ 968Otwieranie wielu plików ............................................................................... 969Przetwarzanie argumentów wiersza polecenia ............................................... 969Tryby otwarcia pliku .................................................................................... 971Dost�p swobodny ....................................................................................... 981

Formatowanie wewn�trzne ................................................................................ 988Podsumowanie ................................................................................................ 991Pytania sprawdzaj�ce ....................................................................................... 992wiczenia programistyczne ............................................................................... 993

Rozdzia� 18. Nowy standard C++ ............................................................................ 997Podsumowanie omawianych elementów C++11 ................................................. 997

Nowe typy .................................................................................................. 997Jednolita inicjalizacja .................................................................................. 998Deklaracje ................................................................................................. 999nullptr ...................................................................................................... 1001Inteligentne wska�niki ............................................................................... 1002Zmiany w specyfikacji wyj�tków .................................................................. 1002Jawny zasi�g elementów wylicze .............................................................. 1002Zmiany w klasach ..................................................................................... 1003Zmiany w szablonach i bibliotece STL ......................................................... 1004Referencje r-warto�ciowe ........................................................................... 1006

16 J�zyk C++. Szko�a programowania

Semantyka przeniesienia i referencje r-warto�ciowe .......................................... 1007Potrzeba semantyki przeniesienia .............................................................. 1007Przyk�ad przenoszenia ............................................................................... 1008Konstruktor przenosz�cy — wnioski ........................................................... 1013Przypisania .............................................................................................. 1014Wymuszanie przeniesienia ........................................................................ 1015

Nowe elementy klas ....................................................................................... 1018Specjalne metody klas .............................................................................. 1018Metody domy�lne i usuni�te ...................................................................... 1019Delegowanie konstruktorów ....................................................................... 1021Dziedziczenie konstruktorów ...................................................................... 1021Zarz�dzanie metodami wirtualnymi: ovverride i final ..................................... 1023

Funkcje lambda ............................................................................................. 1024Wska�niki do funkcji, funktory i lambdy ....................................................... 1024Po co nam lambdy? .................................................................................. 1027

Adaptery ....................................................................................................... 1030Adapter function a nieefektywno�� szablonów ............................................. 1030Naprawa problemu ................................................................................... 1032Dalsze mo�liwo�ci .................................................................................... 1034

Szablony o zmiennej liczbie parametrów .......................................................... 1035Pakiety parametrów szablonu i funkcji ........................................................ 1035Rozpakowywanie pakietów ........................................................................ 1036Rekurencja w szablonach o zmiennej liczbie parametrów ............................. 1037

Pozosta�e udogodnienia C++11 ...................................................................... 1040Programowanie wspó�bie�ne ...................................................................... 1040Uzupe�nienia biblioteki .............................................................................. 1040Programowanie niskopoziomowe ................................................................ 1041Inne ........................................................................................................ 1042

Zmiany j�zyka ................................................................................................ 1042Projekt Boost ........................................................................................... 1043TR1 ......................................................................................................... 1043Korzystanie z bibliotek Boost ..................................................................... 1043

Co dalej? ...................................................................................................... 1044Podsumowanie .............................................................................................. 1045Pytania sprawdzaj�ce ..................................................................................... 1046wiczenia programistyczne ............................................................................. 1049

Dodatek A Systemy liczbowe ............................................................................. 1051Liczby dziesi�tne (o podstawie 10) .................................................................. 1051Liczby ca�kowite ósemkowe (o podstawie 8) ..................................................... 1051Liczby szesnastkowe ...................................................................................... 1052Liczby dwójkowe (o podstawie 2) ..................................................................... 1052Zapis dwójkowy a szesnastkowy ..................................................................... 1053

Dodatek B S�owa zastrze�one j�zyka C++ .......................................................... 1055S�owa kluczowe j�zyka C++ ............................................................................ 1055Leksemy alternatywne .................................................................................... 1056Nazwy zastrze�one bibliotek j�zyka C++ ........................................................... 1056Identyfikatory o specjalnym znaczeniu ............................................................. 1057

Spis tre�ci 17

Dodatek C Zestaw znaków ASCII ...................................................................... 1059

Dodatek D Priorytety operatorów ....................................................................... 1063

Dodatek E Inne operatory .................................................................................. 1067Operatory bitowe ........................................................................................... 1067

Operatory przesuni�cia ............................................................................. 1067Bitowe operatory logiczne .......................................................................... 1069Alternatywne reprezentacje operatorów bitowych ......................................... 1071Kilka typowych technik wykorzystuj�cych operatory bitowe ........................... 1072

Operatory wy�uskania sk�adowych ................................................................... 1073alignof (C++11) ............................................................................................. 1077noexcept (C++11) .......................................................................................... 1078

Dodatek F Klasa szablonowa string ................................................................... 1079Trzyna�cie typów i sta�a .................................................................................. 1080Informacje o danych, konstruktory i ró�ne drobiazgi .......................................... 1080

Konstruktor domy�lny ............................................................................... 1083Konstruktory operuj�ce na klasycznych �acuchach C .................................. 1083Konstruktory operuj�ce na fragmentach �acuchów C .................................. 1084Konstruktory operuj�ce na referencji l-warto�ciowej ..................................... 1084Konstruktory operuj�ce na referencji r-warto�ciowej (C++11) ........................ 1085Konstruktory wykorzystuj�ce n kopii znaku .................................................. 1086Konstruktory wykorzystuj�ce zakres ........................................................... 1086Konstruktor operuj�cy na li�cie inicjalizuj�cej (C++11) ................................. 1086Metody zarz�dzaj�ce pami�ci� ................................................................... 1087

Dost�p do �acucha ....................................................................................... 1087Proste przypisanie ......................................................................................... 1088Przeszukiwanie �acuchów .............................................................................. 1089

Rodzina funkcji find() ................................................................................ 1089Rodzina funkcji rfind() ............................................................................... 1089Rodzina funkcji find_first_of() ..................................................................... 1090Rodzina funkcji find_last_of() ..................................................................... 1090Rodzina funkcji find_first_not_of() .............................................................. 1091Rodzina funkcji find_last_not_of() ............................................................... 1091

Metody i funkcje porównania .......................................................................... 1091Modyfikatory �acuchów ................................................................................. 1093

Metody do��czania i dodawania ................................................................. 1093Inne metody przypisania ............................................................................ 1094Metody wstawiania ................................................................................... 1094Metody usuwania ..................................................................................... 1095Metody zast�powania ............................................................................... 1095Pozosta�e metody modyfikuj�ce: copy() oraz swap() ..................................... 1096

Wej�cie i wyj�cie ............................................................................................ 1096

Dodatek G Metody i funkcje z biblioteki STL ...................................................... 1099STL a C++11 ................................................................................................. 1099

Nowe kontenery ....................................................................................... 1099Zmiany w kontenerach C++98 ................................................................... 1100

Sk�adowe wspólne dla wszystkich (lub wi�kszo�ci) kontenerów .......................... 1101Dodatkowe sk�adowe dla kontenerów sekwencyjnych ........................................ 1104

18 J�zyk C++. Szko�a programowania

Dodatkowe operacje zbiorów i map ................................................................. 1107Kontenery asocjacyjne nieporz�dkuj�ce (C++11) .............................................. 1109Funkcje STL ................................................................................................... 1111

Niemodyfikuj�ce operacje sekwencyjne ...................................................... 1111Mutuj�ce operacje sekwencyjne ................................................................. 1116Operacje sortowania i pokrewne ................................................................ 1125Operacje liczbowe ..................................................................................... 1139

Dodatek H Wybrane pozycje ksi��kowe i zasoby internetowe .............................. 1141Wybrane pozycje ksi��kowe ............................................................................ 1141Zasoby internetowe ........................................................................................ 1142

Dodatek I Dostosowywanie do standardu ANSI/ISO C++ ................................... 1145Unikanie nadu�ywania niektórych dyrektyw preprocesora ................................... 1145

Do definiowania sta�ych lepiej u�ywa� modyfikatora constni� dyrektywy #define .............................................................................. 1145

Do definiowania niewielkich funkcji lepiej u�ywa� specyfikatora inline ni�makrodefinicji #define ............................................................................ 1147

U�ywanie prototypów funkcji ........................................................................... 1148Stosowanie rzutowania typów ......................................................................... 1148Poznawanie i wykorzystywanie mechanizmów j�zyka C++ ................................ 1149U�ywanie nowej organizacji plików nag�ówkowych ............................................. 1149Korzystanie z przestrzeni nazw ........................................................................ 1149U�ywanie inteligentnych wska�ników ................................................................ 1150U�ywanie klasy string ..................................................................................... 1151Korzystanie z biblioteki STL ............................................................................ 1151

Dodatek J Odpowiedzi do pyta� sprawdzaj�cych ................................................ 1153

Skorowidz ........................................................................................ 1179

8Funkcje — zagadnienia

zaawansowane

rozdziale zostaną omówione następujące zagadnienia:

� Funkcje inline.� Zmienne referencyjne.� Przekazywanie funkcji parametrów przez referencję.� Parametry domyślne.� Przeciążanie funkcji.� Szablony funkcji.� Specjalizacje szablonów funkcji.

W rozdziale 7. omalże mimochodem pojawiło się wiele informacji o funkcjach w C++, ale jeszczewięcej jest przed nami. Funkcje C++ mają sporo nowych właściwości, których nie było w starymdobrym C. Te nowe możliwości to funkcje inline, przekazywanie zmiennych przez referencję, wartościdomyślne parametrów, przeciążanie funkcji i szablony funkcji. W niniejszym rozdziale bardziej niżdotąd zajmiemy się tymi elementami C++, które nie występują w C, zatem rozdział ten to pierwszyistotny wypad w kierunku dwóch plusów.

Funkcje inlineFunkcje inline to rozszerzenie C++ stworzone z myślą o przyspieszeniu działania programów.Podstawową różnicą między zwykłymi funkcjami a funkcjami inline nie jest sposób ich kodowania,ale metoda włączania ich do programu przez kompilator. Aby zrozumieć różnicę między zwykłymifunkcjami a funkcjami inline, musimy, dokładniej niż robiliśmy to dotąd, przyjrzeć się wnętrzuprogramu. Przystąpmy od razu do rzeczy.

Ostatecznym wynikiem procesu kompilacji jest program wykonywalny, który składa się z zestawuinstrukcji języka maszynowego. Kiedy uruchamiamy program, system operacyjny ładuje wszystkie teinstrukcje do pamięci komputera, tak że każda instrukcja ma własny adres w pamięci. Następniekomputer kolejno wykonuje instrukcję po instrukcji. Czasami kiedy występuje na przykład pętlaalbo instrukcja warunkowa, wykonywanie programu przeskakuje wprzód lub wstecz, pod inny adres.

W

340 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Wywołania zwykłych funkcji oznaczają skok programu pod inny adres (adres funkcji), a po wykonaniucałej funkcji skok powrotny. Przyjrzyjmy się typowej realizacji tego procesu nieco dokładniej. Kiedyprogram dochodzi do instrukcji skoku funkcji, zapamiętuje adres instrukcji znajdującej się za instrukcjąskoku, kopiuje parametry funkcji na stos (specjalnie przygotowany w tym celu fragment pamięci),skacze do miejsca w pamięci zawierającego początek kodu funkcji, wykonuje kod funkcji (ewentualnieumieszczając wynik funkcji w rejestrze), a następnie przeskakuje z powrotem do instrukcji, której adreszostał zapisany wcześniej1. Skoki wprzód i wstecz oraz zapamiętywanie lokalizacji skoków zajmują czas— jest to koszt wywoływania funkcji.

Funkcje inline stanowią alternatywę dla takiego rozwiązania. W funkcji inline kod skompilowanyjest włączony bezpośrednio w resztę programu — kompilator zastępuje wywołanie funkcji całym jejkodem. Wobec tego program nie musi przeskakiwać w inne miejsce ani potem wracać, dzięki czemufunkcje takie działają nieco szybciej niż ich zwykłe odpowiedniki, ale ceną za to jest większe zużyciepamięci. Jeśli program w 10 różnych miejscach wywołuje funkcję inline, w uzyskanym kodzie pojawisię 10 kopii tej funkcji (rysunek 8.1).

Rysunek 8.1. Funkcje inline a funkcje zwyk�e

1 To trochę jak zostawianie na chwilę czytanego tekstu w celu zapoznania się z przypisem — po lekturze tego przypisu

z powrotem wracamy w to samo miejsce, w którym skończyliśmy czytanie.

Funkcje inline 341

Funkcji inline trzeba używać ostrożnie. Jeśli czas potrzebny na wykonanie funkcji jest o wieledłuższy niż czas obsługi wywołania, to czas zaoszczędzony na braku skoków jest niewielkim ułamkiemcałego procesu wykonywania funkcji. Jeśli czas wykonywania kodu jest krótki, to funkcja inline pozwalaznacznie skrócić czas pracy programu. Jeżeli często udaje nam się zaoszczędzić nawet krótki czas, którystanowi jednak istotny fragment większego procesu, oszczędności mogą być znaczące.

Aby skorzystać z opisywanych tu możliwości, musimy podjąć przynajmniej jedno z dwóch działań:� Poprzedzić deklarację funkcji słowem kluczowym inline.� Poprzedzić definicję funkcji słowem kluczowym inline.

Powszechną praktyką jest pomijanie prototypu i wstawianie pełnej definicji (czyli nagłówka funkcjii całego kodu funkcji) tam, gdzie zwykle jest prototyp.

Kompilator wcale nie musi honorować żądania użytkownika, aby funkcja była inline. Kompilatormoże przyjąć, że funkcja jest zbyt duża lub że wywołuje samą siebie (funkcje inline nie powinny i nawetnie mogą być rekurencyjne), albo dany kompilator może mieć w ogóle funkcje inline wyłączone lubnawet niezaimplementowane.

Listing 8.1 pokazuje technikę funkcji inline w przypadku funkcji square(), która podnosi do kwadratuswój parametr. Zauważmy, że cała definicja tej funkcji mieści się w jednym wierszu. Nie jest to wymóg,ale jeśli definicja nie mieści się w jednym albo dwóch wierszach kodu (przy założeniu rozsądnej długościidentyfikatorów), funkcja zwykle nie nadaje się na funkcję inline.

Listing 8.1. inline.cpp

// inline.cpp -- uycie funkcji inline#include <iostream>

// definicja funkcji inlineinline double square(double x) { return x * x; }

int main(){ using namespace std; double a, b; double c = 13.0;

a = square(5.0); b = square(4.5 + 7.5); // mona przekaza� wyraenie cout << "a = " << a << ", b = " << b << "\n"; cout << "c = " << c; cout << ", c kwadrat = " << square(c++) << "\n"; cout << "Teraz c = " << c << "\n"; return 0;}

Oto wynik uruchomienia programu z listingu 8.1:

a = 25, b = 144c = 13, c kwadrat = 169Teraz c = 14

Widać tu, że funkcjom inline parametry przekazuje się przez wartość, tak jak zwykłym funkcjom.Jeśli parametr jest wyrażeniem, na przykład 4.5 + 7.5, funkcja przekaże wartość tego wyrażenia — w tymprzypadku 12. Powoduje to, że funkcje inline znacznie przewyższają makra znane z języka C; zobaczwskazówkę „Funkcje inline a makra”.

342 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Mimo że nie istnieje osobny prototyp, prototypowanie nadal obowiązuje. Chodzi o to, że w tymprzypadku cała definicja funkcji pojawia się przed pierwszym użyciem tej funkcji i to ona służy jakoprototyp. Znaczy to, że można użyć funkcji square() z parametrem typu int albo typu long, a programi tak parametry te zrzutuje na typ double i dopiero wtedy przekaże funkcji.

Funkcje inline a makraFunkcje inline to cecha specyficzna dla j�zyka C++, natomiast j�zyk C korzysta� z dyrektywypreprocesora #define, aby tworzy� makra stanowi�ce pewne przybli�enie kodu inline. Oto przyk�admakra podnosz�cego liczb� do kwadratu:

#define SQUARE(X) X*X

Jednak definicja taka nie dzia�a przez przekazywanie parametrów, ale przez podstawianie tekstu,gdzie X jest etykiet� symboliczn� i stanowi jedynie namiastk� parametru:

a = SQUARE(5.0); zostanie zast�pione przez a = 5.0*5.0;b = SQUARE(4.5 + 7.5); zostanie zast�pione przez b = 4.5 + 7.5*4.5 + 7.5;d = SQUARE(c++); zostanie zast�pione przez d = c++*c++;

Jedynie pierwszy przyk�ad z powy�szych zadzia�a prawid�owo. Sytuacj� mo�na nieco poprawi�,dodaj�c nawiasy:

#define SQUARE(X) ((X)*(X))

jednak nadal pozostaje problem zwi�zany z tym, �e makra nie s� przekazywane przez warto��.Nawet dla ostatniej definicji wywo�anie SQUARE(c++) spowoduje dwukrotn� inkrementacj� c,podczas gdy w przypadku analogicznej funkcji inline square() z listingu 8.1 wyznaczana jestwarto�� c; warto�� ta jest przekazywana do funkcji, a na koniec jest raz inkrementowana.

Celem nie jest tu pokazywanie, jak pisze si� makra w C. Chodzi raczej o to, �e je�li trzebaw formie makra zapisa� jakie� dzia�anie maj�ce charakter funkcji, to lepiej u�y� funkcji inline.

Zmienne referencyjneJęzyk C++ dodaje do języka nowy typ złożony — zmienną referencyjną. Referencja działa jak alias, czyliinna nazwa zmiennej zdefiniowanej już wcześniej. Jeśli na przykład twain będzie referencją do zmiennejclemens, do tej zmiennej będziemy mogli równoprawnie odwoływać się tak przez nazwę clemens, jak i przeztwain. Ale po co nam to? Chodzi o pomoc dla tych programistów, którzy wstydzą się nazw, jakie wcześniejnadali swoim zmiennym? Może i tak, ale najważniejszym zastosowaniem zmiennych referencyjnychjest używanie ich jako parametrów funkcji. Jeśli parametr jest referencją, funkcja działa na danychoryginalnych, a nie na kopii zmiennej. Referencje to wygodna alternatywa dla wskaźników przyprzetwarzaniu dużych struktur za pomocą funkcji; są one ogromnie ważne przy projektowaniu klas.Zanim zobaczymy, jak używać referencji w funkcjach, przeanalizujmy podstawowe cechy referencji.Pamiętać trzeba, że naszym celem jest omówienie działania referencji, a nie przekonanie się, jak sąone używane najczęściej.

Tworzenie zmiennej referencyjnejJak pamiętamy, w C i C++ symbol & pozwala pozyskać adres zmiennej. W C++ symbolowi & przypisanododatkowe znaczenie, zaprzęgając go do deklarowania referencji. Aby na przykład rodents było alternatywnąnazwą zmiennej rats, można zapisać:

int rats;int & rodents = rats; // rodents staje si� aliasem dla rats

Zmienne referencyjne 343

W tym kontekście & nie jest operatorem adresu, ale jest częścią identyfikatora typu. Tak jak char *oznacza wskaźnik typu char, tak int & sugeruje referencję do wartości int. Deklaracja referencji pozwalawymiennie używać nazw rats i rodents — obie nazwy odnoszą się do tej samej wartości, położonejw tym samym miejscu w pamięci. Na listingu 8.2 widać, że tak jest naprawdę.

Listing 8.2. firstref.cpp

// firstref.cpp -- definiowanie i uycie referencji#include <iostream>int main(){ using namespace std; int rats = 101; int & rodents = rats; // rodents to referencja

cout << "rats = " << rats; cout << ", rodents = " << rodents << endl; rodents++; cout << "rats = " << rats; cout << ", rodents = " << rodents << endl;

// w niektórych implementacjach C++ wymagane jest rzutowanie// typu adresów na typ unsigned cout << "adres rats = " << &rats; cout << ", adres rodents = " << &rodents << endl; return 0;}

Zwróćmy uwagę na operator & w instrukcji:

int & rodents = rats;

Nie jest on tu operatorem adresu, ale służy do zadeklarowania rodents jako zmiennej typu int &,czyli jako referencji do wartości int. Jednak symbol & w wyrażeniu:

cout << ", adres rodents = " << &rodents << endl;

to już operator adresu, a &rodents to adres zmiennej, do której odnosi się rodents. Oto wynikdziałania programu z listingu 8.2:

rats = 101, rodents = 101rats = 102, rodents = 102adres rats = 0x0065fd48, adres rodents = 0x0065fd48

Jak widać, rats i rodents mają tę samą wartość i ten sam adres (w różnych systemach sposóbwyświetlania adresu jest różny). Zwiększenie wartości zmiennej rodents o jeden wpływa na obie zmienne.Ściślej rzecz biorąc, wykonanie instrukcji rodents++ zwiększa o jeden wartość jednej zmiennej, która madwie nazwy. Pamiętajmy, że choć w przykładzie pokazano modelowe działanie referencji, to w praktycezwykle używa się ich inaczej — do przekazywania parametrów funkcji, szczególnie kiedy parametry te sąstrukturami lub obiektami.

Referencje są zwykle czymś niepojętym dla ekspertów języka C, którzy uczą się C++, gdyż są dziwniepodobne do wskaźników, ale jednocześnie jest w nich coś innego. Można na przykład utworzyć referencjęi wskaźnik odwołujące się do zmiennej rats:

int rats = 101;int & rodents = rats; // rodents to referencjaint * prats = &rats; // prats to wskanik

344 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Dalej można używać wymiennie wyrażeń rodents, *prats i rats oraz &rodents, prats i &rats. Z tegopunktu widzenia referencje wyglądają jak nieco inaczej zapisane wskaźniki, w których operator dereferencji *jest dany niejawnie. I tak naprawdę tym mniej więcej są referencje. Jednakże poza samym zapisem istniejąi inne różnice. Po pierwsze, referencję trzeba zainicjalizować w chwili jej deklarowania — nie można referencjizadeklarować, a wartości przypisać jej później, co jest normalnym postępowaniem w przypadkuwskaźników:

int rat;int & rodent;rodent = rat; // nie, tak nie wolno

UwagaDeklaruj�c zmienn� referencyjn�, nale�y j� zainicjalizowa�.

Referencja to coś w rodzaju stałego wskaźnika — trzeba ją zainicjalizować w chwili jej tworzenia,ale kiedy już raz powiążemy ją ze zmienną, to to powiązanie zostanie na zawsze. Wobec tego zapis:

int & rodents = rats;

można w zasadzie traktować jako zamienny z zapisem:

int * const pr = &rats;

W tym przypadku referencja rodents odgrywa taką samą rolę jak wyrażenie *pr.Na listingu 8.3 pokazano, co się stanie, jeśli spróbujemy zmienić powiązanie referencji ze zmiennej

rats na zmienną bunnies.

Listing 8.3. secref.cpp

// secref.cpp -- definiowanie i stosowanie referencji#include <iostream>int main(){ using namespace std; int rats = 101; int & rodents = rats; // rodents to referencja

cout << "rats = " << rats; cout << ", rodents = " << rodents << endl;

cout << "adres rats = " << &rats; cout << ", adres rodents = " << &rodents << endl;

int bunnies = 50; rodents = bunnies; // czy mona zmieni� referencj�? cout << "bunnies = " << bunnies; cout << ", rats = " << rats; cout << ", rodents = " << rodents << endl;

cout << "adres bunnies = " << &bunnies; cout << ", adres rodents = " << &rodents << endl; return 0;}

Zmienne referencyjne 345

Oto wyniki działania programu z listingu 8.3:

rats = 101, rodents = 101adres rats = 0x0065fd44, adres rodents = 0x0065fd44bunnies = 50, rats = 50, rodents = 50adres bunnies = 0x0065fd48, adres rodents = 0x0065fd4

Początkowo rodents odnosi się do rats, ale dalej program próbuje zmienić przypisanie:

rodents = bunnies;

Przez chwilę wygląda na to, że przypisanie się powiodło, gdyż wartość rodents zmieniła się ze 101 na50. Jednak dokładniejsze przyjrzenie się pokazuje, że zmienna rats także zmieniła swoją wartość na 50,a rats i rodents mają ten sam adres, inny od adresu bunnies. Zmienna rodents to alias dla rats, więcprzypisanie tak naprawdę znaczy tylko tyle:

rats = bunnies;

Zatem zmiennej rats przypisujemy wartość zmiennej bunnies. Krótko mówiąc, referencję możnaustawić w deklaracji, inicjalizując ją, ale nie można jej później niczego przypisać.

Załóżmy, że wypróbowaliśmy następujący kod:

int rats = 101;int * pt = &rats;int & rodents = *pt;int bunnies = 50;pt = &bunnies;

Zainicjalizowanie rodents wartością *pt powoduje, że rodents odnosi się do rats. W efekcie zmianapt tak, aby wskazywała bunnies, nie zmieni w niczym tego, że rodents odnosi się do rats.

Referencje jako parametry funkcjiNajczęściej referencje służą jako parametry funkcji, powodując, że nazwa zmiennej widoczna w funkcjijest aliasem zmiennej programu wywołującego. Taka metoda przekazywania parametrów nazywana jestprzekazywaniem przez referencję. Przekazywanie przez referencję pozwala wywołanej funkcji korzystaćze zmiennych funkcji wywołującej. Stanowi to zmianę w stosunku do C, gdzie dane są przekazywanewyłącznie przez wartość. Przekazywanie wartości powoduje, że wywołana funkcja działa na kopiachparametrów funkcji wywołującej (rysunek 8.2). Oczywiście C umożliwia przezwyciężenie tego ograniczeniaprzez użycie wskaźników.

Porównajmy zasady użycia referencji i wskaźników, wykorzystując do tego typowy problem — zamianęwartości dwóch zmiennych. Funkcja zamieniająca te wartości musi być w stanie zmienić wartościprogramu wywołującego. Oznacza to, że nie możemy przyjąć klasycznego przekazywania przez wartość,gdyż zamienialibyśmy wartości kopii zmiennych. Jeśli przekażemy referencję, funkcja będzie działała naoryginalnych danych. Można też przekazać wskaźniki. Na listingu 8.4 pokazano wszystkie trzy metody,z niedziałającym w tym przypadku przekazaniem przez wartość; dzięki temu można porównaćwszystkie opcje.

Listing 8.4. swaps.cpp

// swaps.cpp -- uycie referencji i wskaników do zamiany warto�ci#include <iostream>void swapr(int & a, int & b); // a, b to aliasy warto�ci intvoid swapp(int * p, int * q); // p, q to adresy warto�ci int

346 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Rysunek 8.2. Przekazywanie przez warto� i przez referencj�

void swapv(int a, int b); // a, b to nowe zmienneint main(){ using namespace std; int wallet1 = 300; int wallet2 = 350;

cout << "wallet1 = $" << wallet1; cout << " wallet2 = $" << wallet2 << endl;

cout << "Zamiana warto�ci za pomoc� referencji:\n"; swapr(wallet1, wallet2); // przeka zmienne cout << "wallet1 = $" << wallet1; cout << " wallet2 = $" << wallet2 << endl;

cout << "Zamiana warto�ci za pomoc� wskaników:\n"; swapp(&wallet1, &wallet2); // przeka adresy zmiennych cout << "wallet1 = $" << wallet1; cout << " wallet2 = $" << wallet2 << endl;

cout << "Próba zamiany przy przekazywaniu przez warto��:\n"; swapv(wallet1, wallet2); // przekazanie warto�ci zmiennych cout << "wallet1 = $" << wallet1; cout << " wallet2 = $" << wallet2 << endl; return 0;

Zmienne referencyjne 347

}

void swapr(int & a, int & b) // uycie referencji{ int temp;

temp = a; // uycie a, b jako warto�ci zmiennych a = b; b = temp;}

void swapp(int * p, int * q) // uycie wskaników{ int temp;

temp = *p; // uycie *p, *q jako warto�ci zmiennych *p = *q; *q = temp;}

void swapv(int a, int b) // próba uycia warto�ci{ int temp;

temp = a; // uycie a, b jako warto�ci zmiennych a = b; b = temp;}

Oto wyniki działania programu z listingu 8.4:

wallet1 = $300 wallet2 = $350 <-- warto�ci oryginalneZamiana warto�ci za pomoc� referencji:wallet1 = $350 wallet2 = $300 <-- warto�ci po zamianieZamiana warto�ci za pomoc� wskaników:wallet1 = $300 wallet2 = $350 <-- ponownie po zamianiePróba zamiany przy przekazywaniu przez warto��:wallet1 = $300 wallet2 = $350 <-- zamiana nieudana

Zgodnie z oczekiwaniami użycie wskaźników i referencji zadziałało prawidłowo, natomiast przekazanieprzez wartość nie przyniosło żądanego efektu.

Uwagi o programieNajpierw zwróćmy uwagę na sposób wywołania poszczególnych funkcji na listingu 8.4:

swapr(wallet1, wallet2); // przeka zmienneswapp(&wallet1, &wallet2); // przeka adresy zmiennychswapv(wallet1, wallet2); // przekazanie warto�ci zmiennych

Przekazanie przez referencję (swapr(wallet1, wallet2)) oraz przekazanie przez wartość(swapv(wallet1, wallet2)) wyglądają identycznie. Jedyny sposób zauważenia, że w przypadku swapr()mamy do czynienia z referencjami, to spojrzenie na prototyp funkcji i jej definicję. Z drugiej stronyobecność operatora adresu od razu ujawnia nam, gdzie mamy do czynienia z przekazywaniem przezwskaźnik (swapp(&wallet1, &wallet2)). Przypomnijmy, że deklaracja typu int *p oznacza, że p jestwskaźnikiem danej typu int, zatem parametr odpowiadający p powinien być adresem, na przykład &wallet1.

Teraz porównajmy kod funkcji swapr() (przekazanie przez referencję) i swapv() (przekazanie przezwartość). Jedyna zewnętrzna różnica leży w sposobie deklaracji parametrów funkcji:

348 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

void swapr(int & a, int & b)void swapv(int a, int b)

Wewnętrzna różnica to oczywiście to, że zmienne a i b są aliasami wallet1 i wallet2, wobec czegozamiana wartości a i b powoduje też zamianę wartości wallet1 i wallet2. Jednak w funkcji swapv()zmienne a i b to nowe zmienne będące kopiami wallet1 i wallet2, wobec czego zamiana wartości a i bnie wpływa na wallet1 i wallet2.

Na koniec porównajmy funkcję swapr() (przekazywanie przez referencję) ze swapp() (przekazywanieprzez wskaźnik). Pierwsza różnica to inna deklaracja funkcji:

void swapr(int & a, int & b)void swapp(int * p, int * q)

Druga różnica polega na tym, że wersja wskaźnikowa wymaga użycia operatora dereferencji w przypadkukorzystania z p i q.

Wcześniej mówiliśmy, że zmienne referencyjne należy inicjalizować w chwili ich definiowania.Wywołanie funkcji oznacza zainicjalizowanie parametrów funkcji wartościami argumentów wywołaniafunkcji. Więc parametry funkcji będące referencjami należy traktować jako zainicjalizowane parametramiprzekazanymi w wywołaniu funkcji. Zatem wywołanie funkcji:

swapr(wallet1, wallet2);

powoduje zainicjalizowanie parametrów argumentami wywołania: a jest inicjalizowane wallet1,a b — wallet2.

W�a�ciwo�ci referencjiUżycie parametrów referencyjnych wiąże się z pewnymi specjalnymi warunkami, o których trzeba wiedzieć.Spójrzmy najpierw na listing 8.5. Używane są na nim dwie funkcje podnoszące parametr do sześcianu.Jedna pobiera parametr typu double, druga referencję double. Kod realizujący potęgowanie jest celowonieco zagmatwany, gdyż chodzi o dokładne pokazanie pewnej rzeczy.

Listing 8.5. cubes.cpp

// cubes.cpp -- parametry zwyk�e i referencyjne#include <iostream>double cube(double a);double refcube(double &ra);int main (){ using namespace std; double x = 3.0;

cout << cube(x); cout << " = sze�cian " << x << endl; cout << refcube(x); cout << " = sze�cian " << x << endl; return 0;}

double cube(double a){ a *= a * a; return a;}

Zmienne referencyjne 349

double refcube(double &ra){ ra *= ra * ra; return ra;}

Oto wynik działania programu z listingu 8.5:

27 = sze�cian 327 = sze�cian 27

Zwróćmy uwagę, że funkcja refcube() modyfikuje wartość zmiennej x z funkcji main(), a funkcjacube() nie; dlatego właśnie przekazywanie przez wartość jest standardem. Zmienna a jest inicjalizowanawartością x, ale zmiana a nie wpływa już na x. Funkcja refcube() korzysta z referencji, więc zmianywartości ra automatycznie przenoszą się na x. Jeśli funkcja ma używać przekazanych jej wartości, ale ichnie modyfikować, a korzystamy z referencji, należy skorzystać z referencji stałej. Oto przykład, jak powinnoto wyglądać w prototypie i nagłówku funkcji:

double refcube(const double &ra);

Jeśli zastosujemy taki zapis, przy próbie zmiany wartości ra kompilator zgłosi błąd.Tak naprawdę, jeśli chodzi o napisanie funkcji podobnej do pokazanej tutaj (to znaczy z użyciem

prostego typu liczbowego), zamiast z cokolwiek egzotycznego przekazywania przez referencję należyskorzystać ze zwykłego przekazania przez wartość. Parametry referencyjne stają się przydatne w przypadkuwiększych porcji danych, na przykład struktur i klas.

Funkcje przekazujące parametry przez wartość, jak cube() z listingu 8.5, mogą używać wielu rodzajówparametrów wywołania. Na przykład poprawne są wszystkie poniższe wywołania:

double z = cube(x + 2.0); // wyznaczenie x + 2.0, przekazanie warto�ciz = cube(8.0); // przekazanie warto�ci 8.0int k = 10;z = cube(k); // konwersja warto�ci k na typ double, przekazaniedouble yo[3] = { 2.2, 3.3, 4.4};z = cube(yo[2]); // przekazanie warto�ci 4.4

Załóżmy, że spróbowalibyśmy przekazać podobne parametry do funkcji korzystającej z parametrówreferencyjnych. Mogłoby się wydawać, że przekazywanie przez referencję jest bardziej ograniczone; w końcura jest alternatywną nazwą zmiennej, więc parametr wywołania powinien być zmienną. Zapis typu:

double z = refcube(x + 3.0); // nie powinno si� skompilowa�

nie wygląda zbyt dobrze, bo wyrażenie x + 3.0 nie jest zmienną. Na przykład nie można przypisaćtakiemu wyrażeniu wartości:

x + 3.0 = 5.0; // kompletne nieporozumienie

A co się stanie, jeśli spróbujemy użyć wywołania refcube(x + 3.0)? We współczesnym C++ jest tobłąd i większość kompilatorów go zgłosi. A niektóre starsze wygenerują ostrzeżenie typu:

Ostrze�enie: W wywo�aniu refcube(double &) jako parametru 'ra' u�yto warto�ci tymczasowej.

Powodem tak umiarkowanego protestu jest to, że dawniej C++ pozwalało przekazywać zmiennymreferencyjnym wyrażenia. Czasami nadal jest to możliwe. Skoro x + 3.0 nie jest zmienną typu double,program utworzy tymczasową zmienną bez nazwy i zainicjalizuje ją wyrażeniem x + 3.0. Parametr rastanie się referencją tej zmiennej tymczasowej. Przyjrzyjmy się teraz zmiennym tymczasowym bliżeji sprawdźmy, kiedy są tworzone, a kiedy nie.

350 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Zmienne tymczasowe, parametry referencyjne i constJęzyk C++ może wygenerować zmienną tymczasową, jeśli parametr wywołania nie pasuje do parametrureferencyjnego. Obecnie C++ dopuszcza to tylko w przypadku parametrów referencyjnych z modyfikatoremconst, ale i to nie zawsze. Przyjrzyjmy się teraz sytuacjom, w których C++ generuje zmienne tymczasowe,i zastanówmy się, czemu ograniczenie do referencji stałych jest słuszne.

Po pierwsze, kiedy jest tworzona zmienna tymczasowa? Jeśli parametr referencyjny jest stały,kompilator generuje zmienną tymczasową w dwojakich sytuacjach:

� kiedy parametr wywołania ma prawidłowy typ, ale nie jest l-wartością,

� kiedy parametr wywołania ma niewłaściwy typ, ale typ ten może być przekonwertowanyna właściwy.

Czym jest l-wartość? Otóż l-wartości są to takie obiekty danych, do których można się odwołać.Przykładami są zmienna, element tablicy, pole struktury, referencja czy wskaźnik, do którego zastosowanodereferencję. Nie są l-wartościami literały (poza ciągami w cudzysłowach, które są wewnętrzniereprezentowane jako adresy pamięci) ani złożone wyrażenia. Pojęcie l-wartości w C pierwotnieodnoszono do tych jednostek programu, które mogą wystąpić po lewej stronie operatora przypisania,ale później ta definicja uległa modyfikacji w wyniku wprowadzenia słowa kluczowego const. Obecniel-wartością nazwiemy zarówno zwyczajne zmienne modyfikowalne, jak i zmienne deklarowane ze słowemconst, ponieważ do obu można się odwołać przez adres. Z tym że zwyczajna zmienna może zostać określonajako l-wartość modyfikowalna, a zmienna deklarowana ze słowem const jako l-wartość niemodyfikowalna.

Wróćmy do przykładu; załóżmy, że zmienimy definicję refcube() tak, aby jako parametr pobierałastałą referencję:

double refcube(const double &ra){ return ra * ra * ra;}

Teraz weźmy następujący kod:

double side = 3.0;double * pd = &side;double & rd = side;long edge = 5L;double lens[4] = {2.0, 5.0, 10.0, 12.0};double c1 = refcube(side); // ra to sidedouble c2 = refcube(lens[2]); // ra to lens[2]double c3 = refcube(rd); // ra to rd to sidedouble c4 = refcube(*pd); // ra to *pd to sidedouble c5 = refcube(edge); // ra to zmienna tymczasowadouble c6 = refcube(7.0); // ra to zmienna tymczasowadouble c7 = refcube(side + 10.0); // ra to zmienna tymczasowa

Parametry side, lens[2], rd i *pd to obiekty danych typu double mające nazwy, więc można utworzyćdo nich referencje i zbędne są jakiekolwiek zmienne tymczasowe (przypomnijmy, że element tablicyzachowuje się jak zmienna odpowiedniego typu). Jednak już edge jest wprawdzie zmienną, ale niewłaściwegotypu. Referencja double nie może dotyczyć zmiennej long. Parametry 7.0 i side + 10.0 są z koleiwłaściwego typu, ale nie są nazwanymi obiektami danych. W każdym z tych przypadków kompilatorwygeneruje tymczasową zmienną anonimową i uczyni ra referencją do niej. Te zmienne tymczasoweistnieją tak długo, jak długo działa funkcja, ale później kompilator może je usunąć z pamięci.

Zmienne referencyjne 351

Czemu zatem działa to dobrze w przypadku referencji stałych, a inaczej nie? Przypomnijmy funkcjęswapr() z listingu 8.4:

void swapr(int & a, int & b) // uycie referencji{ int temp;

temp = a; // uycie a, b jako warto�ci zmiennych a = b; b = temp;}

Co by się stało, gdybyśmy w starym, bardziej liberalnym C++ wykonali poniższy kod?

long a = 3, b = 5;swapr(a, b);

Mamy do czynienia z niezgodnością typów, więc kompilator utworzyłby dwie tymczasowe zmienneint, zainicjalizował je wartościami 3 i 5, a następnie zamienił wartości obu tych zmiennych tymczasowych,pozostawiając a i b niezmienione.

Jeśli zadaniem funkcji mającej parametry referencyjne jest zmiana przekazanych wartości, tworzeniezmiennych tymczasowych to uniemożliwia. Rozwiązaniem jest uniemożliwienie tworzenia zmiennychtymczasowych w takich sytuacjach i to właśnie teraz jest zapisane w standardzie C++. Jednak niektórekompilatory w takiej sytuacji nadal generują ostrzeżenie, a nie błąd, należy więc zachować ostrożność.

Teraz zastanówmy się nad funkcją refcube(). Jej zadaniem jest po prostu użycie przekazanychwartości, a nie ich modyfikowanie. Wobec tego wykorzystanie zmiennych tymczasowych w niczymnie przeszkadza, a funkcja może obsłużyć szerszy wachlarz typów parametrów. Jeśli zatem z deklaracjiwynika, że referencja jest stała, C++ generuje w razie potrzeby zmienne tymczasowe. Funkcje C++z parametrami const, którym przekazano parametry wywołania innych typów, zachowują się podobniejak w przypadku przekazywania przez wartość, co oznacza niemożność zmiany oryginalnych danychi użycie tymczasowej zmiennej na wartość.

UwagaJe�li parametr wywo�ania funkcji nie jest l-warto�ci� lub nie pasuje co do typu do odpowiedniegosta�ego parametru referencyjnego, C++ tworzy zmienn� anonimow� w�a�ciwego typu i przypisuje jejwarto�� przekazanego parametru. Od tej chwili parametr odnosi si� do takiej zmiennej anonimowej.

Kiedy to tylko mo�liwe, nale�y u�ywa� const.

Istniej� trzy wa�kie powody deklarowania parametrów referencyjnych jako sta�ych:

� U�ycie const chroni nas przed b��dami programistycznymi powoduj�cymi niezamierzon�zmian� warto�ci.

� U�ycie const pozwala funkcji przetwarza� warto�ci sta�e i niesta�e, a pomini�cie w prototy-pie const oznacza, �e funkcja mo�e przetwarza� tylko dane nieb�d�ce sta�ymi.

� U�ycie referencji sta�ej pozwala funkcji generowa� tymczasowe zmienne i ich u�ywa�.

Parametry referencyjne nale�y deklarowa� jako sta�e zawsze, kiedy tylko jest to mo�liwe.

W C++11 wprowadzono drugi rodzaj referencji: referencję do r-wartości, która (nomen omen) możeodnosić się do r-wartości. Deklaruje się ją za pomocą symbolu &&:

double && rref = std::sqrt(36.00); // niedozwolone dla &double j = 15.0;double && jref = 2.0* j + 18.5; // niedozwolone dla &std::cout << rref << '\n'; // wypisanie 6.0std::cout << jref << '\n'; // wypisanie 48.5

352 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Referencja r-wartościowa została wprowadzona przede wszystkim dla umożliwienia tworzeniaefektywniejszych implementacji pewnych operacji w bibliotekach. W rozdziale 18. będziemy omawiaćsposób stosowania referencji r-wartości w implementacjach tak zwanej semantyki przeniesienia (ang. movesemantics); pierwotny typ referencyjny (ten z deklaracją z pojedynczym znakiem &) jest teraz określanymianem referencji l-wartościowej albo referencji do l-wartości.

U�ycie referencji do strukturReferencje świetnie współpracują ze strukturami i klasami. Początkowo referencje wprowadzono właśniena potrzeby tych dwóch typów, a nie na potrzeby wbudowanych typów podstawowych.

Referencji do struktur używa się w kontekście parametrów funkcji tak samo jak referencji do zmiennychtypów podstawowych — wystarczy w deklaracji parametru odnoszącego się do struktury dodać operatorreferencji, &. Weźmy dla przykładu następującą definicję struktury:

struct free_throws{ std::string name; int made; int attempts; float percent;};

Oraz funkcję operującą na strukturze takiego typu, udostępnianej przez referencję; jej prototypwyglądałby tak:

void set_pc(free_throws & ft); // referencja do struktury

Jeśli funkcja nie ma modyfikować wartości struktury, warto uzupełnić deklarację parametru słowemconst:

void display(const free_throws & ft); // nie zmienia warto�ci struktury

Właśnie tak działa program z listingu 8.6. Ciekawym rozwiązaniem jest też zwracanie przez funkcjęreferencji do struktury. Działa to nieco inaczej niż zwracanie zwykłej struktury i trzeba pamiętać o zasięguzwracanej referencji (wrócimy do tego niebawem).

Listing 8.6. strc_ref.cpp

// strc_ref.cpp -- uycie referencji do struktur#include <iostream>#include <string>struct free_throws{ std::string name; int made; int attempts; float percent;};

void display(const free_throws & ft);void set_pc(free_throws & ft);free_throws & accumulate(free_throws & target, const free_throws & source);

int main(){ // inicjalizacje cz��ciowe -- reszta sk�adowych jest zerowana

Zmienne referencyjne 353

free_throws one = {"Ifelsa Branch", 13, 14}; free_throws two = {"Andor Knott", 10, 16}; free_throws three = {"Minnie Max", 7, 9}; free_throws four = {"Whily Looper", 5, 9}; free_throws five = {"Long Long", 6, 14}; free_throws team = {"Throwgoods", 0, 0}; // bez inicjalizacji free_throws dup;

set_pc(one); display(one); accumulate(team, one); display(team);

// warto�ci zwracanych uyjemy w roli argumentów wywo�ania display(accumulate(team, two)); accumulate(accumulate(team, three), four); display(team);

// warto�ci zwracane uyte w przypisaniach: dup = accumulate(team,five); std::cout << "Statystyka dla team:\n"; display(team); std::cout << "Statystyka dla dup po przypisaniu:\n"; display(dup); set_pc(four);

// le skonstruowane przypisanie accumulate(dup,five) = four; std::cout << "Statystyka dla dup po omy�kowym przypisaniu:\n"; display(dup); return 0;}void display(const free_throws & ft){ using std::cout; cout << "Imi�: " << ft.name << '\n'; cout << "Trafionych: " << ft.made << '\t'; cout << "Rzutów: " << ft.attempts << '\t'; cout << "Skuteczno��: " << ft.percent << '\n';}void set_pc(free_throws & ft){ if (ft.attempts != 0) ft.percent = 100.0f *float(ft.made)/float(ft.attempts); else ft.percent = 0;}free_throws & accumulate(free_throws & target, const free_throws & source){ target.attempts += source.attempts; target.made += source.made; set_pc(target); return target;}

Oto wynik działania programu z listingu 8.6:

Imi�: Ifelsa BranchTrafionych: 13 Rzutów: 14 Skuteczno��: 92.8571Imi�: ThrowgoodsTrafionych: 13 Rzutów: 14 Skuteczno��: 92.8571

354 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Imi�: ThrowgoodsTrafionych: 23 Rzutów: 30 Skuteczno��: 76.6667Imi�: ThrowgoodsTrafionych: 35 Rzutów: 48 Skuteczno��: 72.9167Statystyka dla team:Imi�: ThrowgoodsTrafionych: 41 Rzutów: 62 Skuteczno��: 66.129Statystyka dla dup po przypisaniu:Imi�: ThrowgoodsTrafionych: 41 Rzutów: 62 Skuteczno��: 66.129Statystyka dla dup po omy�kowym przypisaniu:Imi�: Whily LooperTrafionych: 5 Rzutów: 9 Skuteczno��: 55.5556

Uwagi o programieProgram rozpoczyna się od inicjalizacji kilku struktur. Pamiętamy, że jeśli inicjalizator zawiera mniejelementów niż liczba składowych inicjalizowanej struktury, pozostałe składowe będą inicjalizowanezerami (tu wyzerowane zostaną składowe percent). Pierwsze wywołanie funkcji w programie to:

set_pc(one);

Ponieważ parametrem ft funkcji set_pc() jest referencja, w ciele funkcji nazwa ft odnosi się dostruktury one, a kod funkcji set_pc() ustawia wartość składowej one.percent. Przekazywanie przezwartość nie zadziała w tym przypadku, ponieważ funkcja zmieniłaby wtedy jedynie wartość składowejlokalnej (tymczasowej) kopii one. Alternatywą dla referencji jest (wiemy to z poprzedniego rozdziału)parametr typu wskaźnikowego, ale jest on odrobinę bardziej skomplikowany w obsłudze:

set_pcp(&one); // wersja z parametrem typu wskanikowego -- &one zamiast one...void set_pcp(free_throws * pt){ if (pt->attempts != 0) pt->percent = 100.0f *float(pt->made)/float(pt->attempts); else pt->percent = 0;}

Następne wywołanie funkcji w programie to:

display(one);

Ponieważ funkcja display() wypisuje zawartość struktury bez modyfikowania wartości jej składowych,w deklaracji parametru funkcji umieszczono słowo const, więc parametr jest niemodyfikowalną referencją.W tym przypadku akurat można by przekazać strukturę przez wartość, ale przekazywanie przez referencjęjest bardziej efektywne pod względem czasu wykonania i zajętości pamięci (przekazanie przez wartośćoznaczałoby wykonanie tymczasowej kopii argumentu dla potrzeb funkcji).

Kolejne wywołanie funkcji w programie:

accumulate(team, one);

Funkcja accumulate() przyjmuje dwa argumenty w postaci struktur. Jej zadaniem jest dodaniewartości składowych attempts i made drugiego argumentu do odpowiednich składowych pierwszegoargumentu. Modyfikowana jest więc tylko pierwsza z przekazanych struktur i reprezentujący ją parametrjest deklarowany jako zwykła referencja, a drugi parametr to referencja niemodyfikowalna (const):

free_throws & accumulate(free_throws & target, const free_throws & source);

Zmienne referencyjne 355

A co z wartością zwracaną? W omawianym wywołaniu funkcji nie jest ona używana, więc równiedobrze funkcja mogłaby deklarować typ zwracany jako void. Ale już następne wywołanie w programie to:

display(accumulate(team, two));

O co tu chodzi? Prześledźmy operacje na zmiennej strukturowej team. Jest ona pierwszym argumentemwywołania funkcji accumulate(), co oznacza, że w ciele funkcji jest widoczna jako obiekt target.Funkcja accumulate() modyfikuje zmienną team, a następnie zwraca referencję do tejże zmiennej.Zauważmy, że instrukcja zwrócenia wartości wygląda tak:

return target;

Nic tu jawnie nie sygnalizuje, że dochodzi do zwrócenia referencji. Wniosek ten pochodzi wyłączniez analizy nagłówka funkcji (a także jej prototypu):

free_throws & accumulate(free_throws & target, const free_throws & source)

Gdyby typ wartości zwracanej był deklarowany jako free_throws zamiast free_throws &, ta samainstrukcja return zwróciłaby kopię obiektu target (czyli kopię team). Ale skoro wartość zwracana jestreferencją, to znaczy, że wartość zwrócona z powyższego wywołania to obiekt team tożsamy z tymprzekazanym do wywołania accumulate().

A co dalej? Funkcja accumulate() zwraca wartość, która jest użyta jako pierwszy argumentwywołania funkcji display(), co oznacza, że funkcja display() otrzymuje w wywołaniu obiekt team.Ponieważ parametr funkcji display() jest referencją, w ciele funkcji nazwa ft odnosi się w istociedo obiektu team. Słowem, funkcja w tym wywołaniu wypisze zawartość team. W sumie instrukcja:

display(accumulate(team, two));

jest równoważna instrukcjom:

accumulate(team, two);display(team);

Analogiczna analiza dotyczy instrukcji:

accumulate(accumulate(team, three), four);

która mogłaby równie dobrze zostać zapisana jako sekwencja instrukcji:

accumulate(team, three);accumulate(team, four);

Następny wiersz programu używa operatora przypisania:

dup = accumulate(team, five);

Zgodnie z oczekiwaniem w dup ląduje kopia zawartości team.Na koniec program korzysta z funkcji accumulate() w sposób niezgodny z jej przeznaczeniem:

accumulate(dup, five) = four;

Taka instrukcja, to jest przypisanie wartości do wywołania funkcji, zostanie z powodzeniemskompilowana, ponieważ wartością zwracaną przez funkcję jest referencja. Gdyby funkcja accumulate()zwracała strukturę przez wartość, kod nie skompilowałby się. W przypadku referencji wartością zwracanąjest referencja do dup, co oznacza, że powyższa instrukcja jest tożsama z:

accumulate(dup, five); // dodanie danych five do dupdup = four; // zamazanie zawarto�ci dup zawarto�ci� four

356 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Druga instrukcja zasadniczo unieważnia wynik działania poprzedniej i choćby ze względu na tomożna sądzić, że pierwotne użycie accumulate() z przypisaniem do wartości zwracanej było niepoprawne.

Po co zwracać referencje?Wróćmy jeszcze do zagadnienia zwracania referencji z wywołania funkcji. W przypadku zwracaniazwyczajnej wartości mamy do czynienia z odpowiednikiem przekazywania parametru przez wartość:dochodzi do obliczenia wartości wyrażenia instrukcji return i przekazania obliczonej wartości domiejsca wywołania funkcji. Koncepcyjnie dochodzi wtedy do skopiowania wartości zwracanej dotymczasowej lokalizacji, w której jest dostępna dla programu wywołującego. Weźmy taki kod:

double m = sqrt(16.0);cout << sqrt(25.0);

W pierwszej instrukcji dochodzi do skopiowania wartości 4.0 do tymczasowego obiektu, którynastępnie jest kopiowany do zmiennej m. W drugiej instrukcji wartość 5.0 jest również kopiowana dolokalizacji tymczasowej, z której jest następnie przekazywana do cout (to opis koncepcyjny; w praktycekompilatory optymalizujące wywołania mogą skonsolidować poszczególne kroki zwracania wartościi eliminować operacje kopiowania). Teraz weźmy instrukcję:

dup = accumulate(team,five);

Gdyby accumulate() zwracała strukturę zamiast referencji do struktury, oznaczałoby to skopiowaniecałej struktury do tymczasowej lokalizacji, a stamtąd do zmiennej dup. Ale przy zwracaniu referencjimamy bezpośrednie kopiowanie team do dup, co jest operacją dużo efektywniejszą.

UwagaFunkcja zwracaj�ca referencj� jest tak naprawd� aliasem zmiennej, której referencja dotyczy.

Ostrożnie ze zwracaniem referencjiNajważniejsze, co wiąże się ze zwracaniem referencji, to unikanie zwracania referencji do obszarupamięci, który zostanie zwolniony w chwili zakończenia wykonywania funkcji. Chcemy uniknąć kodupodobnego do poniższego:

const free_throws & clone2(free_throws & ft){ free_throws newguy; // pierwszy krok ku powanemu b��dowi newguy = ft; // kopiowanie danych return newguy; // zwracanie referencji do kopii}

Pojawi się tu niefortunny efekt zwracania referencji do zmiennej tymczasowej newguy, która przestanieistnieć w chwili zakończenia wykonywania funkcji (w rozdziale 9. omówimy trwałość różnego rodzajuzmiennych). Analogicznie trzeba unikać zwracania wskaźników na takie zmienne.

Najprostszym sposobem uniknięcia opisanego problemu jest zwracanie referencji przekazanejwcześniej funkcji jako parametru. Parametr referencyjny odnosi się do danych używanych przez funkcjęwywołującą, wobec czego zwracana referencja odniesie się do tych samych danych. Tak działa funkcjaaccumulate() z listingu 8.6.

Druga metoda to alokacja pamięci operatorem new. Widzieliśmy już przykłady, gdzie operator newrezerwował miejsce na łańcuch, a funkcja zwracała wskaźnik na to miejsce. Oto jak można zrobić cośtakiego z referencjami:

Zmienne referencyjne 357

const free_throws & clone(free_throws & ft){ free_throws * pt = new free_throws(); *pt = ft; // kopiowanie informacji return *pt; // zwrócenie referencji do kopii}

Pierwsza instrukcja tworzy strukturę free_throws bez nazwy. Wskaźnik pt wskazuje strukturę, więc*pt jest strukturą. Wydaje się, że zwracana jest struktura, ale deklaracja funkcji mówi, że tak naprawdęzwracana jest referencja do struktury. Można byłoby zatem funkcji tej użyć następująco:

free_throws & jolly = clone(three);

Powoduje to, że jolly jest referencją do nowej struktury. Istnieje tu jednak pewien problem —należałoby użyć delete do zwolnienia pamięci zaalokowanej przez new, kiedy nie będzie już potrzebna.Ale wywołanie clone() ukrywa użycie operatora new, a zwracany obiekt jest referencją, a nie wskaźnikiem,przez co łatwo zapomnieć o konieczności późniejszego użycia delete. W tego typu przypadkachnieocenionym ułatwieniem są szablony automatyzujące zwalnianie pamięci, takie jak omawianew rozdziale 16. szablony auto_ptr albo jeszcze lepiej unique_ptr z C++11.

Po co używać const przy zwracaniu referencji?Na listingu 8.6 występowała taka instrukcja:

accumulate(dup,five) = four;

W jej wyniku najpierw zakumulowaliśmy wartość five w strukturze dup tylko po to, żeby po wyjściuz funkcji stracić obliczone wartości poprzez nadpisanie wartości dup wartością zmiennej four. Dlaczegow ogóle udało się skompilować taką instrukcję? Otóż przypisanie wymaga modyfikowalnej l-wartości polewej stronie: wyrażenie po lewej stronie operatora przypisania powinno identyfikować blok pamięci,który można modyfikować. W tym przypadku funkcja zwracała referencję do dup, a taka referencjaidentyfikuje modyfikowalny blok pamięci, więc cała instrukcja jest dla kompilatora poprawna.

Zwyczajne (niereferencyjne) typy wartości zwracanych to z kolei r-wartości, które nie mają adresu(nie identyfikują bloku pamięci). Takie wyrażenia mogą występować po prawej stronie operatora przypisania,ale nie można ich umieszczać po stronie lewej. Do r-wartości zaliczamy też literały takie jak 10.0 orazwyrażenia w rodzaju x + y. Jest jasne, że próby pobrania adresu literału w rodzaju 10.0 nie mają sensu,ale dlaczego zwyczajna wartość zwracana z funkcji również jest r-wartością? Otóż wartość zwracana,dla przypomnienia, jest umieszczana w tymczasowej lokalizacji pamięci, która niekoniecznie istniejechoćby do końca wykonywania bieżącej instrukcji.

Załóżmy, że zamierzamy użyć referencji jako wartości zwracanej, ale chcielibyśmy zapobiecniepożądanym przypadkom użycia, takim jak przypisanie do wartości zwracanej z funkcji accumulate().Aby to zrobić, wystarczy zadeklarować typ wartości zwracanej jako referencję niemodyfikowalną(ze słowem const):

const free_throws &accumulate(free_throws & target, const free_throws & source);

Wartość zwracana jest teraz l-wartością, ale niemodyfikowalną, i instrukcja przypisania do wartościzwracanej staje się niepoprawna składniowo:

accumulate(dup,five) = four; // niedozwolone dla niemodyfikowalnej referencji zwracanej z funkcji

358 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

A co z pozostałymi wywołaniami funkcji w programie? Nawet po oznaczeniu typu wartości zwracanejsłowem const poniższa instrukcja wciąż będzie poprawna:

display(accumulate(team, two));

Poprawność wynika z tego, że parametr funkcji display() jest również deklarowany jako referencjaniemodyfikowalna (const free_thows &). Ale już poniższe wywołanie będzie niepoprawne, ponieważpierwszy parametr accumulate() to referencja modyfikowalna:

accumulate(accumulate(team, three), four);

Czy to duże utrudnienie? Nie w tym przypadku, skoro powyższe wywołanie można łatwo przepisaćna sekwencję:

accumulate(team, three);accumulate(team, four);

I rzecz jasna wciąż mamy możliwość używania wartości zwracanej funkcji accumulate() po prawejstronie instrukcji przypisania.

Pominięcie const pozwala pisać krótszy, ale trudniejszy w zrozumieniu kod.Zwykle lepiej unikać włączania do programu trudnych do zrozumienia cech, które później stają się

przyczynkiem do powstawania trudnych do zrozumienia błędów. Uczynienie typu zwracanego referencjąstałą chroni nas przed pokusą nieeleganckiego pójścia na skróty. Czasami warto też pominąć słowoconst; przykładem tego jest omawiany w rozdziale 11. przeciążony operator <<.

U�ycie referencji z obiektamiKlasyczną praktyką C++ jest przekazywanie do funkcji obiektów przez referencje. Referencji używa sięna przykład w przypadku funkcji, którym przekazuje się obiekty klas string, ostream, istream, ofstreamoraz ifstream.

Przyjrzyjmy się przykładowi, w którym użyto klasy string oraz pokazano pewne techniki programowania— niekoniecznie godne naśladowania. Chodzi o stworzenie funkcji dodającej podany łańcuch na koniecinnego łańcucha. Listing 8.7 zawiera trzy funkcje, które realizują to zadanie, jedna z tych funkcji jest jednakźle zdefiniowana i może spowodować awarię programu, a nawet program może się nie skompilować.

Listing 8.7. strquote.cpp

// strquote.cpp -- róne techniki programowania#include <iostream>#include <string>using namespace std;string version1(const string & s1, const string & s2);const string & version2(string & s1, const string & s2); // efekty uboczneconst string & version3(string & s1, const string & s2); // z�a technika

int main(){ string input; string copy; string result;

cout << "Podaj �a�cuch: "; getline(cin, input); copy = input; cout << "Wprowadzony �a�cuch: " << input << endl;

Zmienne referencyjne 359

result = version1(input, "***"); cout << "a�cuch po wzbogaceniu: " << result << endl; cout << "Oryginalny �a�cuch: " << input << endl;

result = version2(input, "###"); cout << "a�cuch po wzbogaceniu: " << result << endl; cout << "Oryginalny �a�cuch: " << input << endl;

cout << "Przywrócenie oryginalnego �a�cucha.\n"; input = copy; result = version3(input, "@@@"); cout << "a�cuch po wzbogaceniu: " << result << endl; cout << "Oryginalny �a�cuch: " << input << endl;

return 0;}

string version1(const string & s1, const string & s2){ string temp;

temp = s2 + s1 + s2; return temp;}

const string & version2(string & s1, const string & s2) // efekty uboczne{ s1 = s2 + s1 + s2;// mona bezpiecznie zwróci� referencj� przekazan� do funkcji return s1;}

const string & version3(string & s1, const string & s2) // z�a technika{ string temp;

temp = s2 + s1 + s2;// zwracanie referencji do obiektu lokalnego jest niebezpieczne return temp;

}

Oto wynik uruchomienia programu z listingu 8.7:

Podaj �a�cuch: To nie moja wina.Wprowadzony �a�cuch: To nie moja wina.a�cuch po wzbogaceniu: ***To nie moja wina.***Oryginalny �a�cuch: To nie moja wina.a�cuch po wzbogaceniu: ###To nie moja wina.###Oryginalny �a�cuch: ###To nie moja wina.###Przywrócenie oryginalnego �a�cucha.

W tym momencie działanie programu jest przerywane.

Uwagi o programieWersja 1. funkcji z listingu 8.7 jest najprostsza:

string version1(const string & s1, const string & s2){ string temp;

360 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

temp = s2 + s1 + s2; return temp;}

Funkcja ta ma dwa parametry typu string. Do tworzenia nowego łańcucha mającego pożądanewłaściwości też używamy klasy string. Zauważmy, że oba parametry funkcji są referencjami stałymi.Funkcja ostatecznie da taki sam wynik, jaki dałaby, gdybyśmy przekazali jej obiekty string:

string version4(string s1, string s2) // zadzia�a�aby tak samo

W tym przypadku s1 i s2 byłyby całkiem nowymi obiektami string. Zatem użycie referencji jestwydajniejsze, gdyż nie trzeba tworzyć nowych obiektów i kopiować do nich danych z obiektów oryginalnych.Użycie kwalifikatora const wskazuje, że funkcja użyje oryginalnych łańcuchów, ale nie będzie ichmodyfikowała.

Obiekt temp to nowy obiekt lokalny dla funkcji version1(), który znika w chwili zakończenia działaniafunkcji. Zwrócenie temp jako referencji nie zadziała, więc funkcja jest typu string. Wobec tego zawartośćzmiennej temp zostanie skopiowana w tymczasowe miejsce przeznaczone na wartości zwracane. Dalej,w funkcji main(), treść zwracanej wartości jest kopiowana do łańcucha o nazwie result:

result = version1(input, "***");

Przekazywanie �a�cucha w formacie C jako referencji obiektu stringBy� mo�e cz��� czytelników zauwa�y�a pewn� ciekaw� cech� funkcji version1(): oba parametry,s1 i s2, s� typu const string &, ale przekazane parametry aktualne, input i "***", s� odpowiedniotypów string i const char *. Parametr input jest typu string, wi�c bezproblemowo mo�e si� doniego odwo�ywa� s1. Jak jednak program ma potraktowa� przekazanie wska�nika char jako referencjido obiektu string?

Dziej� si� tutaj dwie rzeczy. Po pierwsze, klasa string zawiera definicj� konwersji typu char* natyp string, dzi�ki czemu mo�liwa jest inicjalizacja obiektu string �acuchem w formacie j�zykaC. Po drugie, trzeba pami�ta� o omawianej wcze�niej w tym rozdziale w�a�ciwo�ci referencjiconst u�ywanych jako parametry. Za�ó�my, �e typ argumentu nie pasuje do typu referencji, alemo�liwa jest odpowiednia konwersja. W takiej sytuacji program utworzy tymczasow� zmienn�odpowiedniego typu, zainicjalizuje j� przekonwertowan� warto�ci� i przeka�e uzyskan� takreferencj� do zmiennej tymczasowej. Wcze�niej w tym rozdziale widzieli�my na przyk�ad, �eparametr const double & mo�e tak obs�u�y� parametr typu int. Analogicznie parametr conststring & mo�e obs�u�y� w ten sposób parametr char * lub const char *.

Wynika st�d, �e je�li parametr jest typu const string &, argument u�yty w wywo�aniu funkcjimo�e by� obiektem string lub �acuchem w formacie C — litera�em uj�tym w cudzys�ów,tablic� znakow� lub wska�nikiem char. Dlatego prawid�owo zadzia�a wywo�anie:

result = version1(input, "***");

Funkcja version2() nie tworzy tymczasowego obiektu string, ale bezpośrednio modyfikuje łańcuchoryginalny:

const string & version2(string & s1, const string & s2) // efekty uboczne{ s1 = s2 + s1 + s2;// mona bezpiecznie zwróci� referencj� przekazan� do funkcji return s1;}

Funkcja może zmieniać s1, gdyż parametr ten, w przeciwieństwie do s2, został zadeklarowany bezsłowa kluczowego const.

Zmienne referencyjne 361

Parametr s1 jest referencją obiektu z funkcji main() (obiektu input) i można bezpiecznie zwrócić s1jako referencję. Skoro s1 jest referencją zmiennej input, wiersz:

result = version2(input, "###");

jest równoważny następującemu zapisowi:

version2(input, "###"); // input zmienione bezpo�rednio w version2()result = input; // referencja do s1 to referencja do input

Jednak, jako że s1 jest referencją do input, wywołanie tej funkcji ma efekt uboczny w postaci zmianytakże obiektu input:

Oryginalny �a�cuch: To nie moja wina.a�cuch po wzbogaceniu: ###To nie moja wina.###Oryginalny �a�cuch: ###To nie moja wina.###

Jeśli zatem nie chcemy, aby oryginalny łańcuch był modyfikowany, taka technika projektowaniajest nieprawidłowa.

Trzecia wersja funkcji z listingu 8.7 stanowi ostrzeżenie, czego nie należy robić:

const string & version3(string & s1, const string & s2) // z�a technika{ string temp;

temp = s2 + s1 + s2;// zwracanie referencji do obiektu lokalnego jest niebezpieczne return temp;}

Pojawia się tu poważny błąd polegający na zwracaniu referencji do zmiennej zadeklarowanej lokalniew funkcji version3(). Funkcja daje się skompilować (nawet bez ostrzeżeń)2, ale przy próbie wykonaniatej funkcji działanie programu jest przerywane. Powodem błędu jest przypisanie:

result = vesion3(input, "@@@");

w którym program próbuje odwołać się do nieużywanego już obszaru pamięci.

Obiekty po raz wtóry — obiekty, dziedziczenie i referencjeKlasy ostream i ofstream mają ciekawą cechę bycia referencjami „wprzód”. Jak mówiliśmy w rozdziale6., obiekty typu ofstream mogą używać metod ostream, co pozwala na wprowadzanie i zapisywaniedanych z i do pliku w takiej samej postaci, jak to się robi w przypadku konsoli. Cechą języka, któraumożliwia przekazywanie właściwości jednej klasy innej klasie, jest dziedziczenie, dokładnie omówionew rozdziale 13. Na razie tylko kilka słów wstępu: klasa ostream to klasa bazowa (bo klasa ofstreamoparta jest na niej), a klasa ofstream to klasa pochodna. Klasa pochodna dziedziczy metody klasy bazowej,co znaczy, że obiekt ofstream może korzystać z takich cech klasy bazowej jak metody formatująceprecision() czy setf().

Innym aspektem dziedziczenia jest to, że referencja klasy bazowej może odnosić się do obiektu klasypochodnej bez konieczności rzutowania. Praktyczny efekt jest taki, że można zdefiniować funkcjęmającą jako parametr referencję klasy bazowej i funkcja ta będzie mogła używać w tym parametrze 2 Tak naprawdę zależy to od kompilatora — na przykład w wersji 6. kompilatora Borland na etapie kompilacji zgłaszany

jest błąd Attempting to return a reference to local variable 'temp', czyli Próba zwrócenia referencji do zmiennej lokalnej 'temp'— przyp. tłum.

362 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

obiektów tak klasy bazowej, jak i pochodnej. Na przykład funkcja z parametrem typu ostream & możeodebrać obiekt ostream (choćby cout) lub obiekt ofstream (zadeklarowany przez nas).

Na listingu 8.8 pokazano to na przykładzie jednej funkcji, która zapisuje dane w pliku i na ekranie;zmienia się jedynie parametr wywołania tej funkcji. Program prosi o podanie ogniskowej obiektywu(głównego lustra lub soczewki) oraz pewnych cech okularu teleskopu. Powiększenie to długość ogniskowejpodzielona przez ogniskową okularu, więc obliczenia są proste. Program korzysta też z metod formatowania,które — jak to już zostało zapowiedziane — działają równie dobrze z obiektem cout, jak i z obiektamiklasy ofstream (w tym przypadku fout).

Listing 8.8. filefunc.cpp

//filefunc.cpp -- funkcja korzystaj�ca z parametru ostream &#include <iostream>#include <fstream>#include <cstdlib>using namespace std;

void file_it(ostream & os, double fo, const double fe[],int n);const int LIMIT = 5;int main(){ ofstream fout; const char * fn = "ep-data.txt"; fout.open(fn); if (!fout.is_open()) { cout << "Nie mog� otworzy� " << fn << ". Do widzenia.\n"; exit(EXIT_FAILURE); } double objective; cout << "Podaj ogniskow� teleskopu w milimetrach:"; cin >> objective; double eps[LIMIT]; cout << "Podaj ogniskowe (w mm) " << LIMIT << " okularów:\n"; for (int i = 0; i < LIMIT; i++) { cout << "Okular nr " << i + 1 << ": "; cin >> eps[i]; } file_it(fout, objective, eps, LIMIT); file_it(cout, objective, eps, LIMIT); cout << "Gotowe\n"; return 0;}

void file_it(ostream & os, double fo, const double fe[],int n){ ios_base::fmtflags initial; initial = os.setf(ios_base::fixed); // zachowaj formatowanie pocz�tkowe os.precision(0); os << "Ogniskowa obiektywu: " << fo << " mm\n"; os.setf(ios::showpoint); os.precision(1); os.width(12); os << "f okularu"; os.width(15); os << "powi�kszenie" << endl; for (int i = 0; i < n; i++) { os.width(12);

Zmienne referencyjne 363

os << fe[i]; os.width(15); os << int (fo/fe[i] + 0.5) << endl; } os.setf(initial); // przywrócenie pocz�tkowych ustawie� formatowania}

Oto wynik uruchomienia programu z listingu 8.8:

Podaj ogniskow� teleskopu w milimetrach: 1800Podaj ogniskowe (w mm) 5 okularów:Okular nr 1: 30Okular nr 2: 19Okular nr 3: 14Okular nr 4: 8.8Okular nr 5: 7.5Ogniskowa obiektywu: 1800 mm f okularu powi�kszenie 30.0 60 19.0 95 14.0 129 8.8 205 7.5 240Gotowe

Wiersz:

file_it(fout, objective, eps, LIMIT);

powoduje wypisanie danych okularu do pliku ep-data.txt, a wiersz:

file_it(cout, objective, eps, LIMIT);

wypisanie tych samych danych na ekranie.

Uwagi o programieNajważniejsze w kodzie z listingu 8.8 jest to, że parametr os typu ostream & może odnosić się do obiektuklasy ostream, jak cout, oraz do obiektu klasy ofstream, jak fout. Jednak program ten pokazuje także to,jak w obu wzmiankowanych typach obiektów można używać metod formatowania klasy ostream.Przejrzymy te metody, a niektóre z nich krótko omówimy; więcej szczegółów na ten temat pojawi sięw rozdziale 17.

Metoda setf() pozwala ustawiać różne stany związane z formatowaniem. Na przykład wywołaniesetf(ios_base::fixed) ustawia obiekt w tryb dziesiętnego zapisu stałoprzecinkowego. Wywołaniesetf(ios_base:showpoint) ustawia obiekt w tryb pokazywania końcowej kropki dziesiętnej, nawetjeśli za nią są same zera. Metoda precision() określa liczbę cyfr pokazywanych na prawo od kropkidziesiętnej (o ile obiekt jest w trybie fixed). Wszystkie te ustawienia obowiązują do momentu, aż zostanąw kolejnym wywołaniu zmienione. Metoda width() ustala szerokość pola użytą przy następnym pokazywaniudanych. Ustawienie to dotyczy pojedynczej wartości, po czym jest przywracane ustawienie domyślne(a domyślnie pole ma szerokość zero, czyli jest rozszerzane tak, aby akurat pomieściło podaną wartość).

Funkcja file_it() korzysta z ciekawej pary wywołań metod:

ios_base::fmtflags initial;initial = os.setf(ios_base::fixed); // zapami�tanie aktualnego stanu formatowania...os.setf(initial); // odtworzenie pocz�tkowego stanu formatowania

364 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Metoda setf zwraca kopię wszystkich ustawień formatowania, które obowiązywały przed tymwywołaniem. ios_base::fmtflags to wymyślna nazwa typu potrzebnego do zapisania tych informacji.Wobec tego przypisanie zmiennej initial powoduje zapisanie ustawień, które obowiązywały przedwywołaniem funkcji file_it(). Zmienna initial może być później używana jako parametr setf()w celu wyzerowania wszystkich ustawień formatowania na wartości oryginalne. Wobec tego funkcjaprzywraca obiekt do stanu, w jakim znajdował się on przed przekazaniem go funkcji file_it().

Szersza znajomość klas pomoże zrozumieć działanie tych metod oraz powód, dla którego naprzykład stale pojawia się ios_base. Jednak aby użyć tych metod, nie musimy czekać do rozdziału 17.

I na koniec jeszcze jedna uwaga: każdy obiekt ma własne ustawienia formatowania. Kiedy programprzekazuje cout do funkcji file_it(), ustawienia cout są zmieniane, a potem odtwarzane. Gdy programprzekazuje funkcji file_it() obiekt fout, zmiana ustawień i ich odtwarzanie dotyczą obiektu fout.

Kiedy korzysta z referencji jako parametrów?Istnieją dwa zasadnicze powody używania parametrów referencyjnych:

� umożliwienie modyfikacji danych w wywoływanej funkcji,� przyspieszenie wykonywania programu dzięki przekazaniu samej referencji zamiast całego obiektu

danych.

Drugi powód jest szczególnie istotny w przypadku większych obiektów danych, jak strukturyi egzemplarze klas. Obydwa podane powody dotyczą także przekazywania przez wskaźniki. Nie dziejesię tak bez powodu — referencje są pewnego rodzaju interfejsem do wskaźników. Kiedy zatem używaćreferencji, kiedy wskaźników, a kiedy przekazywać parametry przez wartość? Oto garść wskazówek.

Funkcja korzysta z przekazanych danych, ale ich nie modyfikuje:� Jeśli obiekt danych jest mały, na przykład jest to wbudowany typ danych lub mała struktura,

należy go przekazać przez wartość.� Jeśli obiekt danych jest tablicą, nie mamy wyboru — musimy użyć wskaźnika. Wskaźnik

powinien być typu const.� Jeśli obiekt danych jest duży, należy użyć wskaźnika const lub referencji const, aby w ten sposób

zwiększyć szybkość działania programu. Zaoszczędzimy na czasie i pamięci, gdyż nie trzebabędzie kopiować struktury czy klasy.

� Jeśli obiekt danych jest egzemplarzem klasy, należy użyć referencji const. Semantyka klas częstowymaga użycia referencji — to właśnie było głównym powodem dodania referencji do C++.Wobec tego standardowym sposobem przekazywania egzemplarzy klas jest użycie referencji.

Funkcja modyfikuje przekazane dane:� Jeśli obiekt danych jest typem wbudowanym, należy użyć wskaźnika. Jeśli natkniemy się gdzieś

w kodzie na zapis poprawto(&x), gdzie x jest typu int, będzie dość oczywiste, że zadaniem funkcjijest modyfikacja wartości x.

� Jeśli obiekt danych jest tablicą, nie mamy wyboru i używamy wskaźnika.� Jeśli obiekt danych jest strukturą, używamy referencji lub wskaźnika.� Jeśli obiekt danych jest egzemplarzem klasy, używamy referencji.

Są to oczywiście tylko wytyczne i bywa, że konieczne jest dokonanie wyboru innego niż tu sugerowany.Na przykład obiekt cin korzysta z referencji do typów podstawowych, więc zamiast cin >> &n możnaużyć zapisu cin >> n.

Parametry domy�lne 365

Parametry domy�lneTeraz zajmijmy się kolejną sztuczką możliwą do zastosowania w C++ — parametrami domyślnymi.Parametr domyślny to wartość używana w przypadku, kiedy w wywołaniu funkcji zabraknie wskazanegoparametru. Jeśli na przykład ustawimy funkcję void wow(int n) tak, aby parametr n miał wartość domyślną 1,to wywołanie wow() będzie równoważne wywołaniu wow(1). Zwiększa to elastyczność użycia funkcji.Załóżmy, że mamy funkcję left() zwracającą pierwszych n znaków łańcucha, przy czym parametramisą łańcuch i n. Ściślej rzecz biorąc, funkcja zwraca wskaźnik na nowy łańcuch zawierający wybranyfragment łańcucha wejściowego. Przykładowo wywołanie left("teoria", 3) powoduje utworzeniełańcucha "teo"; zwracany jest wskaźnik na ten nowy łańcuch. Załóżmy teraz, że ustaliliśmy, iż wartościądomyślną drugiego parametru jest 1. Wywołanie left("teoria", 3) zadziała jak dotąd, gdyż przekazanawartość 3 nadpisze wartość domyślną. Jednak już wywołanie left("teoria") nie spowoduje błędu,lecz zwróci wskaźnik na łańcuch "t". Tego typu wartości domyślne są przydatne, jeśli program częstopobiera łańcuchy jednoznakowe, ale czasami także dłuższe.

Jak ustalić wartość domyślną? Trzeba to zrobić w prototypie funkcji. Kompilator odszukuje prototypfunkcji w celu sprawdzenia, ile dana funkcja ma parametrów, więc także w prototypie jest miejsce naparametry domyślne. Wystarczy w prototypie przypisać wartość wybranemu parametrowi i voilá!Oto przykładowy prototyp funkcji left():

char * left(const char * str, int n = 1);

Chcemy, aby nasza funkcja zwracała nowy łańcuch — stąd jej typem jest char*, czyli wskaźnik char.Pierwotny łańcuch ma być niezmieniany, dlatego używamy słowa kluczowego const. Parametr n mamieć wartość domyślną 1, dlatego parametrowi temu przypisujemy jedynkę. Parametr domyślny zachowujesię jak wartość inicjalizująca — czyli w powyższym przykładzie zmienna n jest inicjalizowana wartością1. Jeśli nie zmienialibyśmy n, wartością tej zmiennej pozostałoby 1, ale jeżeli przekażemy funkcji drugiparametr, zostanie on przypisany do n i nadpisze 1.

Kiedy używamy funkcji z listą parametrów, musimy wartości domyślne dodawać od strony prawejdo lewej. Nie można zatem użyć niektórych parametrów domyślnych, jeśli za nimi są parametry bezwartości domyślnych:

int harpo(int n, int m = 4, int j = 5); // DOBRZEint chico(int n, int m = 6, int j); // �LEint groucho(int k = 1, int m = 2, int n = 3); // DOBRZE

Przy prototypie funkcji harpo() jak powyżej można później wywoływać tę funkcję z jednym,dwoma lub trzema parametrami:

beeps = harpo(2); // równowane harpo(2,4,5)beeps = harpo(1,8); // równowane harpo(1,8,5)beeps = harpo(8,7,6); // nie b�d� uyte adne parametry domy�lne

Argumenty są przypisywane odpowiednim parametrom od lewej strony do prawej; nie można żadnychz nich pomijać. Wobec tego poniższy zapis jest niedozwolony:

beeps = harpo(3, ,8); // �LE, m nie zostanie ustawione na 4

Parametry domyślne nie są jakimś wielkim postępem w programowaniu; są po prostu wygodne.Kiedy zaczniemy pracę z klasami, okaże się, że parametry takie pozwalają ograniczyć liczbę konstruktorów,metod oraz metod przeciążonych.

Na listingu 8.9 pokazano użycie parametrów domyślnych. Jak widać, parametry domyślne występujątylko w prototypie. Definicja funkcji jest taka sama jak bez parametrów domyślnych.

366 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Listing 8.9. left.cpp

// left.cpp -- funkcja obs�ugi �a�cuchów z parametrem domy�lnym#include <iostream>const int ArSize = 80;char * left(const char * str, int n = 1);int main(){ using namespace std; char sample[ArSize]; cout << "Podaj �a�cuch znakowy:\n"; cin.get(sample,ArSize); char *ps = left(sample, 4); cout << ps << endl; delete [] ps; // zwolnienie starego �a�cucha ps = left(sample); cout << ps << endl; delete [] ps; // zwolnienie nowego �a�cucha return 0;}

// funkcja zwraca wskanik na nowy �a�cuch sk�adaj�cy si�// z pierwszych n znaków �a�cucha strchar * left(const char * str, int n){ if(n < 0) n = 0; char * p = new char[n+1]; int i; for (i = 0; i < n && str[i]; i++) p[i] = str[i]; // kopiowanie znaków while (i <= n) p[i++] = '\0'; // wyzerowanie reszty �a�cucha return p;}

Oto wynik uruchomienia programu z listingu 8.9:

Podaj �a�cuch znakowy:bielactwobielb

Uwagi o programieProgram z listingu 8.9 korzysta z operatora new, aby stworzyć nowy łańcuch na wybrane znaki. Może sięzdarzyć, że złośliwy użytkownik poda ujemną liczbę znaków, a wtedy funkcja ustawi tę liczbę na 0 i zwrócipusty łańcuch. Inna możliwość to podanie przez nieodpowiedzialnego użytkownika większej liczbyznaków, niż ma łańcuch. Przed tym funkcja chroni się następująco:

i < n && str[i]

Warunek i < n powoduje przerwanie pętli po skopiowaniu n znaków. Drugi warunek, str[i],dotyczy kodu kopiowanego znaku. Jeśli pętla dojdzie do znaku NUL o kodzie 0, działanie pętli zostanieprzerwane. Ostatnia pętla while kończy łańcuch znakiem NUL i wypełnia znakiem NUL pozostałązaalokowaną pamięć.

Inną metodą ustalania wielkości nowego łańcucha jest ustawienie n na mniejszą z dwóch wartości— wartości przekazanej oraz długości łańcucha:

Przeci��anie funkcji 367

int len = strlen(str);n = (n < len) ? n : len; // mniejsza z n i lenchar * p = new char[n+1];

W ten sposób zapewniamy, że new nie zaalokuje więcej miejsca, niż jest potrzebne na łańcuch.Może to być przydatne w przypadku wywołań typu left("Cze��!", 32767). W pierwszym rozwiązaniuskopiujemy napis "Cze��!" do tablicy mającej 32 767 znaków, przy czym wszystkie te znaki pozapierwszymi sześcioma ustawimy na zero. Drugie rozwiązanie powoduje skopiowanie tego samegonapisu do tablicy 7-znakowej. Jednak dodanie kolejnego wywołania funkcji, strlen(), powodujezwiększenie programu, spowolnienie jego działania i wymaga włączenia pliku nagłówkowego cstring(lub string.h). Programiści C zawsze dążyli do zapewnienia jak najszybszego działania swoich programów,do bardziej zwartego kodu i do składania na barki programisty odpowiedzialności za prawidłowewywoływanie funkcji. Jednak w C++ większy nacisk kładzie się na niezawodność. Ostatecznie powolny,ale dobrze działający program jest lepszy niż program działający szybciej, lecz z błędami. Jeśli czaspotrzebny na wywołanie funkcji strlen() okaże się problemem, możemy funkcji left() umożliwićbezpośrednie wykrycie, czy mniejsze jest n, czy długość łańcucha:

int m = 0;while ( m <= n && str[m] != '\n') m++;char * p = new char[m+1];// w reszcie kodu naley uy� m zamiast n

Pamiętajmy, że wyrażenie str[m] != '\0' otrzymuje wartość true dla każdego str[m] różnego odznaku pustego i false, kiedy str[m] to znak pusty. W wyrażeniach logicznych && wartości niezerowe sąkonwertowane na wartość logiczną true, a warunek pętli while można zapisać również tak:

while (m<=n && str[m])

Przeci��anie funkcjiPolimorfizm funkcji to bardzo eleganckie rozszerzenie C++ względem C. O ile parametry domyślnepozwalają wywoływać tę samą funkcję z różnymi zestawami parametrów, o tyle polimorfizm funkcji(nazywany też przeciążaniem funkcji) pozwala używać wielu funkcji o takiej samej nazwie. Słowopolimorfizm oznacza posiadanie wielu postaci, zatem polimorfizm funkcji umożliwia funkcji przybieraniewielu form. Z kolei termin przeciążanie funkcji oznacza możliwość wiązania z jedną nazwą wielu funkcji— czyli nazwa jest przeciążana. Oba podane tu pojęcia oznaczają to samo, ale częściej mówi się o przeciążaniufunkcji — chyba dlatego, że bardziej kojarzy się z ciężką pracą. Korzystając z przeciążania, możnazdefiniować całą rodzinę funkcji realizujących w zasadzie te same zadania, ale na różnych zestawachparametrów.

Przeciążone funkcje przypominają czasowniki mające więcej niż jedno znaczenie. Na przykładmożna kopać piłkę do bramki i można kopać w ziemi w poszukiwaniu skarbu. Kontekst pozwala(przynajmniej powinien pozwolić) określić, o jakie kopanie chodzi. Analogicznie w C++ na podstawiekontekstu określa się, której wersji przeciążonej funkcji należy użyć.

Kluczem do przeciążania funkcji jest lista parametrów, nazywana też sygnaturą funkcji. Jeśli dwiefunkcje mają tyle samo takich samych (co do typu) parametrów, mają takie same sygnatury, to nazwytych parametrów są bez znaczenia. Język C++ umożliwia definiowanie funkcji o takich samych nazwach,ale o różnych sygnaturach. Sygnatury mogą różnić się liczbą parametrów, ich typami lub jednym i drugim.Można na przykład zdefiniować ciąg funkcji print() mających następujące prototypy:

368 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

void print(const char * str, int width); // nr 1void print(double d, int width); // nr 2void print(long l, int width); // nr 3void print(int i, int width); // nr 4void print(const char *str); // nr 5

Kiedy używamy funkcji print(), kompilator odbiera do wywołania prototyp mający taką samąsygnaturę:

print("Ciastka", 15); // uyj nr 1print("Syrop"); // uyj nr 5print(1999.0, 10); // uyj nr 2print(1999, 12); // uyj nr 4print(1999L, 15); // uyj nr 3

Na przykład wywołanie print("Ciastka", 15); korzysta z parametrów będących łańcuchem i liczbącałkowitą, więc pasuje do prototypu numer 1.

Gdy korzysta się z funkcji przeciążonych, trzeba zapewnić, że w wywołaniu użyte zostaną prawidłowetypy parametrów. Weźmy na przykład pod uwagę następujące instrukcje:

unsigned int year = 3210;print(year, 6); // niejednoznaczne wywo�anie

Który prototyp print() pasuje do takiego wywołania? Żaden! Brak pasującego prototypu nieprzekreśla jeszcze możliwości wywołania takiej funkcji, bo C++ będzie próbował wymusić dopasowanieprzez standardowe konwersje typów. Gdyby na przykład prototyp numer 2 był jedynym prototypemfunkcji print, wywołanie print(year, 6) spowodowałoby konwersję wartości year na typ double.Jednak powyżej trzy prototypy mają za pierwszy parametr liczbę, więc year można skonwertować natrzy inne typy. W przypadku takiej niejednoznaczności C++ odrzuci wywołanie funkcji jako błędne.

Niektóre sygnatury pozornie się różnią, ale mimo to nie mogą istnieć jednocześnie. Na przykładweźmy pod uwagę następujące dwa prototypy:

double cube(double x);double cube(double & x);

Można by pomyśleć, że mamy do czynienia z przeciążaniem funkcji, gdyż sygnatury są różne, jednakdla kompilatora jest inaczej. Jeśli mamy wywołanie:

cout << cube(x);

parametr x pasuje do prototypu double x i do prototypu double &x. Kompilator nie jest w stanie„stwierdzić”, której funkcji ma użyć. Aby uniknąć tego typu problemów, kompilator, sprawdzającsygnatury funkcji, traktuje referencję do danego typu i sam ten typ jako równoważne.

Proces wybierania funkcji rozróżnia zmienne z modyfikatorem const i bez niego. Spójrzmy nanastępujące prototypy:

void dribble(char * bits); // przeci�enievoid dribble(const char *cbits); // przeci�enievoid dabble(char *bits); // nie ma przeci�eniavoid drivel(const char * bits); // nie ma przeci�enia

Oto do których prototypów będą pasowały różne wywołania funkcji:

const char p1[20] = "Jaka jest pogoda?";char p2[20] = "Jest ruch w interesie?";dribble(p1); // dribble(const char *);dribble(p2); // dribble(char *);

Przeci��anie funkcji 369

dabble(p1); // brak dopasowaniadabble(p2); // dabble(char *);drivel(p1); // drivel(const char *);drivel(p2); // drivel(const char *);

Funkcja dribble() ma dwa prototypy: jeden dla wskaźników stałych, drugi dla zwykłych; kompilatorwybierze jeden z nich w zależności od tego, czy przekazany parametr jest stałą, czy nie. Funkcja dabble()pasuje jedynie do wywołań z parametrem niebędącym stałą, natomiast funkcja drivel() pasuje dowywołań z parametrem stałym i niestałym. Powodem tej różnicy w zachowaniu dabble() i drivel()jest to, że można przypisać wartość bez const do zmiennej const, ale nie odwrotnie.

Trzeba pamiętać, że o możliwości przeciążania decyduje sygnatura, a nie typ funkcji. Na przykładponiższe dwie deklaracje są ze sobą niezgodne:

long gronk(int n, float m); // takie same sygnatury,double gronk(int n, float m); // wi�c sytuacja niedopuszczalna

Zatem funkcji gronk() nie można przeciążyć pokazaną metodą. Można użyć różnych typów wartościzwracanej, ale poza tym inne muszą być sygnatury:

long gronk(int n, float m); // sygnatury s� róne, wi�cdouble gronk(float n, float m); // teraz ju jest w porz�dku

W dalszej części tego rozdziału, kiedy będziemy omawiać szablony funkcji, dokładniej przeanalizujemyteż dobieranie funkcji.

Przeci��anie po parametrach referencyjnychW klasach i szablonach STL cz�sto stosuje si� parametry referencyjne i cho�by dlatego wartowiedzie�, jak przeci��anie dzia�a z ró�nymi typami referencyjnymi. We�my nast�puj�ce trzy prototypyfunkcji:

void sink(double & r1); // dla modyfikowalnej l-warto�civoid sank(const double & r2); // dla l-warto�ci (modyfikowalnej i niemodyfikowalnej) oraz r-warto�civoid sunk(double && r3); // dla r-warto�ci

Parametr deklarowany jako l-warto�� referencyjna r1 pasuje do ka�dego argumentu b�d�cegomodyfikowaln� l-warto�ci�, na przyk�ad do zwyczajnej zmiennej typu double. Parametr r2 b�dziedopasowany do argumentu b�d�cego modyfikowaln� l-warto�ci�, niemodyfikowaln� l-warto�ci�b�d� r-warto�ci� (mo�e to by� na przyk�ad wyra�enie sumy dwóch warto�ci typu double).Wreszcie referencja do r-warto�ci b�dzie pasowa� wy��cznie do r-warto�ci. Zauwa�my, �e r2mo�na dopasowa� do tych samych argumentów co r1 i r3, co prowadzi do pytania o zachowanieprogramu w przypadku niejednoznacznego przeci��enia funkcji. W takiej sytuacji realizowane jestdok�adniejsze dopasowanie:

void staff(double & rs); // dla modyfikowalnej l-warto�civoit staff(const double & rcs); // dla r-warto�ci i niemodyfikowalnej l-warto�civoid stove(double & r1); // dla modyfikowalnej r-warto�civoid stove(const double & r2); // dla niemodyfikowalnej l-warto�civoid stove(double && r3); // dla r-warto�ci

W ten sposób mo�emy dopasowywa� zachowanie funkcji do typu argumentu wywo�ania:

double x = 55.5;const double y = 32.0;stove(x); // wywo�anie wersji stove(double &)stove(y); // wywo�anie wersji stove(const double &)stove(x+y); // wywo�anie wersji stove(double &&)

Je�li (za�ó�my) pomin�liby�my funkcj� stove(double &&), to wywo�anie stove(x+y) zostanierozprowadzone do funkcji stove(const double &).

370 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Przyk�ad przeci��ania funkcjiW tym rozdziale tworzyliśmy już funkcję left() zwracającą wskaźnik na pierwszych n znaków łańcucha.Dodajmy teraz jeszcze jedną funkcję left(), która zwraca pierwszych n cyfr liczby całkowitej. Możemyjej użyć na przykład do sprawdzania początkowych cyfr konta bankowego zapisanego jako długa liczbacałkowita, w celu uzyskania informacji, w jakim banku konto to jest prowadzone.

Funkcja działająca na liczbach całkowitych jest nieco bardziej skomplikowana niż działająca nałańcuchach znakowych, gdyż poszczególne cyfry nie są przechowywane w odrębnych elementach tablicy.Jedno rozwiązanie polega na wyliczeniu najpierw, ile liczba ma cyfr. Dzielenie przez 10 powodujeodrzucenie jednej cyfry, więc stosując kolejne dzielenia, można policzyć cyfry. Można użyć na przykładnastępującej pętli:

unsigned digits = 1;while (n /= 10) digits++;

Pętla zlicza, ile razy można usunąć z n cyfrę, tak aby jeszcze coś zostało. Przypomnijmy, że n /= 10to skrócony zapis n = n / 10. Jeśli n jest na przykład równe 8, warunek pętli spowoduje przypisanie nwartości 8/10, czyli 0, gdyż jest to dzielenie całkowitoliczbowe. Pętla od razu zostanie przerwana, a zmiennadigits zostanie z wartością 1. Jednak już dla n = 238 w pierwszym cyklu pętli mamy 238/10, czyli 23.Nie jest to zero, więc po zwiększeniu digits o jeden pętla jest kontynuowana. Następnie n przybierawartość 23/10, czyli 2. Znowu wynik nie jest zerem, zatem digits ma już wartość 3. W następnymcyklu n jest równe 2/10, tym razem 0 — pętla jest przerywana, zmiennej digits pozostaje wartość 3.

Załóżmy teraz, że wiemy, iż liczba ma pięć cyfr, a chcemy zwrócić jej pierwsze 3 cyfry. Wystarczynajpierw podzielić liczbę przez 10, a potem ten wynik jeszcze raz przez 10. Każde dzielenie przez 10 powodujeodrzucenie jednej cyfry z prawej strony. Aby określić liczbę cyfr, jaką należy odrzucić, odejmujemyliczbę pokazywanych cyfr od całkowitej liczby cyfr. Żeby na przykład pokazać pierwsze 4 cyfry liczby9-cyfrowej, odrzucamy ostatnich 5 cyfr. Rozwiązanie to możemy zapisać w formie programu następująco:

ct = digits - ct;while (ct--) num /= 10;return num;

Na listingu 8.10 kod ten włączono do nowej funkcji left(). Funkcja zawiera też kod dodatkowy,który obsługuje przypadki szczególne, jak żądanie zera cyfr lub żądanie większej liczby cyfr, niż ma danaliczba. Sygnatura nowej funkcji left() jest inna niż sygnatura starej, więc możemy obu funkcji używaćw tym samym programie.

Listing 8.10. leftover.cpp

// leftover.cpp -- przeci�anie funkcji left()#include <iostream>unsigned long left(unsigned long num, unsigned ct);char * left(const char * str, int n = 1);

int main(){ using namespace std; char * trip = "Hawaii!!"; // warto�� testowa unsigned long n = 12345678; // warto�� testowa int i; char * temp;

Przeci��anie funkcji 371

for (i = 1; i < 10; i++) { cout << left(n, i) << endl; temp = left(trip,i); cout << temp << endl; delete [] temp; // wskazuje tymczasowy obszar pami�ci } return 0;

}

// funkcja ta zwraca pierwszych ct cyfr liczby numunsigned long left(unsigned long num, unsigned ct){ unsigned digits = 1; unsigned long n = num;

if (ct == 0 || num == 0) return 0; // je�li brak cyfr, zwraca 0 while (n /= 10) digits++; if (digits > ct) { ct = digits - ct; while (ct--) num /= 10; return num; // zwraca ct skrajnych lewych cyfr } else // je�li ct >= liczby cyfr, return num; // zwraca ca�� liczb�}

// funkcja zwraca wskanik nowego �a�cucha zawieraj�cego// pierwszych n znaków �a�cucha strchar * left(const char * str, int n){ if(n < 0) n = 0; char * p = new char[n+1]; int i; for (i = 0; i < n && str[i]; i++) p[i] = str[i]; // kopiowanie znaków while (i <= n) p[i++] = '\0'; // ustawienie reszty znaków na zera return p;}

Oto wynik działania programu z listingu 8.10:

1H12Ha123Haw1234Hawa12345Hawai123456Hawaii1234567

372 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Hawaii!12345678Hawaii!!12345678Hawaii!!

Kiedy korzysta z przeci��ania funkcji?Przeciążanie funkcji może wydawać się doprawdy fascynujące, ale nie należy go nadużywać.Przeciążanie funkcji należy stosować w przypadku funkcji realizujących te same zadania, ale na różnegorodzaju danych. Poza tym warto się zastanowić, czy podobnych efektów nie uda się uzyskać za pomocąparametrów domyślnych. Można na przykład zastąpić pojedynczą funkcję left() przetwarzającą łańcuchydwoma funkcjami przeciążonymi:

char * left(const char * str, unsigned n); // dwa parametrychar * left(const char * str); // jeden parametr

Jednak użycie pojedynczej funkcji z parametrem domyślnym jest prostsze. Piszemy tylko jednąfunkcję, program potrzebuje pamięci tylko na jedną funkcję… Jeśli zechcemy taką funkcję zmodyfikować,wystarczy poprawić tylko jeden fragment kodu. Jeśli jednak potrzebne są różne typy parametrów, a parametrydomyślne nie rozwiązują problemu, to pora skorzystać z przeciążania funkcji.

Co to jest ozdabianie nazw?W jaki sposób C++ rozró�nia poszczególne wersje funkcji przeci��onych? Przypisuje ka�dej z nichukryte cechy identyfikacyjne. Kiedy korzystamy z narz�dzia do edycji programów w �rodowiskuprogramistycznym, kompilator robi pewne sztuczki okre�lane mianem ozdabiania nazw. Polegaj�one na szyfrowaniu nazw funkcji zgodnie z parametrami tych funkcji podanymi w prototypie.We�my pod uwag� nast�puj�cy prototyp:

long MyFunctionFoo(int, float);

Taka posta� jest czytelna dla programisty; wiadomo, �e funkcja ma dwa parametry typu intoraz float i zwraca warto�� typu long. Kompilator dokumentuje ten interfejs, zamieniaj�cwewn�trznie nazw� na do�� niecodzienny napis, na przyk�ad:

?MyFunctionFoo@@YAXH

Tak udekorowana nazwa oryginalna ma zakodowan� liczb� parametrów oraz ich typy. Inne sygnaturyfunkcji powoduj� do��czanie innych symboli, poza tym ró�ne kompilatory stosuj� ró�ne konwencjedekorowania.

Szablony funkcjiWspółczesne kompilatory C++ wykorzystują jedną z nowszych cech C++ — szablony funkcji. Szablonfunkcji to ogólny opis funkcji, czyli taki, w którym funkcja jest opisana przez typy ogólne — pod niemożna później podstawiać typy konkretne, jak int czy double. Przekazując szablonowi typ jako parametr,można wymusić na kompilatorze wygenerowanie funkcji określonego typu. Szablony umożliwiająprogramowanie ogólne (ang. generic programming). Typy są przekazywane jak parametry, więc o szablonachmówi się czasem jako o typach sparametryzowanych. Sprawdźmy teraz, do czego szablony funkcji mogąsię przydać i jak się ich używa.

Definiowaliśmy wcześniej (na listingu 8.4) funkcję, która zamieniała wartości dwóch zmiennychtypu int. Załóżmy, że chcielibyśmy zamienić wartości dwóch zmiennych typu double. Jedno rozwiązanieto powielenie poprzedniego kodu, ale z zastąpieniem typu int typem double. Gdybyśmy chcieli zamieniać

Szablony funkcji 373

wartości typu char, ponownie skorzystalibyśmy z tej samej techniki. Przeprowadzanie takich banalnychmodyfikacji byłoby marnowaniem naszego cennego czasu, poza tym zawsze za którymś razem możnapopełnić błąd. Jeśli wprowadzamy zmiany ręcznie, zawsze możemy przeoczyć któreś wystąpienie int. Jeżelirealizujemy podstawienie globalne, na przykład zamieniając int na double, możemy zrobić coś takiego:

int x;short interval;

zostanie zamienione na:

double x; // zamierzona zamiana typushort doubleerval; // niezamierzona zmiana nazwy zmiennej

Szablony funkcji C++ automatyzują proces generowania różnych odmian funkcji, przez co pozwalająoszczędzić czas i zwiększyć niezawodność kodu.

Szablony funkcji umożliwiają zdefiniowanie funkcji z dowolnym typem. Na przykład możnaprzygotować szablon do zamiany wartości następująco:

template <typename AnyType>void Swap(AnyType &a, AnyType &b){ AnyType temp; temp = a; a = b; b = temp;}

W pierwszym wierszu deklarujemy tworzenie szablonu, ustalamy też, że typ parametryzujący będziesię nazywał AnyType. Słowa kluczowe template i typename są obowiązkowe, tyle że zamiast słowa typenamemożna użyć class. Obowiązkowe są też nawiasy kątowe. Nazwa typu (u nas AnyType) jest dowolna, byletylko spełniała standardowe zasady nazewnictwa obowiązujące w C++; wielu programistów preferujew tej sytuacji proste nazwy, jak T, które (trzeba to przyznać) istotnie skracają kod. Reszta kodu opisujealgorytm zamieniający dwie wartości typu AnyType. Szablon nie powoduje utworzenia żadnej nowejfunkcji, ale daje kompilatorowi wskazówki, jak takie funkcje należałoby definiować. Jeśli zechcemy,aby funkcja zamieniała miejscami wartości int, kompilator utworzy potrzebną definicję funkcji, zamieniającwszystkie wystąpienia typu AnyType na typ int. Jeśli potrzebna nam będzie funkcja zamieniająca wartościtypu double, kompilator zgodnie z tym samym szablonem utworzy i taką funkcję.

Przed ustaleniem standardu C++98 ze słowem kluczowym typename w C++ w tym konkretnymkontekście (to jest w kontekście parametrów szablonów) stosowano słowo class. Powyższy szablonmożna by więc przepisać tak:

template <class AnyType>void Swap(AnyType &a, AnyType &b){ AnyType temp; temp = a; a = b; b = temp;}

Słowo kluczowe typename nieco lepiej objaśnia, że AnyType jest typem; jednak w dużych bibliotekachgotowego już kodu używane jest starsze słowo kluczowe class. Standard C++ traktuje w omawianymkontekście oba słowa identycznie. Tutaj będziemy również stosować obie formy, choćby po to, żeby oswoićczytelnika z obiema wciąż powszechnie stosowanymi składniami deklaracji typowego parametru szablonu.

374 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

WskazówkaSzablonów nale�y u�ywa�, kiedy potrzebne nam s� funkcje stosuj�ce ten sam algorytm doró�nych typów danych. Je�li nie zale�y nam na zgodno�ci wstecz i sta� nas na wysi�ek wpisaniad�u�szego s�owa, zamiast class nale�y stosowa� raczej typename.

Aby poinformować kompilator, że potrzebna nam będzie określona funkcja Swap(), wystarczy użyćjej w programie. Kompilator sprawdzi, jakiego typu są jej parametry, i potrzebną funkcję wygeneruje.Na listingu 8.11 pokazano, jak to działa. Program jest zbudowany jak w przypadku zwykłych funkcji,prototyp szablonu funkcji jest na górze pliku, a sam szablon zdefiniowano za funkcją main().W przykładzieposługujemy się też tradycyjną już nazwą parametru typowego T zamiast AnyType.

Listing 8.11. funtemp.cpp

// funtemp.cpp -- uycie szablonu funkcji#include <iostream>// prototyp szablonu funkcjitemplate <typename T> // albo class Tvoid Swap(T &a, T &b);

int main(){ using namespace std; int i = 10; int j = 20; cout << "i, j = " << i << ", " << j << ".\n"; cout << "U�ycie funkcji obs�uguj�cej typ int, " "wygenerowanej przez kompilator:\n"; Swap(i,j); // generuje void Swap(int &, int &) cout << "Teraz i, j = " << i << ", " << j << ".\n";

double x = 24.5; double y = 81.7; cout << "x, y = " << x << ", " << y << ".\n"; cout << "U�ycie funkcji obs�uguj�cej typ double, " "wygenerowanej przez kompilator:\n"; Swap(x,y); // generuje void Swap(double &, double &) cout << "Teraz x, y = " << x << ", " << y << ".\n"; // cin.get(); return 0;}

// definicja szablonu funkcjitemplate <typename T> // lub class Tvoid Swap(T &a, T &b){ T temp; // zmienna temp typu T temp = a; a = b; b = temp;}

Pierwsza funkcja Swap() z listingu 8.11 ma dwa parametry typu int, więc kompilator wygenerujewersję int tej funkcji: wszystkie wystąpienia T zostaną zamienione na int, zatem uzyskamy definicję:

void Swap(int &a, int &b){ int temp; temp = a; a = b; b = temp;}

Szablony funkcji 375

Tego kodu nie widać, ale kompilator go wygeneruje i umieści w programie wynikowym. Drugafunkcja Swap() ma parametry typu double, więc w nowej wersji kompilator zastąpi wszystkie wystąpienia Tsłowem double:

void Swap(double &a, double &b){ double temp; temp = a; a = b; b = temp;}

Oto wynik działania programu z listingu 8.11:

i, j = 10, 20.U�ycie funkcji obs�uguj�cej typ int, wygenerowanej przez kompilator:Teraz i, j = 20, 10.x, y = 24.5, 81.7.U�ycie funkcji obs�uguj�cej typ double, wygenerowanej przez kompilator:Teraz x, y = 81.7, 24.5.

Zauważmy, że szablony funkcji nie powodują skrócenia programu wynikowego. Na listingu 8.11nadal mamy dwie osobne definicje funkcji, zupełnie jakbyśmy każdą z nich zdefiniowali ręcznie.Ostateczny kod nie zawiera szablonów, ale jedynie ich konkretne wcielenia. Zaletą szablonów jest to,że upraszczają definiowanie wielu podobnych funkcji oraz ograniczają ryzyko popełnienia błędów.

Zazwyczaj szablony są w całości umieszczane w osobnych plikach nagłówkowych, włączanychnastępnie do plików korzystających z tych szablonów; o samych plikach nagłówkowych będzie jeszczemowa w rozdziale 9.

Przeci��one szablonySzablonów używa się, kiedy potrzebne są różne funkcje oparte na tym samym algorytmie, ale działającena różnych typach, jak na listingu 8.11. Może się jednak zdarzyć, że nie wszystkie typy będą wykorzystywałyten sam algorytm. Aby obsłużyć i taki przypadek, można przeciążać definicje szablonów tak samo, jakprzeciąża się definicje zwykłych funkcji. Tak jak w przypadku zwykłego przeciążania przeciążone szablonymuszą mieć odrębne sygnatury funkcji. Na przykład na listingu 8.12 dodano nowy szablon zamianywartości, obsługujący elementy dwóch tablic. Nasz pierwszy szablon miał sygnaturę (T &, T &), a nowy— (T [],T [], int). Zwróćmy uwagę na to, że ostatni element tej sygnatury jest konkretnym typemint, a nie typem ogólnym. Nie wszystkie parametry szablonów muszą być typami ogólnymi.

Kiedy w pliku twotemps.cpp kompilator natknie się na pierwsze wywołanie Swap(), „stwierdzi”, żeużyto dwóch parametrów typu int, więc wywołanie to pasuje do pierwotnego szablonu. Jednak drugiewywołanie ma za parametry dwie tablice typu int oraz liczbę int, więc pasuje do nowego szablonu.

Listing 8.12. twotemps.cpp

// twotemps.cpp -- uycie przeci�onych szablonów funkcji#include <iostream>template <typename T> // szablon oryginalnyvoid Swap(T &a, T &b);

template <typename T> // nowy szablonvoid Swap(T *a, T *b, int n);

void Show(int a[]);const int Lim = 8;

376 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

int main(){ using namespace std; int i = 10, j = 20; cout << "i, j = " << i << ", " << j << ".\n"; cout << "U�ycie funkcji obs�uguj�cej typ int, " "wygenerowanej przez kompilator:\n"; Swap(i,j); // pasuje do szablonu oryginalnego cout << "Teraz i, j = " << i << ", " << j << ".\n";

int d1[Lim] = {0,7,0,4,1,7,7,6}; int d2[Lim] = {0,7,2,0,1,9,6,9}; cout << "Tablice pocz�tkowo:\n"; Show(d1); Show(d2); Swap(d1,d2,Lim); // pasuje do nowego szablonu cout << "Tablice po zamianie:\n"; Show(d1); Show(d2);// cin.get(); return 0;}

template <typename T>void Swap(T &a, T &b){ T temp; temp = a; a = b; b = temp;}

template <typename T>void Swap(T a[], T b[], int n){ T temp; for (int i = 0; i < n; i++) { temp = a[i]; a[i] = b[i]; b[i] = temp; }}

void Show(int a[]){ using namespace std; cout << a[0] << a[1] << "/"; cout << a[2] << a[3] << "/"; for (int i = 4; i < Lim; i++) cout << a[i]; cout << endl;}

Oto wynik działania programu z listingu 8.12:

i, j = 10, 20.U�ycie funkcji obs�uguj�cej typ int, wygenerowanej przez kompilator:Teraz i, j = 20, 10.Tablice pocz�tkowo:07/04/177606/20/1969

Szablony funkcji 377

Tablice po zamianie:06/20/196907/04/1776

Ograniczenia szablonówZałóżmy, że dany jest następujący szablon funkcji:

template <class T> // albo template <typename T>void f(T a, T b){...}

Kod szablonu często bazuje na silnych założeniach co do operacji możliwych na danym typie. Naprzykład poniższa instrukcja w kodzie szablonu opiera się na założeniu, że dla obiektów zdefiniowanooperację przypisania, co nie będzie możliwe dla T będącego typem tablicowym:

a = b;

Podobnie poniższa instrukcja bazuje na założeniu, że dla a i b zdefiniowana jest operacja >, co niebędzie spełnione dla T będącego zwyczajną strukturą:

if (a > b)

Operator > jest co prawda zdefiniowany dla nazw tablic, ale ponieważ nazwy tablic są adresami,porównanie dotyczy wartości adresów tablic, co niekoniecznie jest pożądaną semantyką porównania(zapewne bliższe oczekiwań byłoby porównanie rozmiarów tablic). A poniższa instrukcja zakłada, żedla T zdefiniowano operację mnożenia, co jest nieprawdą w przypadku tablic, wskaźników i struktur:

T c = a*b;

Krótko mówiąc, łatwo jest napisać szablon funkcji, która nie będzie działać dla niektórych (albo dlawielu) typów. Z drugiej strony niekiedy uogólnienie funkcji ma rzeczywiście sens, nawet jeśli zwyczajnaskładnia C++ na to nie pozwala. Na przykład przydałaby się możliwość dodawania struktur zawierającychwspółrzędne pozycji, mimo że dla struktur nie istnieje operator +. W C++ można wtedy przeciążyćoperator + tak, aby działał z odpowiednio zdefiniowaną strukturą lub klasą; takie podejście będziemyomawiać w rozdziale 11. Taka struktura nadawałaby się również do użycia w szablonie, który do poprawnegodziałania wymaga operacji dodawania. Alternatywą jest udostępnienie specjalizowanych definicji szablonówdla wybranych typów; to będzie przedmiotem omówienia w następnym punkcie.

Specjalizacje jawneZałóżmy, że zdefiniowaliśmy następującą strukturę:

struct job{ char name[40]; double salary; int floor;};

Przyjmijmy jeszcze, że chcemy móc zamienić zawartość dwóch takich struktur. W pierwotnym szabloniedo zamiany używamy następującego kodu:

temp = a;a = b;b = temp;

378 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Wobec tego, że C++ pozwala przypisywać jedne struktury innym, kod ten zadziała poprawnie, nawetjeśli T jest strukturą job. Załóżmy jednak, że chcielibyśmy zamienić wartościami pola salary i floor,zostawiając name bez zmian. Potrzebny tu będzie inny kod, ale parametry Swap() będą takie same jakw pierwszym przypadku (referencje do dwóch struktur job), wobec czego nie można zastosowaćprzeciążania szablonów.

Można jednak podać specjalizowaną definicję funkcji, nazywaną specjalizacją jawną, która będziemiała potrzebny kod. Jeśli kompilator znajdzie specjalizowaną definicję pasującą dokładnie do wywołaniafunkcji, użyje tej właśnie definicji, nie szukając nawet szablonów.

Mechanizm specjalizacji zmieniał się wraz z rozwojem języka C++. Przyjrzymy się aktualnej wersji,zgodnej ze standardem C++.

Specjalizacja trzeciej generacji (standard C++ ISO/ANSI)Po wczesnych eksperymentach w tym zakresie standard C++98 ustanowił, co następuje:

� Dla danej nazwy funkcji można mieć funkcję nieszablonową, szablon funkcji oraz jawnąspecjalizację szablonu funkcji, a także przeciążone wersje ich wszystkich.

� Prototyp i definicja jawnej specjalizacji muszą być poprzedzone zapisem template <>, powinnywymieniać nazwę specjalizowanego typu.

� Specjalizacja przykrywa zwykły szablon, a funkcja zwykła przykrywa je wszystkie.

Oto wszystkie trzy postaci prototypów funkcji zamieniającej struktury job:

// prototyp zwyk�ej funkcjivoid Swap(job &, job &);

// szablon prototyputemplate <typename T>void Swap(T &, T &);

// jawna specjalizacja typu jobtemplate <> void Swap<job>(job&, job &);

Jak już wspomnieliśmy wcześniej, jeśli istnieje więcej niż jeden z tych prototypów, kompilatorwybiera nieszablonową wersję przed jawną specjalizacją i przed szablonem, a jawna specjalizacja jestwybierana przed wersją wygenerowaną przed szablonem. Na przykład w poniższym kodzie pierwszewywołanie Swap() korzysta z szablonu ogólnego, a drugie korzysta z jawnej specjalizacji bazującejna typie job:

...template <class T> // szablonvoid Swap(T &, T &);

// jawna specjalizacja dla typu jobtemplate <> void Swap<job>(job &, job &);int main(){ double u, v; ... Swap(u,v); // uyj szablonu job a, b; ... Swap(a,b); // uycie void Swap<job>(job &, job &)}

Szablony funkcji 379

Zapis <job> w Swap<job> jest opcjonalny, gdyż z typów parametrów funkcji wynika, że jest tospecjalizacja job. Wobec tego ten sam prototyp można zapisać jako:

template <> void Swap(job &, job &); // prostsza forma

Gdyby przyszło nam pracować ze starszym kompilatorem, wrócilibyśmy do użycia wywołaniastarszego niż standard C++. Najpierw jednak sprawdźmy, jak powinny działać specjalizacje jawne.

Przykład jawnej specjalizacjiNa listingu 8.13 pokazano działanie specjalizacji. Kod ten jest zgodny ze standardem ANSI C++.

Listing 8.13. twoswap.cpp

// twoswap.cpp -- specjalizacja nadpisuje szablon#include <iostream>template <typename T>void Swap(T &a, T &b);

struct job{ char name[40]; double salary; int floor;};

// jawna specjalizacjatemplate <> void Swap<job>(job &j1, job &j2);void Show(job &j);

int main(){ using namespace std; cout.precision(2); cout.setf(ios::fixed, ios::floatfield); int i = 10, j = 20; cout << "i, j = " << i << ", " << j << ".\n"; cout << "U�ycie generowanej przez kompilator funkcji " "zamieniaj�cej warto�ci int:\n"; Swap(i,j); // generuje void Swap(int &, int &) cout << "Teraz i, j = " << i << ", " << j << ".\n";

job sue = {"Susan Yaffee", 73000.60, 7}; job sidney = {"Sidney Taffee", 78060.72, 9}; cout << "Przed zamian� struktur job:\n"; Show(sue); Show(sidney); Swap(sue, sidney); // uywa void Swap(job &, job &) cout << "Po zamianie struktur job:\n"; Show(sue); Show(sidney);// cin.get(); return 0;}

template <typename T>void Swap(T &a, T &b) // wersja ogólna{ T temp; temp = a; a = b; b = temp;

380 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

}

// zamienia tylko pola salary i floor struktury job

template <> void Swap<job>(job &j1, job &j2) // specjalizacja{ double t1; int t2; t1 = j1.salary; j1.salary = j2.salary; j2.salary = t1; t2 = j1.floor; j1.floor = j2.floor; j2.floor = t2;}

void Show(job &j){ using namespace std; cout << j.name << ": " << j.salary << "z� na pi�trze " << j.floor << endl;}

Oto wyniki działania programu z listingu 8.13:

i, j = 10, 20.U�ycie generowanej przez kompilator funkcji zamieniaj�cej warto�ci int:Teraz i, j = 20, 10.Przed zamian� struktur job:Susan Yaffee: 73000.60z� na pi�trze 7Sidney Taffee: 78060.72z� na pi�trze 9Po zamianie struktur job:Susan Yaffee: 78060.72z� na pi�trze 9Sidney Taffee: 73000.60z� na pi�trze 7

Konkretyzacje i specjalizacjeAby lepiej zrozumieć szablony, zajmijmy się konkretyzacją i specjalizacją. Pamiętajmy, że włączenieszablonu funkcji samo z siebie nie powoduje wygenerowania definicji funkcji; jest to tylko plangenerowania takiej definicji. Kiedy kompilator generuje definicję funkcji dla danego typu na podstawieszablonu, wynikiem jest konkretyzacja (czyli utworzenie egzemplarza) szablonu. Na przykład na listingu8.13 wywołanie funkcji Swap(i,j) powoduje, że kompilator wygeneruje egzemplarz Swap() dla typu int.Szablon nie jest definicją funkcji, natomiast konkretyzacja dla typu int już jest definicją funkcji. Tegotypu konkretyzację nazywamy niejawną, gdyż kompilator „wnioskuje” o konieczności przygotowaniadefinicji po ustaleniu, że program używa funkcji Swap() z parametrami typu int.

Początkowo niejawna konkretyzacja była jedynym sposobem generowania definicji funkcji napodstawie szablonów, ale obecnie C++ pozwala też na konkretyzacje jawne. Znaczy to, że można jawnienakazać kompilatorowi utworzenie konkretnego egzemplarza, na przykład Swap<int>(). Składnia jestprosta — deklaruje się wszystkie potrzebne funkcje, podając typy w <> i poprzedzając deklarację słowemkluczowym template:

template void Swap<int>(int, int); // jawna konkretyzacja

Kompilator pozwalający na jawne konkretyzowanie szablonu po natknięciu się na powyższądeklarację na podstawie szablonu Swap() utworzy egzemplarz z typem int. Deklaracja ta znaczy zatem:„użyj szablonu Swap() do wygenerowania definicji funkcji dla typu int”.

Szablony funkcji 381

Porównajmy teraz jawną konkretyzację szablonu z jego jawną specjalizacją, w której używa się jednejz równoważnych deklaracji:

template <> void Swap<int>(int &, int &); // jawna specjalizacjatemplate <> void Swap(int &, int &); // jawna specjalizacja

Różnica jest taka, że dwie powyższe deklaracje nie oznaczają użycia szablonu Swap() do wygenerowaniadefinicji funkcji; oznaczają „użyj odrębnej, wyspecjalizowanej definicji funkcji jawnie zdefiniowanej dlatypu int”. Prototypy te muszą być powiązane z odpowiadającymi im definicjami funkcji. Deklaracjajawnej specjalizacji po słowie kluczowym template ma <>, a w jawnej konkretyzacji nawiasów kątowych <>już nie ma.

Ostrze�eniePróba u�ycia w jednym pliku (a ogólnie w jednej jednostce translacji) jawnej konkretyzacji i jawnejspecjalizacji dla tego samego typu danych jest b��dem.

Jawne konkretyzacje mogą być tworzone również poprzez użycia funkcji w programie. Weźmy naprzykład taki kod:

template <class T>T Add(T a, T b) // przekazywanie przez warto��{ return a + b;}...int m = 6;double x = 10.2;cout << Add<double>(x, m) << endl; // jawna konkretyzacja

Szablon nie dopasowałby wywołania Add(x, m), ponieważ oczekuje, że oba przekazywane argumentybędą tego samego typu. Ale wywołanie w postaci Add<double>(x, m) wymusza konkretyzację szablonudla typu double, a także rzutowanie typów argumentów wywołania funkcji na double w przypadkudrugiego argumentu skonkretyzowanej funkcji Add<double>(double, double).

A co w przypadku analogicznej operacji na szablonie Swap()?

int m = 5;double x = 14.3;Swap<double>(m, x); // prawie dobrze

Powyższy kod wygeneruje co prawda jawną konkretyzację szablonu dla typu double, ale niestetyszablonu nie uda się skompilować, ponieważ pierwszy parametr funkcji Swap ma typ double &, a takityp nie może się odnosić do zmiennej m typu int.

Niejawna konkretyzacja, jawna konkretyzacja oraz jawna specjalizacja określane są zbiorczo mianemspecjalizacji. Ich wspólną cechą jest to, że reprezentują definicję funkcji używającej konkretnych typów,a nie ogólny opis funkcji.

Dodanie jawnej konkretyzacji szablonu doprowadziło do opracowania nowej składni: templatei template <>; służy to odróżnieniu jawnej konkretyzacji od jawnej specjalizacji. Jak w wielu innychprzypadkach zwiększenie możliwości odbywa się kosztem skomplikowania składni. W poniższymfragmencie kodu zestawiono omawiane tu pojęcia:

...template <class T>void Swap (T &, T &); // prototyp szablonu

382 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

template <> void Swap<job>(job &, job &); // jawna specjalizacja dla typu jobint main(void){ template void Swap<char>(char &, char &); // jawna konkretyzacja dla typu char short a, b; ... Swap(a,b); // niejawna konkretyzacja szablonu dla typu short job n, m; ... Swap(n, m); // uycie jawnej specjalizacji dla typu job char g, h; ... Swap(g, h); // uycie jawnie skonkretyzowanego szablonu dla typu char ...}

Kiedy kompilator dochodzi do jawnej konkretyzacji szablonu dla typu char, na podstawie definicjiszablonu generuje wersję char funkcji Swap(). W innych przypadkach użycia Swap() kompilator dopasowujeszablon do argumentów wykorzystanych w wywołaniu funkcji. Kiedy na przykład kompilator dochodzido wywołania Swap(a,b), generuje wersję short funkcji Swap(), gdyż oba parametry są typu short. Kiedykompilator dochodzi do wywołania Swap(n,m), używa odrębnej definicji (jawnej specjalizacji) przygotowanejdla typu job. Gdy kompilator dochodzi do wywołania Swap(g,h), używa specjalizacji szablonuwygenerowanej wcześniej podczas przetwarzania konkretyzacji jawnej.

Któr� wersj� funkcji wybierze kompilator?Biorąc pod uwagę przeciążanie funkcji, szablony funkcji oraz przeciążanie szablonów funkcji, C++ potrzebujejasno zdefiniowanej strategii pozwalającej decydować, której definicji funkcji użyć w poszczególnychwywołaniach, szczególnie jeśli przekazywanych jest wiele parametrów — i ma taką strategię. Proces tenjest nazywany rozwiązywaniem przeciążeń. Kompletny opis tej strategii wymagałby osobnego rozdziału,więc tylko przyjrzyjmy się z grubsza, jak się odbywa ten proces.

� Etap 1. — zbieranie listy funkcji potencjalnie przydatnych w danej sytuacji. Wybierane są funkcjei szablony funkcji mające taką samą nazwę jak funkcje wywołane.

� Etap 2. — spośród funkcji potencjalnie przydatnych wybierane są funkcje pasujące. Są to funkcjemające odpowiednią liczbę parametrów, dla których istnieją niejawne metody konwersji lubdokładne dopasowanie typu parametru. Na przykład jeśli funkcja jest wywoływana z parametremtypu float, przekazana wartość zostanie skonwertowana na typ double, aby pasowała do parametrudouble, ale w przypadku konkretyzacji będzie ona już dotyczyć typu float.

� Etap 3. — sprawdzenie, czy istnieje funkcja pasująca najlepiej. Jeśli tak, zostanie ona użyta.Jeżeli nie, wywołanie funkcji kończy się błędem.

Przyjrzyjmy się przypadkowi z pojedynczym parametrem funkcji, na przykład:

may('B'); // parametr aktualny jest typu char

Najpierw kompilator dobiera „podejrzanych”, czyli funkcje i szablony funkcji mające nazwę may().Następnie wybierane są funkcje i szablony mające jeden parametr. Grono kandydatów obejmujewszystkie poniższe funkcje o tej samej nazwie i z jednym argumentem wywołania:

void may(int); // #1float may(float, float = 3); // #2void may(char); // #3

Szablony funkcji 383

char * may(const char *); // #4char may(const char &); // #5template<class T> void may (const T &); // #6template<class T> void may (T *); // #7

Pamiętajmy, że sprawdzane są dalej typy parametrów, a nie typy wartości zwracanych. Jednakdwóch kandydatów, #4 i #7, nie pasuje, gdyż typu liczbowego nie można niejawnie (czyli bez jawnegorzutowania) przekształcić na wskaźnik. Drugi szablon nadaje się, bo służy do wygenerowania specjalizacji,przy czym typ T to typ char. W ten sposób mamy pięć potencjalnie użytecznych funkcji, z których każdamogłaby zostać użyta, gdyby była jedyną deklaracją.

Następnie kompilator sprawdza, która z funkcji pasuje najlepiej. Istotna jest tu konwersja koniecznado wywołania funkcji. Ogólnie rzecz biorąc, dopasowania od najlepszych do najgorszych układają sięnastępująco:

1. Dokładne dopasowania, w tym zwykłe funkcje przed szablonami.2. Konwersje będące promocjami typów (na przykład automatyczna konwersja char i short na typ

int oraz float na double).3. Konwersje standardowe, na przykład konwersja typu int na char lub typu long na typ double.4. Konwersje użytkownika, na przykład zdefiniowane w deklaracjach klas.

Na przykład funkcja #1 jest lepsza od funkcji #2, gdyż konwersja typu char na int to promocja(rozdział 3.), a konwersja char na float to konwersja standardowa (rozdział 3.). Funkcje #3, #5 i #6 sąlepsze od #1 i #2, gdyż są to dokładne dopasowania. Funkcje #3 i #5 są lepsze od #6, ponieważ ta ostatniajest szablonem. Pojawia się kilka pytań związanych z opisanymi zasadami wywoływania. Czym jestdopasowanie dokładne? I co jeśli mamy dwa takie dopasowania jak #3 i #5?

Zwykle, jak w powyższym przykładzie, dwa dokładne dopasowania świadczą o błędzie, chociaż od tejzasady bywają wyjątki. Jak widać, bez dalszej analizy tego tematu się nie obędzie!

Dopasowania dokładne i najlepszeC++ dopuszcza pewne najprostsze konwersje w przypadku dopasowania dokładnego. Zestawiono jew tabeli 8.1; Type oznacza dowolny typ. Na przykład parametr wywołania int dokładnie pasuje doparametru int &. Zauważmy, że Type może mieć postać typu char &, więc te oczywiste konwersje tozamiana char & na const char &. Wiersz z zapisem Type (lista-parametrów) oznacza przekazaniewskaźnika funkcji o takiej samej sygnaturze i takiej samej wartości zwracanej. Przypomnijmy sobiewskaźniki funkcji z rozdziału 7. Pamiętajmy też, że można przekazać nazwę funkcji jako parametrfunkcji oczekującej wskaźnika na funkcję. Słowo kluczowe volatile omówimy dalej w rozdziale 9.

Tabela 8.1. Oczywiste konwersje dozwolone przy dok�adnym dopasowaniu

Z parametru wywo�ania Na parametr

Type Type &

Type & Type

Type [] * Type

Type (lista-parametrów) Type (*) (lista-parametrów)

Type const Type

Type volatile Type

Type * const Type *

Type * volatile Type *

384 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Załóżmy, że mamy następujący kod funkcji:

struct blot {int a; char b[10];};blot ink = {25, "spots"};...recycle(ink);

Wtedy dokładne będą dopasowania wszystkich poniższych prototypów:

void recycle(blot); // #1 blot-blotvoid recycle(const blot); // #2 blot-(const blot)void recycle(blot &); // #3 blot-(blot &)void recycle(const blot &); // #4 blot-(const blot &)

Zgodnie z oczekiwaniami, jeśli mamy kilka pasujących prototypów, kompilator nie potrafizakończyć procesu rozwiązywania przeciążeń. Nie ma funkcji pasującej najlepiej, więc kompilatorzgłasza błąd — prawdopodobnie w komunikacie pojawi się gdzieś słowo ambiguous, niejednoznaczny.

Jednak czasami rozwiązanie przeciążeń jest możliwe nawet wtedy, kiedy mamy dwie dokładniedopasowane funkcje. Po pierwsze, wskaźniki i referencje nie-const są preferowane przy dopasowywaniudo wskaźników i referencji nie-const. Gdyby zatem w przykładzie z recycle() do dyspozycji były tylkofunkcje #3 i #4, wybrana zostałaby #3, bo jej parametr nie zawiera słowa kluczowego const. Jednakrozróżnienie między const a nie-const dotyczy tylko danych wskaźnikowych i referencyjnych. Gdybydostępne były tylko funkcje #1 i #2, błąd niejednoznaczności wystąpiłby i tak.

Inny przypadek, kiedy jedno dokładne dopasowanie jest lepsze od innego, to sytuacja, gdy jednafunkcja jest szablonem, a druga nie. Wtedy za lepiej dopasowaną uznawana jest funkcja niebędącaszablonem; zasada ta obejmuje także jawne specjalizacje.

Jeśli mamy dwa dokładnie dopasowane szablony, ale jeden z nich jest bardziej wyspecjalizowany,jest on uznawany za lepszy. Wobec tego jawna specjalizacja jest preferowana przed specjalizacją niejawną:

struct blot {int a; char b[10];};template <class Type> void recycle (Type t); // szablontemplate <> void recycle<blot> (blot & t); // specjalizacja blotblot ink = {25, "spots"};...recycle(ink); // uycie specjalizacji

Termin najbardziej wyspecjalizowana nie musi oznaczać specjalizacji jawnej; wskazuje raczej, żeprzy ustalaniu użytych typów kompilator musi przeprowadzić mniej konwersji. Weźmy na przykładpod uwagę dwa przykłady szablonów:

template <class Type> void recycle (Type t); // #1template <class Type> void recycle (Type * t); // #2

Załóżmy, że program zawiera powyższe szablony oraz kod:

struct blot {int a; char b[10];};blot ink = {25, "spots"};...recycle(&ink); // adres struktury

Wywołanie recycle(&ink) pasuje do szablonu #1, gdzie Type jest interpretowany jako blot *.Wywołanie recycle(&ink) pasuje także do szablonu #2, tym razem Type to blot. Powoduje to dwieniejawne konkretyzacje szablonu, recycle<blot *>(blot *) oraz recycle<blot>(blot *).

Szablony funkcji 385

Z dwóch powyższych szablonów bardziej wyspecjalizowany jest szablon recycle<blot *>(blot *),gdyż jego generowanie wymaga mniej konwersji. Wobec tego szablon #2 wskazuje już jawnie, że parametrfunkcji był wskaźnikiem na typ Type, więc może być bezpośrednio utożsamiony z blot. Jednak w szablonie #1Type jest parametrem funkcji, zatem Type musi być interpretowany jako wskaźnik blot. Wobec tegow szablonie #2 Type był specjalizowany jako wskaźnik i dlatego jest „bardziej wyspecjalizowany”.

Zasady znajdowania najbardziej wyspecjalizowanego szablonu to zasady uporządkowania częściowegoszablonów funkcji. Tak jak jawna konkretyzacja szablonów są one elementami języka C++ wprowadzonymistandardem C++98.

Przykład zasad uporządkowania częściowegoTeraz przyjrzyjmy się kompletnemu programowi używającemu uporządkowania częściowego dodecydowania, którego szablonu należy użyć. Na listingu 8.14 pokazane są dwie definicje szablonówwyświetlających zawartość tablicy. Pierwsza definicja, szablon A, zakłada, że tablica zostanie przekazanajako parametr z danymi do pokazania. Szablon B zakłada, że elementami tablicy są wskaźniki nawyświetlane dane.

Listing 8.14. tempover.cpp

// tempover.cpp -- przeci�anie szablonów#include <iostream>

template <typename T> // szablon Avoid ShowArray(T arr[], int n);

template <typename T> // szablon Bvoid ShowArray(T * arr[], int n);

struct debts{ char name[50]; double amount;};

int main(){ using namespace std; int things[6] = {13, 31, 103, 301, 310, 130}; struct debts mr_E[3] = { {"Ima Wolfe", 2400.0}, {"Ura Foxe", 1300.0}, {"Iby Stout", 1800.0} }; double * pd[3];

// ustawienie wskaników na pola amount struktur z tablicy Mr_E for (int i = 0; i < 3; i++) pd[i] = &mr_E[i].amount;

cout << "Wyliczanie rzeczy pana E.:\n";// things to tablica intShowArray(things, 6); // uywamy szablonu A cout << "Wyliczanie d�ugów pana E.:\n";// pd to tablica wskaników na double ShowArray(pd, 3); // uywa szablonu B (bardziej wyspecjalizowanego) return 0;}

386 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

template <typename T>void ShowArray(T arr[], int n){ using namespace std; cout << "szablon A\n"; for (int i = 0; i < n; i++) cout << arr[i] << ' '; cout << endl;}

template <typename T>void ShowArray(T * arr[], int n){ using namespace std; cout << "szablon B\n"; for (int i = 0; i < n; i++) cout << *arr[i] << ' '; cout << endl;}

Weźmy pod uwagę wywołanie:

ShowArray(things, 6);

Identyfikator things to nazwa tablicy int, więc pasuje on do szablonu:

template <typename T> // szablon Avoid ShowArray(T arr[], int n);

gdzie jako T jest brane int.

Teraz weźmy pod uwagę wywołanie:

ShowArray(pd, 3);

Tym razem pd to nazwa tablicy double *. Można je dopasować do szablonu A:

template <typename T> // szablon Avoid ShowArray(T arr[], int n);

Tym razem T zostanie zinterpretowane jako double *. Tak wygenerowana funkcja wyświetliłabyzawartość tablicy pd — trzy adresy. Wywołanie funkcji będzie pasowało także do szablonu B:

template <typename T> // szablon Bvoid ShowArray(T * arr[], int n);

Tym razem T to typ double, a funkcja wyświetli dereferencje elementów *arr[i], czyli wartościdouble wskazywane w tablicy. Z pokazanych dwóch szablonów bardziej specjalizowany jest B, gdyżprzyjmuje założenie, że wszystkie elementy tablicy są wskaźnikami, więc ten szablon zostanie użyty.

Oto wyniki działania programu z listingu 8.14:

wyliczanie rzeczy pana E.:szablon A13 31 103 301 310 130Wyliczanie d�ugów pana E.:szablon B2400 1300 1800

Szablony funkcji 387

Gdybyśmy z programu usunęli szablon B, kompilator użyłby do pokazania zawartości pd szablonu A,więc pokazane zostałaby adresy, a nie wartości. Łatwo to sprawdzić samemu.

Krótko mówiąc, proces rozwiązywania przeciążeń szuka najlepiej pasującej funkcji. Jeśli jest tylkojedna taka funkcja, jest ona wybierana. Jeżeli jest więcej niż jedna funkcja pasująca, ale tylko jedna z nichnie jest szablonem, ta jest wybierana. Gdy jest więcej kandydatów na dopasowanie i są to same szablony,a tylko jedna funkcja jest bardziej specjalizowana od reszty, to ta jest wybierana. Jeśli istnieją dwie lubwięcej dobrych funkcji niebędących szablonami, wszystkie są tak samo wyspecjalizowane, to wywołaniefunkcji jest niejednoznaczne i powoduje błąd. Kiedy nie ma żadnych pasujących wywołań, to oczywiściejest to błąd.

Prowokowanie dopasowaniaW niektórych sytuacjach można sprowokować pożądane dopasowanie przeciążenia poprzez odpowiednizapis wywołania funkcji. Weźmy program z listingu 8.15, który, nawiasem mówiąc, eliminuje prototypszablonu i zawiera definicję szablonu funkcji na samym początku pliku. Tak jak w przypadku zwyczajnychfunkcji, definicje funkcji szablonowych mogą pełnić równocześnie rolę własnego prototypu — jeśli funkcja(również szablon funkcji) jest definiowana przed miejscem pierwszego użycia, nie potrzebuje prototypu.

Listing 8.15. choices.cpp

// choices.cpp -- wybór szablonu#include <iostream>template<class T> // albo template <typename T>T lesser(T a, T b) // #1{ return a < b ? a : b;}

int lesser (int a, int b) // #2{ a = a < 0 ? -a : a; b = b < 0 ? -b : b; return a < b ? a : b;}

int main(){ using namespace std; int m = 20; int n = -30; double x = 15.5; double y = 25.9;

cout << lesser(m, n) << endl; // #2 cout << lesser(x, y) << endl; // #1 (z double jako T) cout << lesser<>(m, n) << endl; // #1 (z int jako T) cout << lesser<int>(x, y) << endl; // #1 (z int jako T) return 0;}

(Ostatnie wywołanie funkcji konwertuje argumenty typu double na typ int; niektóre kompilatorymogą w tym miejscu ostrzegać o ryzyku związanym z taką konwersją).

Oto wyniki działania programu z listingu 8.15:

2015.5-3015

388 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Program z listingu 8.15 zawiera szablon funkcji, która zwraca mniejszy z pary argumentówwywołania (w przypadku wersji zwyczajnej porównanie dotyczy wartości bezwzględnej argumentów).Jeśli definicja funkcji znajduje się przed miejscem jej pierwszego użycia, pełni rolę prototypu funkcji;można wtedy pominąć same prototypy. Weźmy pierwszą instrukcję wywołania funkcji:

cout << lesser(m, n) << endl; // #2

Typy obu argumentów wywołania funkcji pasują zarówno do szablonu, jak i do zwyczajnej wersjifunkcji, więc kompilator wybiera funkcję zwyczajną; wynikiem wywołania będzie tu 20.

Następne wywołanie funkcji pasuje z kolei do szablonu, przy T równym double:

cout << lesser(x, y) << endl; // #1 (z double jako T)

Z kolei w instrukcji:

cout << lesser<>(m, n) << endl; // #1 (z int jako T)

Obecność nawiasów ostrych przy nazwie funkcji (lesser<>(m, n)) oznacza, że kompilator powinienwybrać wersję szablonową, a nie zwykłą, więc kompilator po analizie typów argumentów (int) konkretyzujeszablon funkcji dla T równego int.

Wreszcie w ostatniej instrukcji wywołania:

cout << lesser<int>(x, y) << endl; // #1 (z int jako T)

mamy jawne żądanie konkretyzacji szablonu z typem int jako T i ta właśnie funkcja jest tu wywoływana.Wartości x i y są w ramach wywołania funkcji rzutowane na typ int, a sama funkcja zwraca takżewartość typu int, dlatego program wypisuje 15 zamiast 15.5.

Funkcje z różnymi typami argumentówWszystko się komplikuje jeszcze bardziej, jeśli mamy wywołanie funkcji z wieloma parametrami, którepasuje do wielu prototypów. Kompilator musi spróbować dopasować wszystkie parametry i jeśli znajdzieopcję lepszą od pozostałych, wybierze ją. Aby jedna funkcja była lepsza od innej, jej parametry musząbyć równie dobrze dopasowane, a chociaż jeden z nich musi być dopasowany lepiej.

W książce tej nie chcemy analizować procesu dopasowywania na trudnych przykładach. Opisanetu zasady pozwalają oceniać dopasowanie wszystkich możliwych prototypów i szablonów.

Ewolucja szablonów funkcjiU zarania kariery języka C++ większość programistów nie przykładała uwagi do szablonów jako skutecznegomechanizmu uogólniania kodu. Ale sprytni i dociekliwi programiści zdołali przesunąć granicę użytecznościszablonów w sposób wręcz niewyobrażalny. Uwagi programistów, którzy parali się na poważnieprogramowaniem z użyciem szablonów, zostały uwzględnione w standardzie C++98 oraz w dodatkachdo standardowej biblioteki szablonów. Postęp odbywał się dalej, a kolejni programiści eksplorująmożliwości programowania uogólnionego i niekiedy odbijają się od ograniczeń mechanizmu szablonów.Z kolei ich opinie i pomysły stanowią podstawę dla zmian zatwierdzonych w standardzie C++11.Warto pokrótce przedstawić podnoszone problemy i zaproponowane w standardzie rozwiązania.

Co to za typ?Przy pisaniu szablonu w C++98 nie zawsze można było łatwo ustalić typ do użycia w deklaracjachwystępujących w kodzie szablonu. Weźmy następujący (fragmentaryczny) przykład:

Szablony funkcji 389

template<class T1, class T2>void ft(T1 x, T2 y){ ... ?typ? xpy = x + y; ...}

Jaki powinien być deklarowany typ dla xpy? Nie wiadomo przecież, jak użytkownicy będą stosowaliszablon ft() i dla jakich typów będą go konkretyzować. Właściwy może się okazać typ T1, T2 albo wręczzupełnie inny typ. Jeśli na przykład T1 to double, a T2 to int, właściwym typem dla sumy byłby typdouble. A jeśli T1 to short, a T2 to int, właściwym typem dla sumy jest int. Dla T1 będącego short i T2będącego char dodawanie oznacza wykonanie automatycznej promocji całkowitoliczbowej, a typemwynikowym dodawania jest int. Co więcej, jeśli T1 i T2 nie są typami wbudowanymi, tylko np.strukturami czy klasami, to mogą mieć własne przeciążone operatory +, co jeszcze bardziej komplikujekwestię doboru typu wynikowego sumy. W C++98 dylemat wyboru typu dla xpy nie ma prostegorozwiązania.

Słowo decltype (C++11)W C++11 zaproponowano rozwiązanie w postaci nowego słowa kluczowego: decltype. Można je stosowaćw taki sposób:

int x;decltype(x) y; // y b�dzie tego samego typu co x

Argumentem wyrażenia decltype może być wyrażenie, więc w przykładzie z szablonem ft()można by rozwiązać problem typu xpy w następujący sposób:

decltype(x + y) xpy; // xpy b�dzie tego samego typu co wyraenie x + yxpy = x + y;

Instrukcje te można połączyć w deklarację z równoczesną inicjalizacją:

decltype(x + y) xpy = x + y;

Nasz szablon ft() możemy więc poprawić następująco:

template<class T1, class T2>void ft(T1 x, T2 y){ ... decltype(x + y) xpy = x + y; ...}

Słowo decltype jest narzędziem nieco bardziej skomplikowanym, niżby się zdawało na pierwszy rzutoka. Kompilator jest tu zmuszany do wykonania całej procedury wywnioskowania typu deklaracji.Załóżmy, że mamy następujący kod:

decltype(wyra�enie) zmienna;

Oto nieco uproszczona wersja koniecznej procedury wnioskowania.

Etap 1.: Jeśli wyrażenie jest prostym identyfikatorem nieujętym w nawias, to zmienna jest tegosamego typu co identyfikator, z wszystkimi kwalifikatorami, takimi jak const, włącznie:

390 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

double x = 5.5;double y = 7.9;double &rx = x;const double * pd;decltype(x) w; // w jest typu doubledecltype(rx) u = y; // u jest typu double &decltype(pd) v; // v jest typu const double *

Etap 2.: Jeśli wyrażenie jest wywołaniem funkcji, zmienna otrzyma typ zgodny z typem wartościzwracanej tej funkcji:

long indeed(int);decltype (indeed(3)) m; // m jest typu int

UwagaNie dochodzi tu bynajmniej do obliczenia warto�ci wyra�enia (poprzez realizacj� wywo�ania funkcji).Typ warto�ci zwracanej jest ustalany na podstawie prototypu funkcji; jest to wystarczaj�ce dojednoznacznego okre�lenia typu deklarowanej zmiennej.

Etap 3.: Jeśli wyrażenie jest l-wartością, to zmienna jest referencją do typu wyrażenia. Może sięwydawać, że reguła ta oznacza, iż wcześniejszy przykład z w powinien być rozstrzygnięty jako typ referencyjny,skoro w jest l-wartością. Pamiętajmy jednak, że przypadek zmiennej w został załatwiony już na 1. etapieprocedury i kompilator nie będzie jej kontynuował. Niniejszy etap dotyczy wyłącznie wyrażeń niebędącychprostymi identyfikatorami. A więc czym? Cóż, w pierwszej kolejności przychodzi na myśl identyfikatorujęty w nawias:

double xx = 4.4;decltype ((xx)) r2 = xx; // r2 jest typu double &decltype(xx) w = xx; // w jest typu double (dopasowanie z etapu 1.)

Co ciekawe, poza kontekstem decltype same nawiasy nie zmieniają ani wartości, ani l-wartościowościwyrażenia. Na przykład poniższe dwie instrukcje są traktowane jako tożsame:

xx = 98.6;(xx) = 98.6; // () nie wp�ywa na zachowanie xx

Etap 4.: Jeśli nie udało się rozstrzygnąć typu na poprzednich etapach, zmienna będzie tego samegotypu co wyrażenie:

int j = 3;int &k = jint &n = j;decltype(j+6) i1; // i1 jest typu intdecltype(100L) i2; // i2 jest typu longdecltype(k+n) i3; // i3 jest typu int

Zwróćmy uwagę, że chociaż k i n są referencjami, to wyrażenie k+n nie ma typu referencyjnego:to zwyczajna suma dwóch wartości typu int i jako taka ma typ int.

Jeśli potrzebna jest większa liczba deklaracji tego samego typu, można ze słowem kluczowymdecltype zastosować słowo typedef:

template<class T1, class T2>void ft(T1 x, T2 y){ ... typedef decltype(x + y) xytype; xytype xpy = x + y;

Szablony funkcji 391

xytype arr[10]; xytype & rxy = arr[2]; // rxy jest referencj� ...}

Alternatywna składnia funkcji (opóźniona deklaracja typu zwracanego)Mechanizm decltype sam w sobie nie eliminuje wszystkich powiązanych problemów deklaracji typóww szablonach. Weźmy na przykład taki (niekompletny) szablon funkcji:

template<class T1, class T2>?type? gt(T1 x, T2 y){ ... return x + y;}

Ponownie nie wiemy tu, jaki typ otrzymamy z dodawania x i y. Wydawałoby się, że deklaracjadecltype(x + y) mogłaby rozwiązać kwestię typu wartości zwracanej, ale niestety w tym miejscu w kodzieszablonu parametry x i y nie zostały jeszcze zadeklarowane, więc nie mamy ich (i ich typów) w zasięgudeklaracji typu zwracanego funkcji. Słowo decltype z wyrażeniem angażującym parametry funkcji możewystępować jedynie po zadeklarowaniu tych parametrów. Dlatego C++11 dopuszcza nową składniędeklarowania i definiowania funkcji. Dla typów wbudowanych działa to tak jak poniżej. Prototyp:

double h(int x, float y);

Można zapisać za pomocą składni alternatywnej:

auto h(int x, float y) -> double;

W ten sposób można przesunąć deklarację typu wartości zwracanej za deklaracje typów parametrówfunkcji. Kombinacja -> double z powyższego przykładu to tak zwana opóźniona deklaracja typu zwracanego(ang. trailing return type). Widać tu też kolejny element C++11 w postaci słowa auto, które pełni tu rolęsymbolu zastępczego dla właściwego typu wartości zwracanej z funkcji, podawanego za listą parametrów.Podobnie można zrealizować definicję funkcji:

auto h(int x, float y) -> double{/* cia�o funkcji */};

Połączenie tej składni ze słowem decltype pozwala na ostateczne rozwiązanie problemu definicjinaszego szablonu gt():

template<class T1, class T2>auto gt(T1 x, T2 y) -> decltype(x + y){ ... return x + y;}

Teraz decltype znajduje się za deklaracjami parametrów, więc x i y są nazwami w zasięgu kompilatorai mogą być użyte do określenia typu wartości zwracanej.

392 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

PodsumowanieObsługa funkcji w C++ jest znacznie rozbudowana względem języka C. Jeśli użyjemy słowa kluczowegoinline w definicji funkcji i definicję tę umieścimy przed pierwszym wywołaniem funkcji, sugerujemykompilatorowi uczynienie tej funkcji funkcją inline. Funkcje takie nie powodują skoku do odrębnejsekcji kodu, ale wywołanie funkcji jest zastępowane jej pełnym kodem. Cechę tę należy wykorzystywaćjedynie w odniesieniu do krótkich funkcji.

Zmienna referencyjna to swoista odmiana wskaźnika, która umożliwia tworzenie aliasu (czyli innejnazwy) zmiennej. Zmienne referencyjne są używane przede wszystkim jako parametry funkcjiprzekazujących struktury i obiekty klas. Normalnie identyfikator deklarowany jako referencja danegotypu może odnosić się tylko do danych tego typu. Jeśli jednak dana klasa jest pochodną innej klasy,jak klasa ofstream pochodząca od ostream, referencja typu bazowego może też odwoływać się do typupochodnego.

W prototypach funkcji C++ można definiować wartości domyślne parametrów. Jeśli w wywołaniufunkcji takie parametry zostaną pominięte, program użyje ich wartości domyślnych. Jeżeli wywołaniefunkcji zawiera wartość parametru, program użyje tej wartości, a nie wartości domyślnej. Parametrydomyślne mogą występować tylko po prawej stronie listy parametrów. Jeśli zatem podamy wartośćdomyślną jakiegoś parametru, musimy podać też wartości wszystkich następnych parametrów.

Sygnatura funkcji to lista jej parametrów. Można zdefiniować dwie funkcje o takiej samej nazwie,o ile tylko mają różne sygnatury. Nazywamy to polimorfizmem funkcji lub przeciążaniem funkcji.Zwykle przeciąża się funkcje realizujące takie same zadania na różnych typach danych.

Szablony funkcji automatyzują proces przeciążania funkcji. Definiuje się funkcję, używając typuogólnego oraz konkretnego algorytmu, a kompilator generuje później potrzebne definicje funkcjiz konkretnymi typami danych używanymi w programie.

Pytania sprawdzaj�ce1. Jakie funkcje są dobrymi kandydatami na funkcje inline?

2. Załóżmy, że funkcja piosenka() ma następujący prototyp:void piosenka(const char* tytul, int razy);

a) Jak zmodyfikować prototyp, aby parametrowi razy nadać wartość domyślną 1?b) Jakie zmiany trzeba byłoby wtedy wprowadzić w definicji funkcji?c) Czy można parametrowi tytul nadać wartość domyślną "�pij, aniele mój"?

3. Napisz przeciążoną wersję funkcji iquote() wyświetlającej parametr ujęty w podwójnycudzysłów. Napisz trzy jej wersje: dla liczb int, dla liczb double oraz dla napisów string.

4. Oto szablon struktury:struct pudelko{ char producent[40]; float wysokosc; float szerokosc; float dlugosc; float pojemnosc;};

a) Napisz funkcję, która będzie miała jako parametr referencję struktury pudelko oraz którapokaże wartości wszystkich pól.

wiczenia programistyczne 393

b) Napisz funkcję, która będzie miała jako parametr referencję struktury pudelko oraz którawyliczy wartość pola pojemnosc jako iloczyn pozostałych wymiarów.

5. Jak należałoby zmienić listing 7.15, aby funkcje fill() i show() korzystały z parametrów typureferencyjnego?

6. Poniżej opisane są pewne pożądane zachowania. Wskaż, czy można je osiągnąć za pomocąparametrów domyślnych, przeciążania funkcji, jednego i drugiego, czy wcale. Podaj odpowiednieprototypy.

a) masa(gestosc, objetosc) zwraca masę obiektu o zadanej gęstości i objętości. masa(gestosc)zwraca masę objętości 1 metra sześciennego przy ustalonej gęstości. Wszystkie wielkościsą typu double.

b) repeat(10, "Czuj� si� �wietnie") pokazuje wskazany łańcuch 10-krotnie,a repeat("Ale ty jeste� g�upi.") wyświetla podany łańcuch 5-krotnie.

c) srednia(3,6) zwraca średnią dwóch parametrów int; srednia(3.0, 6.0) zwraca średniąwartości typu double jako double.

d) Wywołanie mangle("Mi�o mi ci� widzie�") zwraca znak M lub wskaźnik łańcucha"Mi�o mi ci� widzie�" w zależności od tego, czy wynik jest przypisywany zmiennej char,czy zmiennej char*.

7. Napisz szablon funkcji zwracającej większy z jej parametrów.

8. Mając szablon z poprzedniego ćwiczenia oraz strukturę pudelko z ćwiczenia 4., podaj specjalizacjęszablonu, która będzie pobierała dwa parametry typu pudelko i zwracała jeden obiekt pudełkoo większej pojemności.

9. Jakie typy zostaną przypisane do zmiennych v1, v2, v3, v4 i v5 w poniższym kodzie (załóżmy,że jest to fragment kompletnego programu)?

int g(int x);...float m = 5.5f;float & rm = m;decltype(m) v1 = m;decltype(rm) v2 = m;decltype((m)) v3 = m;decltype (g(100)) v4;decltype (2.0 * m) v5;

wiczenia programistyczne1. Napisz funkcję, która normalnie pobiera jeden parametr, adres łańcucha, po czym zaraz pokazuje

ten łańcuch. Jeśli jednak podany zostanie niezerowy drugi parametr, napis ma się pojawić tylerazy, ile razy dotąd wywołano tę funkcję. Zauważmy, że drugi parametr nie mówi, ile razy należypokazać napis. Owszem, funkcja jest nieco bzdurna, ale jej napisanie będzie dobrym ćwiczeniemutrwalającym wiedzę. Użyj opisanej funkcji w prostym programie, który pokaże jej działanie.

2. Struktura Batonik ma trzy pola: markę producenta, wagę (z częścią ułamkową) oraz liczbę kalorii(całkowitoliczbowo). Napisz program korzystający z funkcji, której parametry to referencjado typu Batonik, wskaźnik do typu char, wartość double oraz wartość int; funkcja ma używaćostatnich trzech wartości do ustawienia odpowiednich pól struktury. Ostatnie trzy parametry

394 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

mają mieć wartości domyślne "Millennium Munch", 2,85 i 350. Poza tym program powinienkorzystać z funkcji pobierającej referencję do Batonika i wyświetlającej zawartość struktury.Należy użyć w miarę potrzeb const.

3. Napisz funkcję pobierającą referencję do obiektu string jako parametr i zamieniającą zawartośćtego łańcucha na wielkie litery. Użyj funkcji toupper() opisanej w tabeli 6.4. Napisz program,który w pętli pozwoli przećwiczyć wprowadzanie danych do tej funkcji. Oto wynik przykładowegouruchomienia tego kodu:

Podaj �a�cuch (q, aby sko�czy�): odejd� st�dODEJD ST�DNast�pny �a�cuch (q, aby sko�czy�): niez�y pasztet!NIEZY PASZTET!Nast�pny �a�cuch (q, aby sko�czy�): qDo widzenia

4. Oto szkielet pewnego programu:#include <iostream>using namespace std;#include <cstring> // dla strlen(), strcpy()struct stringy { char * str; // wskazuje �a�cuch int ct; // d�ugo�� �a�cucha (bez \0')};

// tutaj prototypy set(), show() i show(){ stringy beany; char testing[] = "Rzeczywisto�� to ju� nie to, co kiedy�.";

set(beany, testing); // pierwszy parametr jako referencja, // alokacja pami�ci na wynik sprawdzania, // ustawienie pola str struktury beany tak, by wskazywa�a nowy blok; // kopiowanie testing do nowego bloku, // ustawienie pola ct zmiennej beany show(beany); // pokazuje napis z pola raz show(beany, 2); // pokazuje napis z pola dwukrotnie testing[0] = 'D'; testing[1] = 'u'; show(testing); // pokazuje �a�cuch testing raz show(testing, 3); // pokazuje �a�cuch testing trzykrotnie show("Gotowe!"); return 0;}

Uzupełnij powyższy szkielet, definiując opisane funkcje i dodając im prototypy. Zauważmy,że potrzebne są dwie funkcje show(), obie z parametrami domyślnymi. W razie potrzeby użyjparametrów const. Zauważmy, że set() powinno korzystać z new do zaalokowania pamięci nałańcuch. Użyte tutaj techniki są podobne jak w przypadku projektowania i implementacji klas(w niektórych kompilatorach konieczna może być zmiana nazw plików nagłówkowychi usunięcie dyrektywy using).

5. Napisz szablon funkcji max5() pobierającej jako parametr tablicę pięciu wartości typu T, zwracającejnajwiększy element z tablicy. Wielkość tablicy jest ustalona, można ją na stałe zapisać w kodziepętli, bez przekazywania jako parametr. Przetestuj program przy użyciu funkcji z tablicą pięciuwartości int i pięciu wartości double.

6. Napisz szablon funkcji maxn() pobierającej jako parametry tablicę typu T oraz liczbę elementówtej tablicy, zwracającej największy element tablicy. Przetestuj w programie 5-elementową tablicęwartości int i 4-elementową tablicę double. Program powinien korzystać ze specjalizacji przyjmującej

wiczenia programistyczne 395

jako parametr tablicę typu char, zwracającej adres najdłuższego napisu. Jeśli jest kilka najdłuższychłańcuchów, funkcja powinna zwracać adres pierwszego z nich. Sprawdź specjalizację z tablicąpięciu łańcuchów.

7. Zmodyfikuj listing 8.14 tak, aby stosował dwa szablony funkcji o nazwie SumArray(), zwracającejsumę elementów tablicy, a nie wypisującej zawartości tej tablicy. Program powinien podawać teżłączną liczbę przedmiotów i sumę zadłużenia.

396 Rozdzia� 8. Funkcje — zagadnienia zaawansowane

Skorowidz

A

abstrakcja, 449abstrakcyjne

klasy bazowe, 657, 665, 707typy danych, 488

adaptatory, 892adaptery, 1030adres

danych, 304funkcji, 326łańcucha, 173struktury, 317tablicy, 167zmiennej, 154

ADT, Abstract Data Type, 488agregacja, 687–696, 753algorytm, 895

działający w miejscu, 896generate(), 1024konwersji, 113kopiujący, 896next_permutation(), 897remove(), 898sort(), 897

aliasy typów (C++11) , 218, 752alokacja pamięci, 158, 356

automatyczna, 177dynamiczna, 178statyczna, 178

alokatory, 848analiza

przypadków użycia, 1045szablonu klasy, 729

ANSI C, 38, 101, 409aplikacje wielowątkowe, 1046

argumentypozatypowe, 735pozatypowe szablonu, 734przekazywane przez referencję,

846wiersza polecenia, 969

arytmetyka wskaźników, 165, 169ASCII, 1059asembler, 33automatyczna dedukcja typów, 116,

331, 862automatyczne

deklaracje typów, 116konwersje typów, 285

autoprzypisanie, 570

Bbajt, 82białe znaki, 58, 127biblioteka

algorytmów, 895algorytmów STL, 1111arytmetyki liczb rzeczywistych,

1040Boost.Filesystem, 1044Boost.Math, 1044cctype, 251cstring, 174iostream, 514lokalizacji, 931matematyczna, 318standardowa, 38string, 835szablonów, 1099

szablonów STL, 38, 185, 1043,1151

wejścia-wyjścia, 922wyrażeń regularnych, 1041

bibliotekiC++, 39funkcji, 65klas, 65projektu Boost, 1043

bit, 82blok, 178, 203

catch, 782, 786, 793try, 782, 786, 793

błąd, 44kompilacji, 1023niedomiaru, 797niezgodności typów, 159ochrony pamięci, general

protection fault, 560błądzenie losowe, random walk, 530błędne

dane wejściowe, 263przypisanie, 361

błędyprzydziału pamięci, 798w programie, 562

Borland C++ Builder, 103brak łączenia, 417budowa programu, 40bufor, 920, 991bufor wyjściowy, 930

Cciągi znaków, 577cin, 63, 73, 262cout, console output, 55, 62, 267

1180 J�zyk C++. Szko�a programowania

CRC,Class/Responsibilities/Collaborators, 1045

czas wykonywania, 155czas życia, 403

Ddane

wejściowe z klawiatury, 995wyjściowe, 935, 959

dedukcja automatyczna typów, 850,862

definicja, 60funkcji, 71, 281, 333funkcji specjalizowana, 378klasy, 675klasy Queue, 617konstruktora, 464operatora przypisania, 570skonkretyzowanej wersji

szablonu, 740szablonu klasy, 724z inicjalizacją, 486

deklaracja, 999aliasów, 333const, 101definiująca, 60, 412klasy, 454, 492, 759klasy Stock, 469, 480lokalna, 145nazwy tablicy jako parametru, 291przestrzeni nazw, 436przyjaźni, 510, 765referencji, 60, 412struktury, 142using, 54, 432, 706wskaźnika, 156, 168wskaźnika na funkcję, 326wyprzedzająca, 765–772zewnętrzna, 144, 196zmiennej, 60

dekrementacja, 199dekrementacja przedrostkowa, 202delegowanie konstruktorów, 1021dereferencja, wyłuskanie, 55, 202destruktor, 463, 475, 675, 681

domyślny, 562klasy, 468, 470klasy StringBad, 558wirtualny, 640, 648, 653

długośćłańcucha, 140tablicy, 128

dobór typu, 89dodawanie

wektorów, 520wskaźników, 166

dokumentacja, 105dołączanie, 136

danych do pliku, 974pliku, 973

domyślny operator przypisania, 674dopasowania prototypów, 383dostęp do

deklaracji klasy, 471elementów tablic, 168funkcji zaprzyjaźnionych, 700klas zagnieżdżonych, 774łańcucha, 1087metod klasy bazowej, 699obiektów klasy bazowej, 699pamięci obiektu, 451pól struktury, 175przestrzeni nazw std, 75składowych, 454, 671, 693składowych klasy, 453, 656, 774składowych prywatnych, 628wskaźnika this, 479zmiennej przez referencję, 1028zmiennej przez wartość, 1028

dostępswobodny, 981swobodny do pliku binarnego,

985w klasie zagnieżdżonej, 775

dynamiczny przydział pamięci, 584,665, 669

dyrektywa#define, 1145, 1147#endif, 1145#ifndef, 451, 1145#include, 52, 398, 1145#pragma, 1145using, 54, 74, 432–434

dyrektywy preprocesora, 1145działanie decltype, 1000dziedziczenie, 361, 626, 683, 712

chronione, 704konstruktorów, 1021pojedyncze, 715prywatne, 697, 700–704

publiczne, 636, 678, 687wielokrotne, 698, 706, 714, 720

dzielenie, 108

Eedytory, 40efekt uboczny, side effect, 200efekty uboczne wyrażenia, 194egzemplarz, 452elastyczność unii, 1041element języka, token, 58elementy struktury, 143endl, 56enumerator, 152EOF, End Of File, 224–226, 273etykieta

case, 258protected, 453

FFIFO, first in, first out, 598flushing, 920formatowanie, 363, 939

danych wyjściowych, 931wewnętrzne, 988, 989

formaty domyślne dla obiektu cout,932

funkcja, 34, 76, 280, 333, Patrz takżerodzina funkcjiabort(), 779accumulate(), 1139adjacent_difference(), 1140adjacent_find(), 1114all_of() (C++11), 1113any_of() (C++11), 1113atan(), 314atan2(), 314binary_search(), 1130cin.clear(), 229cin.get(), 227, 957cin.get(ch) a cin.get(), 230cin.get(char), 223cin.get(void), 956cin.peek(), 961clock(), 217copy(), 1118copy_backward(), 1119copy_if() (C++11), 1119copy_n() (C++11), 1118

Skorowidz 1181

count(), 1114count_if(), 1024, 1115counts(), 747cout.put(), 94equal(), 1115equal_range(), 1130exit(), 779file_it(), 363fill(), 322, 1121fill_array(), 300fill_n(), 1121find(), 832, 1113funkcja find_ar(), 860find_end(), 1114find_first_of(), 1114find_if(), 1113find_if_not() (C++11), 1113flush(), 930for_each(), 854, 1113free(), 1149generate(), 1024, 1121generate_n(), 1121get(), 132, 957get(ch), 954get(void), 955getline(), 131, 828, 957getname(), 176, 178hmean(), 780ignore(), 958includes(), 1132inner_product(), 1139inplace_merge(), 1131insert(), 879iota() (C++11), 1140is_heap() (C++11), 1135is_heap_until() (C++11), 1135is_int(), 250is_partitioned() (C++11), 1124is_permutation() (C++11), 1115is_sorted() (C++11), 1129is_sorted_until() (C++11), 1129iter_swap(), 1120left(), 367lexicographical_compare(), 1137longjmp(), 1149lower_bound(), 1129main(), 49, 70, 283make_heap(), 1134malloc(), 159, 1149marm(), 788max(), 1136max_element(), 1137

merge(), 1131min(), 1135min_element(), 1136minmax() (C++11), 1136minmax_element() (C++11), 1137mismatch(), 1115move() (C++11), 1119move_backward() (C++11), 1119n_chars(), 287, 289nagłówek, 76next_permutation(), 1138none_of() (C++11), 1113nth_element(), 1129operator()(), 887operator()[], 797operator<(), 573, 855operator<<(), 693, 923, 925operator>>(), 829, 946partial_sort(), 1128partial_sum(), 1140partition(), 1125partition_copy() (C++11), 1125partition_point() (C++11), 1125peek(), 961pop_heap(), 1134prev_permutation(), 1138print(), 367printf(), 62push_back(), 876push_front(), 876push_heap(), 1134putback(), 961putchar(), 228rand(), 531random_shuffle (), 1124rbegin(), 868read(), 960remodel(), 840remove(), 1122remove_copy(), 1122remove_copy_if(), 1122remove_if(), 1122replace(), 1120replace_copy(), 1121replace_copy_if(), 1121replace_if(), 1120report(), 747reverse(), 1123reverse_copy(), 1123rotate(), 1123rotate_copy(), 1124Say(), 810

search(), 1116search_n(), 1116seekg(), 982–985, 992seekp(), 992set_difference(), 1133set_intersection(), 1132set_symmetric_difference (), 1133set_terminate(), 804set_unexpected(), 806set_union(), 882, 1132setf(), 940, 941, 943setFormat(), 644setjmp(), 1149show_array(), 297show_polar(), 317shuffle(), 1124sort(), 854, 1128sort_heap(), 1134Speak(), 810splice(), 879sqrt(), 66srand(), 531stable_partition(), 1125stable_sort(), 1128std::move(), 1018stonetolb(), 73strcat(), 139strchr(), 720strcmp(), 211, 212strcpy(), 171, 173, 558strlen(), 128, 139, 171, 367, 558strncpy(), 173Student::sum(), 705subdivide(), 325Sum(), 501swap(), 1119Swap(), 374swap_ranges(), 1120terminate(), 804time(), 531tooBig(), 889toupper(), 394transform(), 891, 1120unexpected(), 805unique(), 1123unique_copy(), 1123unsetf(), 943upper_bound(), 1130Worker::Show(), 715WorseThan(), 855write(), 928, 978

1182 J�zyk C++. Szko�a programowania

funkcjealokacji, 425alokacji miejscowej, 429bezparametrowe, 69biblioteczne, 68czysto wirtualne, 658dwuargumentowe, 288inline, 339–342, 770konwersji, 539–543, 597, 675lambda, 1024łączone wewnętrznie, 770łączone zewnętrznie, 770nieskładowe, 518operatora, 499operatora new, 425niezwracające wartości, 281porównania klasy string, 1091predefiniowane, 70przeciążające operatory, 518, 547przetwarzające łańcuch, 308przetwarzające obiekty, 318przetwarzające struktury, 310przetwarzające tablice, 291rekurencyjne, 322składowe, 94składowe kontenera list, 877STL, 1111typu void, 281użytkownika, 70, 73wejścia, 957wejścia formatowanego, 946wejścia jednoznakowe, 954wejścia nieformatowanego, 954wirtualne, 651wywołujące, 66wywoływane, 66z biblioteki STL, 1099z parametrami w postaci

wskaźnika, 634z rodziny cctype, 252z zakresami tablic, 301zaprzyjaźnione, 509, 545, 654,

681, 772zaprzyjaźnione spoza szablonu,

746zaprzyjaźnione z klasą hasDMA,

671zwracające łańcuchy, 309zwracające wartości, 66, 281

funkcjidefiniowanie, 281prototypowanie, 283wywoływanie, 66, 283

funktor, 887, 892, 1025adaptowalny, 892dwuargumentowy, 888jednoargumentowy, 888multiplies(), 893predefiniowany, 891

Ggeneratory liczb pseudolosowych,

1040globalna przestrzeń nazw, 431głęboka kopia obiektu, deep copy,

567

Hhermetyzacja, encapsulation, 453hermetyzacja danych, 462hierarchia

iteratorów, 865klas, 716klas wyjątków, 794

IIDE, Integrated Development

Environment, 40, 970identyfikator MAX_LENGTH, 1146identyfikatory override i final, 1057idiom funkcji tablicowych, 300idiomy programistyczne, 441implementacja

Apple Xcode, 40Borland 5.5, 40Digital Mars, 40Digital Mars C++, 40Embarcadero C++ Builder, 40Freescale CodeWarrior, 40GNU C++, 40hierarchii Sales, 801IBM XL C/C++, 40klasy Brass, 640klasy kolejki, 600klasy Stock, 456klasy type_info, 813konstruktora, 582kopiującego operatora

przypisania, 1014metod klas, 455Microsoft Visual C++, 40

Open Watcom C++, 40StringBad, 560

indeks, 122indeksowanie, 906indeksowanie ciągu, 574inicjalizacja, 85

elementów tablicy, 482klamrowa, 125listą w C++11, 474listą, list initialization, 112łańcuchów, 136obiektów, 538, 589obiektu klasy string, 827, 1083operatorem new, 424składowych klas, 1003składowych klas w C++11, 604struktur w C++11, 145tablic, 124, 173tablic dwuwymiarowych, 232tablic w C++11, 125tablicy łańcuchem, 127w C++11, 86wskaźników, 156, 159zawieranych obiektów, 692zmiennych automatycznych, 407zmiennych statycznych, 411

inicjalizatory, 616inkrementacja, 199

i dekrementacja wskaźników, 202przedrostkowa, 202

instrukcja, 59, 76, 194break, 257–260, 580cin.get(), 63continue, 259goto, 1148if, 237if else, 239przypisania, 61pusta, 217return, 73, 282switch, 255typedef, 218

instrukcjedeklaracji, 60niewyrażeniowe, 195wyrażeniowe, 195

inteligentny wskaźnik, 837, 845,1002, 1150

interfejs, 451funkcji, 50, 291klasy kolejki, 599zawieranych obiektów, 693

Skorowidz 1183

ISO 10646, 98ISO 8859-2, 92iterator, 858, 862

back_insert_iterator, 869front_insert_iterator, 869insert_iterator, 869ostream_iterator, 867reverse_iterator, 876

iteratorybiblioteki STL, 867dostępu swobodnego, 864dwukierunkowe, 864odwrotne, 868postępujące, 864wejściowe, 863wyjściowe, 863

Jjawna

kwalifikacja std::vector, 182specjalizacja, 740

jawne typowanie zmiennych, 116jawny konstruktor kopiujący, 567jednostka translacji, 402język

C, 32maszynowy, 39

językimodelowania, 1045niskiego poziomu, 33proceduralne, 33wysokiego poziomu, 33

Kkarty CRC, 1045kasowanie bitu, 940kategorie przydziału, 410klasa, 64, 447, 450

array, 182, 185, 881bad_index, 800baseDMA, 667Brass, 637, 657BrassPlus, 639, 656Circle, 658Customer, 609Ellipse, 657exception, 796forward_list, 879fstream, 981

hasDMA, 667, 668ifstream, 271ios, 921, 933ios_base, 921, 933, 968iostream, 921istream, 65, 270, 921, 946, 947istringstream, 990, 991logic_error, 800Magnificent, 810multimap, 885nbad_index, 801Node, 774ofstream, 267, 965, 982ostream, 65, 512, 921–932ostringstream, 991priority_queue, 880queue, 880Queue, 598, 608, 859set, 882SingingWaiter, 712Someclass, 1019, 1020stack, 880Stack, 620, 724std::initializer_list, 999Stock, 452, 468, 477, 488

plik definicji klasy, 469plik klienta, 471plik nagłówkowy, 468

Stonewt, 535, 543streambuf, 921string, 135, 185, 318, 823, 911,

1079–97, 1151funkcje porównania, 1091funkcje wyszukujące, 1089konstruktory, 1082–1086metody, 1081, 1087, 1091modyfikatory łańcuchów,

1093–1096typy, 1080wejście i wyjście, 139, 1096

StringBad, 554, 557, 562Student, 689–702TableTennisPlayer, 625Time, 500, 502, 512Time z funkcjami

zaprzyjaźnionymi, 515type_info, 813Useless, 1013valarray, 688, 903vector, 181, 847–853, 903Vector, 519–532Worker, 711

klasy, 34, 624adaptatorów, 893bazowe, 361, 624, 679

abstrakcyjne, 657, 665niewirtualne, 722wirtualne, 722

kontenerowe, 861, 912pochodne, 361, 624, 631–635, 679

bez dynamicznego przydziałupamięci, 665

z dynamicznym przydziałempamięci, 666

szablonowe, 181, 1079wejścia-wyjścia, 921wyjątków stdexcept, 796z new, 597zagnieżdżone, 773, 820zaprzyjaźnione, 764, 771

klasyfikacjaalgorytmów, 896typów danych, 106

klient, 460kod

ASCII, 92błędu, 780liczbowy znaku, 95wykonywalny, 39wynikowy, 39źródłowy, 39

kody wyjścia, 72kolejka

FIFO, 598LIFO, 407, 598

kolejkidołączanie elementu, 605usuwanie elementu, 605, 607

kolejnośćdziałań, 107inicjalizacji, 693

kombinacje typów, 179komentarze, 47, 51Komitet Standaryzacji C++, 1042kompilacja, 39–44

diagnostyczna, 915produkcyjna, 915programu, 400rozłączna, 397

kompilatorbcc32.exe, 399CC, 42cfront, 42g++, 42, 43

1184 J�zyk C++. Szko�a programowania

komunikat abnormal programtermination, 779

komunikatybłędów kompilatora, 44diagnostyczne, 571, 1015

koniecłańcucha, 130pliku, 224

konkatenacja, 136konkretyzacja, 380

jawna, explicit instantiation, 380,754, 740

niejawna, implicit instantiation,381, 754, 739

szablonu use_f(), 1032konsola, 265konsolidacja, 41–44konsolidacja bibliotek, 403konstruktor, 475, 628, 675, 713, 757

klasy, 464basic_string, 1082bazowej, 653BrassPlus, 642pochodnej, 630, 653Queue, 602RatedPlayer, 629Stonewt, 544string, 824StringBad, 557

kopiujący, 562–565, 571, 582, 673jawny, 567klasy baseDMA, 667klasy bazowej, 668

przenoszący, move constructor,563, 1008–1013

Stonewt(double), 545konstruktory

bezpośrednich klas bazowych, 713domyślne, 466, 673, 826, 1021domyślne nowe, 572konwersji, 675kopiujące, 566operujące

na fragmentach łańcuchów C,1084

na klasycznych łańcuchach C,1083

na liście inicjalizującej, 1086na referencji l-wartościowej,

1084na referencji r-wartościowej,

1085

w C++11, 827wykorzystujące

n kopii znaku, 1086zakres, 1086

kontenerforward_list, 879list, 877map, 901queue, 880vector, 876, 877

kontenery, 872asocjacyjne, 881, 1108asocjacyjne nieporządkujące, 1109

operacje dodatkowe, 1110typy, 1110

asocjacyjne nieuporządkowane,887

metody, 1103odwracalne, reversible container,

876, 1103sekwencyjne, 874, 1104

operacje dodatkowe, 1105składowe dodatkowe, 1104STL, 1005typy i operacje, 1103w C++11, 1099w C++98, 1100wspólne składowe, 1101

kontroladostępu, 453, 775poprawności kodu, 44strumienia, 968

konwersja, 675liczb, 111na typ zmiennej, 111niejawna, implicit conversion, 536odwrotna, 539przy inicjalizacji, 111przy inicjalizacji klamrowej

(C++11), 112przy przypisaniu, 111w wyrażeniach, 113

konwersjeautomatyczne, 534podczas przekazywania

parametrów, 114typów, 110–113, 537–542

kopia łańcucha, 173kopiowanie, 566

głębokie, 568obiektów, 583

składowa po składowej, 584

kowariancja zwracanego typu, 655książki i witryny, 1141kwalifikator

const, 100, 420cv, 420std::, 804

kwalifikowana nazwa metody, 455

Llambda, 1025, 1028leksemy alternatywne, 1056liczba znaków w łańcuchu, 198liczbydwójkowe, 1052

dziesiętne, 1051ósemkowe, 1051pseudolosowe, 533, 903szesnastkowe, 1052zespolone, 550, 903zmiennoprzecinkowe, 101, 936

sposoby zapisu, 102zalety i wady, 105

licznikobiektów, 557, 571pętli, 198

LIFO, last in, first out, 178, 407, 598lista, 600

inicjalizacyjna, 124, 474, 630, 998inicjalizacyjna konstruktora, 603parametrów funkcji, 1035parametrów szablonu, 1035typów wyjątków, 788wyrażeń inicjalizujących, 483

listingacctabc.cpp, 661acctabc.h, 659addpntrs.cpp, 165address.cpp, 154and.cpp, 245append.cpp, 974arfupt.cpp, 331arith.cpp, 107arraynew.cpp, 164arrayone.cpp, 123arraytp.h, 734arrfun1.cpp, 292arrfun2.cpp, 294arrfun3.cpp, 298arrfun4.cpp, 301arrobj.cpp, 320arrstruc.cpp, 148

Skorowidz 1185

assgn_st.cpp, 146assign.cpp, 112autoscp.cpp, 405bank.cpp, 613bigstep.cpp, 198binary.cpp, 979block.cpp, 204bondini.cpp, 97brass.cpp, 640brass.h, 638calling.cpp, 280carrots.cpp, 59cctypes.cpp, 252chartype.cpp, 92check_it.cpp, 948choices.cpp, 183, 387cinexcp.cpp, 951cinfish.cpp, 262cingolf.cpp, 264compstr1.cpp, 211compstr2.cpp, 213condit.cpp, 254constcast.cpp, 818convert.cpp, 73coordin.h, 399copyit.cpp, 869count.cpp, 970cubes.cpp, 348defaults.cpp, 932delete.cpp, 176divide.cpp, 108dma.cpp, 670dma.h, 669dowhile.cpp, 220enum.cpp, 258equal.cpp, 209error1.cpp, 779error2.cpp, 781error3.cpp, 782error4.cpp, 786error5.cpp, 790exc_mean.cpp, 786exceed.cpp, 87express.cpp, 194external.cpp, 413file1.cpp, 401file2.cpp, 402filefunc.cpp, 362fileio.cpp, 967fill.cpp, 936firstref.cpp, 343floatnum.cpp, 104

fltadd.cpp, 105forloop.cpp, 190formore.cpp, 196forstr1.cpp, 199forstr2.cpp, 205fowl.cpp, 842frnd2tmp.cpp, 748fun_ptr.cpp, 328funadap.cpp, 894functor.cpp, 889funtemp.cpp, 374get_fun.cpp, 958getinfo.cpp, 63hangman.cpp, 832hexoct1.cpp, 90hexoct2.cpp, 91if.cpp, 238ifelse.cpp, 240ifelseif.cpp, 242ilist.cpp, 910init_ptr.cpp, 158inline.cpp, 341inserts.cpp, 870instr1.cpp, 129instr2.cpp, 131instr3.cpp, 133iomanip.cpp, 945jump.cpp, 260lambda0.cpp, 1026lambda1.cpp, 1029left.cpp, 366leftover.cpp, 370lexcast.cpp, 1043limits.cpp, 83list.cpp, 878listrmv.cpp, 898lotto.cpp, 290manip.cpp, 933manyfrnd.cpp, 752mixtypes.cpp, 181modulus.cpp, 110more_and.cpp, 247morechar.cpp, 93multimap.cpp, 886myfirst.cpp, 48mytime0.cpp, 500mytime0.h, 499mytime1.cpp, 502mytime1.h, 502mytime2.cpp, 507mytime2.h, 507mytime3.cpp, 516

mytime3.h, 515namesp.cpp, 438namesp.h, 438nested.cpp, 232, 778newexcp.cpp, 798newplace.cpp, 426newstrct.cpp, 175not.cpp, 249num_test.cpp, 191numstr.cpp, 134or.cpp, 244ourfunc.cpp, 70outfile.cpp, 268pairs.cpp, 738peeker.cpp, 962placenew1.cpp, 592placenew2.cpp, 595plus_one.cpp, 199pointer.cpp, 155precise.cpp, 936protos.cpp, 283ptrstr.cpp, 171queue.cpp, 610queue.h, 609queuetp.h, 776random.cpp, 985randwalk.cpp, 531recur.cpp, 323rtti1.cpp, 811rtti2.cpp, 814ruler.cpp, 324rvref.cpp, 1006sales.cpp, 801sales.h, 800sayings1.cpp, 579sayings2.cpp, 587secref.cpp, 344setf.cpp, 939setf2.cpp, 941setops.cpp, 883showpt.cpp, 937smrtptrs.cpp, 840somedefs.h, 1030sqrt.cpp, 68stack.cpp, 489stack.h, 488stacker.cpp, 490stacktem.cpp, 727stacktp.h, 726static.cpp, 418stcktp1.h, 731stdmove.cpp, 1015

1186 J�zyk C++. Szko�a programowania

listingstkoptr1.cpp, 732stock00.cpp, 456stock10.h, 469stock20.cpp, 480stock20.h, 480stocks00.cpp, 452stone.cpp, 538stone1.cpp, 542stonewt.cpp, 535stonewt.h, 535stonewt1.cpp, 541stonewt1.h, 540str1.cpp, 824str2.cpp, 836strc_ref.cpp, 352strctfun.cpp, 315strctptr.cpp, 317strfile.cpp, 829strgback.cpp, 309strgfun.cpp, 308strgstl.cpp, 897strin.cpp, 990string1.cpp, 577string1.h, 577strings.cpp, 128strngbad.cpp, 555strngbad.h, 554strout.cpp, 989strquote.cpp, 358strtype1.cpp, 135strtype2.cpp, 137strtype3.cpp, 138strtype4.cpp, 139structur.cpp, 144studentc.cpp, 694studentc.h, 691studenti.cpp, 701studenti.h, 698sumafile.cpp, 271support.cpp, 413swaps.cpp, 345switch.cpp, 256tabtenn0.cpp, 625tabtenn0.h, 624tabtenn1.cpp, 632tabtenn1.h, 631tempmemb.cpp, 742tempover.cpp, 385tempparm.cpp, 745textin1.cpp, 222textin2.cpp, 223

textin3.cpp, 226textin4.cpp, 229tmp2tmp.cpp, 750topfive.cpp, 319travel.cpp, 311truncate.cpp, 963tv.cpp, 766tv.h, 765tvfm.h, 769twoarg.cpp, 288twod.cpp, 737twofile1.cpp, 416twoswap.cpp, 379twotemps.cpp, 375typecast.cpp, 115use_new.cpp, 160use_sales.cpp, 802use_stuc.cpp, 696use_stui.cpp, 702use_tv.cpp, 767usealgo.cpp, 901usebrass1.cpp, 644usebrass2.cpp, 646usebrass3.cpp, 664usedma.cpp, 672useless.cpp, 1008usestock0.cpp, 460usestock1.cpp, 471usestock2.cpp, 483usetime0.cpp, 501usetime1.cpp, 503usetime2.cpp, 508usett0.cpp, 626usett1.cpp, 632valvect.cpp, 905variadic1.cpp, 1037variadic2.cpp, 1039vect.cpp, 522vect.h, 521vect1.cpp, 848vect2.cpp, 851vect3.cpp, 855vegnews.cpp, 559vslice.cpp, 907waiting.cpp, 218while.cpp, 214width.cpp, 934worker0.cpp, 708worker0.h, 707workermi.cpp, 718workermi.h, 716workmi.cpp, 720

worktest.cpp, 709wrapped.cpp, 1033write.cpp, 928

literał nullptr, 1001literały

całkowitoliczbowe, 90literały łańcuchowe, 172znakowe, 307

l-wartość, 350l-wartość modyfikowalna, 350

Łłańcuch, 185

jako parametr, 307literalny, 141w stylu C, 307w tablicy, 128znakowy, 55

łańcuchowedane wejściowe, 959funkcje wejścia, 957

łańcuchy, 126łączenie, linkage, 404

dwóch łańcuchów, 127instrukcji wyjścia, 64językowe, 422wewnętrzne, 409, 415zewnętrzne, 404, 409, 411

łączność, 107łączność prawostronna, 202

Mmakro, 342makrodefinicja #define, 1147manipulator, 991

endl, 56flush, 930setfill(), 944setprecision(), 944setw(), 944

manipulatory standardowe, 943mapy, 1108maska bitowa, 938mechanizm

constexpr, 1041narzucania wzorca, 1039RTTI, 812, 820throw, 789

Skorowidz 1187

mechanizmy metaprogramowania,1042

metaoperator wielokropka, ellipsis,1035

metoda, 94acquire(), 457angval(), 525at(), 184bad(), 273blab(), 743Brass::Withdraw(), 643BrassPlus::ViewAcct(), 643BrassPlus::Withdraw(), 643cin.gcount(), 961cin.get(), 956cin.get(ch), 956cin.get(char &), 955clear(), 262, 952close(), 267cout.put(), 94dequeue(), 606enqueue(), 773eof(), 270erase(), 850, 898exceptions(), 951fail(), 270find(), 831gcount(), 961get(), 954get(char &), 955get(void), 956getchar(), 955getline(), 960good(), 968HowMany(), 571insert(), 851is_open(), 271, 968isempty(), 604isfull(), 604klasy ostream, 927lower_bound(), 883magval(), 525open(), 267, 837operator()(), 1025operator[](), 574, 706, 801operator+(), 503, 547pop(), 745precision(), 363push(), 745push_back(), 870put(), 927queuecount(), 604read(), 978

remove(), 898reserve(), 836reset(), 525setf, 364setf(), 104, 938setstate(), 950show(), 454Singer::Show(), 715SingingWaiter::Show(), 715size(), 198, 334str(), 989Student::operator<(), 690swap(), 849tellg(), 983tellp(), 983topval(), 483update(), 455upper_bound(), 883vector<int>::push_back(), 870ViewAcct(), 650Waiter::Show(), 715what(), 797, 952width(), 934write(), 930, 978

metodyautomatycznie generowane, 673chronione, 663dołączania i dodawania klasy

string, 1093domyślne, 1019formatujące, 269klas do obsługi rachunków, 640klas hierarchii, 708klas hierarchii Worker, 718klasy, 682

baseDMA, 670bazowej, 632, 682hasDMA, 671istream, 953, 960lacksDMA, 670list, 879pochodnej, 682Queue, 602Remote, 765Singer, 709Stonewt, 541string, 1081, 1087, 1091String, 577StringBad, 555Time, 502Tv, 766vector, 849, 851Vector, 522Waiter, 709

kontenerów, 898modyfikujące klasy string, 1096niemodyfikujące, 475podwójne, 714przypisania klasy string, 1094publiczne klasy, 673publiczne klasy Student, 706rozwijane w miejscu wywołania,

457specjalne, 673, 1020specjalne klas, 562, 1018statyczne, 575STL, 1005usunięte, 1020usuwania klasy string, 1095wejścia, 960wirtualne, 645, 653, 680, 1023wstawiania klasy string, 1094z biblioteki STL, 1099zaprzyjaźnione, 768zarządzające pamięcią, 1087zastępowania klasy string, 1095zdefiniowane dla kontenerów,

1103miejscowy operator new, 425, 595mniej znaczący bajt, 1054mnożenie wektora przez liczbę, 528model, 866model klient-serwer, 460modyfikacja tablicy, 298modyfikator

const, 294, 350, 677, 1145static, 1147

modyfikatory łańcuchów, 1093multimapy, 1108mutujące operacje sekwencyjne, 1117

Nnadużywanie RTTI, 815nagłówek

climits, 84funkcji, 71, 293int main(), 50

narzędziedo indeksowania, 907Terminal, 45

nawiasykątowe, 1006klamrowe, 147, 483kwadratowe, 122ostre, 688

1188 J�zyk C++. Szko�a programowania

nazwafunkcji, 327tablicy, 180, 292

nazwyparametrów metod, 465plików nagłówkowych, 53składowych klasy, 465uniwersalne znaków, 97zastrzeżone bibliotek, 1056zastrzeżone makr, 1056zmiennych, 80

niejawna konwersja argumentów,487, 547

niejawne rzutowanie w górę, 705niemodyfikujące operacje

sekwencyjne, 1112nienazwana przestrzeń nazw, 437nieokreślona wartość zmiennej, 86niewirtualne klasy bazowe, 722notacja

[begin, end), 827naukowa, 102, 943throw(), 839wskaźnikowa, 175

noweelementy klas, 1018kontenery STL, 1005metody STL, 1005typy, 997

nowy standard C++, 997, 1045

Oobiekt, 76, 492

array, 183cerr, 922cin, 73, 922, 945clog, 922coua, 924cout, 55, 922, 931danych, 160fdci, 1033fin, 966fis, 966fout, 965klasy ifstream, 966, 968klasy ofstream, 965klasy Queue, 601klasy Vector, 526multimap, 885ostream, 548outFile, 269

przekazywany przez wartość, 561Stonewt, 541stringwprowadzanie danych, 828strumienia wejścia, 829type_info, 820unique_ptr, 844, 846vector, 183

obiektyBrass, 644dane i metody, 459funkcyjne, 887, 1024inflatable, 148inteligentnych wskaźników, 840klasy bazowej, 627klasy pochodnej, 627klasy string, 318odziedziczone, 692składowe, 692std::string, 172Stock, 452typu array, 320typu initializer_list, 910wewnątrz obiektów, 700

obliczanie przesunięcia, 428obsługa

błędów, 264, 1149stosu, 489tablic, 734typu wchar_t, 927wejścia-wyjścia, 918, 919wyjątków, 784–794, 808znaków, 253

obszar deklaracyjny, 429odczyt

łańcuchów, 129pliku binarnego, 979z pliku, 270

odpowiedniki operatorów, 892odpowiedzi do pytań, 1153odwracanie łańcucha, 207określanie adresu, 154opcje kompilacji, 44operacje

dodatkowe list, 1106dodatkowe zbiorów i map, 1107iteratora dostępu swobodnego,

864klasy set, 883klasy stack, 881klasy string, 138kontenera queue, 880

liczbowe, 1139na kontenerach, 1104na stogach, 1133na zbiorach, 1131numeryczne, 895sekwencyjne, 895sekwencyjne mutujące, 1116sortowania, 895sortowania i pokrewne, 1126wejścia-wyjścia, 918, 951wejścia-wyjścia plikowego, 965zdefiniowane dla sekwencji, 1106zdefiniowane dla zbiorów, 1108

operator&, 154, 343 (), 887*, 155<<, 55, 512, 924, 1096=, 812==, 812>>, 63, 947, 990, 1096alignof, 1041, 1077alternatywy ||, 243bitowego przesunięcia w lewo, 55bitowej alternatywy (|), 951const_cast, 817dekrementacji --, 199delete, 161, 581, 1149delete [], 163dodawania, 502dostępu do składowej, 505dostępu do składowej przez

wskaźnik, 506dostępu pośredniego, 180dynamic_cast, 809–820dzielenia, 108iloczynu bitowego AND, 55indeksowania, 506, 574inkrementacji ++, 199koniunkcji &&, 245, 247kropki, 148literału, 1042modulo, 110, 505negacji logicznej !, 248new, 159, 185, 581, 799, 1149

wersja miejscowa, 592new[], 163, 425noexcept, 1078(), 530()(), 1025<<(), 513, 527, 566>>(), 548, 571, 576

Skorowidz 1189

pobrania adresu, 562przecinek, 205, 207przeniesienia, move assignment

operator, 563przypisania, 506, 568, 582, 674,

1014przypisania (^=), 766przypisania (=), 208przypisania domyślny, 674przypisania jawny, 679reinterpret_cast, 817–819równości (==), 208różnicy symetrycznej, 766RTTI, 506rzutowania typu, 506sizeof, 83, 124, 505static_cast<>, 115, 117strzałki ->, 174trójargumentowy, 253typeid, 813, 820warunkowy, 506większości (>), 478wskazania składowej, 505wstawiania (<<), 62wyboru ?:, 253wyboru zakresu (::), 1146wyłuskania, 167wywołania funkcji, 506zasięgu (::), 485, 505

operatoryarytmetyczne, 106bitowe, 1067

kasowanie bitu, 1072przełączanie bitu, 1072sprawdzanie wartości bitu, 1073ustawianie bitu, 1072

bitowe alternatywne, 1071bitowe logiczne, 1069jawnych konwersji, 1003logiczne, 250pobierania >>, 946przekierowania, 923przesunięcia, 1067przypisania, 203przypisania złożone, 203relacyjne, 208rzutowania typu, 816wyłuskania składowych, 1073

opóźniona deklaracja typuzwracanego, 391

opróżnianie bufora wyjściowego, 930otwieranie plików, 968ozdabianie nazw, 372

Ppakiety parametrów, 1035pamięć

automatyczna, 403dynamiczna, 397, 403, 423, 554statyczna, 403wątku (C++11), 403wolna, free store, 403

parametrargc, 969mask, 943

parametrydomyślne, 365formalne, 334funkcji, 287referencyjne, 351, 356, 364typu wskaźnikowego, 354

permutacje, 897, 1138pętla

do while, 219for, 190–213

sekcja sterująca, 192składnia, 191struktura, 193

nieskończona, 220odczytująca dane z pliku, 274while, 213, 216zakresowa for (C++11), 221

pętlezagnieżdżone, 230, 232zakresowe, 221, 857, 1004

plikfloat.h, 103fstream, 991iostream, 52, 63, 991myfirst.cpp, 51

plik nagłówkowy, 398<fstream>, 533<iostream>, 522algorithm, 895array, 182cctype, 251cfloat, 103chrono, 1040climits, 83, 84cmath.h, 314complex, 903coordin.h, 399cstdint, 1042cstdio, 918cstdlib, 533, 614, 779

cstring, 128, 171ctime, 218, 614exception, 796fstream, 267, 965functional, 892, 1033iomanip, 944iostream, 90, 224, 270, 517, 918iterator, 867locale, 931memory, 840queue, 880random, 903, 1040ratio, 1040regex, 1041set, 881sstream, 989, 992stdexcept, 796, 822tuple, 1040typeinfo, 813, 814valarray, 688, 903vector, 848

plikibinarne, 976, 985, 992nagłówkowe, 53, 105, 1149tekstowe, 273, 977, 992tymczasowe, 988z kodem źródłowym, 40

płytka kopia, shallow copy, 565POD, plain old data, 455, 1041podobiekt, subobject, 697podwójne dziedziczenie, 711podwójny ukośnik, 51pojęcie, concept, 866pola bitowe, 149pole typu string, 146polecenie CC, 42polimorfizm, 646polimorfizm funkcji, 367porównania, 208porównywanie

ciągów, 573łańcuchów, 210, 213, 1091obiektów klasy string, 213, 831

porządek bajtów, 1054powiązanie obiektów z plikami,

267, 269poziom chroniony, 656późna deklaracja typu, 1000precyzja, 941precyzja wyświetlania, 936predykat, 888predykat dwuargumentowy, 888

1190 J�zyk C++. Szko�a programowania

preprocesor, 52, 1145prezentacja tablicy, 297priorytety operatorów, 107, 250, 1063procedura wnioskowania, 389program

arrayone.cpp, 124cfront, 41strings.cpp, 129trzyplikowy, 401

programowanieniskopoziomowe, 1041obiektowe, 32, 448, 454od ogółu do szczegółu, 300od szczegółu do ogółu, 35, 300ogólne, generic programming, 372strukturalne, 33uogólnione, 35, 823, 847, 858współbieżne, 1040, 1046

projektBoost, 1043TR1, 1043

proste przypisanie, 1088prototyp, 284

konstruktora, 464ANSI C, 285C++, 285funkcji, 68, 283, 296, 333, 1148

display(), 539for_each, 888seekg(), 982

prowokowanie dopasowania, 387przechodniość, 436przeciążanie funkcji, 133, 224, 367, 498

z typami referencyjnymi, 369przeciążanie operatów, 55, 109,

498, 512 [], 826<<, 512, 561, 596, 924>>, 579arytmetycznych, 528dodawania, 547porównania, 834przeciążonego, 529przypisania, 576

przeciążone szablony, 375przekazywanie

adresu funkcji, 326adresu struktury, 317argumentów

do konstruktora, 629przez referencję, 320przez stos, 408przez wartość, 320

funkcji tablicy, 293łańcucha, 360obiektu przez referencję, 358obiektu przez wartość, 650przez referencję, 345, 349przez wartość, 286, 346, 349struktur przez wartość, 311

przekierowanie, 225, 923przekształcanie

referencji, 649wskaźnika, 649

przenoszenie, 1008, 1018przenośność, 37przepełnienie, 88przesłonięcie

definicji, 405wersji bazowej, 1023

przestrzeń nazw, 54, 429, 438, 1149std, 74VECTOR, 521

przesunięcie, 428przeszukiwanie łańcuchów, 1089przybornik, 879przydział, 410

dynamiczny, 423pamięci, 424pamięci dla ciągu znaków, 558statyczny, 409–417

przypadki użycia, 1045przypisanie, 136, 1014

niepoprawne, 569, 648obiektu do samego siebie, 568, 598wartości

do wywołania funkcji, 355elementom tablicy, 296wskaźnikom, 168

z uwzględnieniem pól, 146pula wolnej pamięci, free store, 161punkt odniesienia, sequence point,

200puste wiersze, 134pzekazywanie obiektów

przez referencję, 676przez wartość, 676

Rrachunek lambda, 1025referencja, 224, 342

do obiektu klasy pochodnej, 361do r-wartości, 351do struktur, 352

jako parametry funkcji, 345l-wartościowa, 1008niemodyfikowalna, 357r-wartościowe, 1006

reguła jednokrotnej definicji, 412rekurencja, 322rekurencyjne

używanie szablonów, 736, 1037wywołanie show_list3(), 1038

relacjajest-czymś, 635, 678, 683, 697ma-coś, 635, 690, 697, 704, 753

relacje przyjaźni, 771reprezentacja liczby

zmiennoprzecinkowej, 976rodzaje

dziedziczenia, 705iteratorów, 862kontenerów, 872

rodzina funkcjifind(), 1089find_first_not_of(), 1091find_first_of(), 1090find_last_not_of(), 1091find_last_of(), 1090rfind(), 1089

rodzina klasfstream, 65Grand, 812iostream, 65ostream, 513

rozmiar łańcucha, 136rozpakowywanie pakietów, 1036rozszerzenie pliku, 41rozwiązywanie przeciążeń, 382rozwijanie stosu, 789RTTI, runtime type identification,

808, 813, 820r-wartość, 351rzutowanie

typów, 114, 152, 534, 672, 817,1148

w dół, downcasting, 649w górę, upcasting, 649w górę niejawne, 650wskaźnika, 820

Sscalanie, 1131sekcje klasy zawierającej, 775sekwencja, 875

Skorowidz 1191

semantyka przeniesienia, movesemantics, 352, 1007

separator, 206separator instrukcji, 50serwer, 460sklejanie danych wyjściowych, 926składnia

deklarowania funkcji, 1000inicjalizacji listą, 998instrukcji if, 238instrukcji if else, 239pętli for, 191prototypu, 284wywołania funkcji, 67

składowanum_strings, 555stanu, state member, 526

składowechronione, 680prywatne, 680statyczne, 575

skracanie wiersza, 963słowo

class, 1002decltype, 391, 1000decltype (C++11), 389export, 726, 1005extern, 422mutable, 420nullptr, 774register, 409static, 410, 422struct, 1002

słowo kluczowe, 72, 1055auto, 116, 999auto w C++11, 407class, 452const, 100, 234, 677, 1146enum, 151explicit, 537, 675, 692friend, 776inline, 341, 770namespace, 431new, 675, 679noexcept, 788private, 453, 656, 776protected, 656, 776public, 453, 656, 706, 776return, 72static, 126, 486struct, 142, 143template, 373, 725

typedef, 218, 333, 1001typename, 373, 754unsigned, 87using, 705virtual, 640, 653, 683, 712void, 50, 69volatile, 383, 420, 817

sortowanie, 1127specjalizacja, 380, 725, 739, 755

częściowa, 741jawna, 378, 379szablonu, 749

specyfikacjawyjątków, 806, 1002wyjątków a C++11, 788

specyfikator, 419final, 1023inline, 1147override, 1023

stała CLOCKS_PER_SEC, 218stałe

całkowitoliczbowa, 90formatowania, 939symboliczne, 85symboliczne preprocesora, 85trybu otwarcia pliku, 972typu char, 95wskaźniki, 305zmiennoprzecinkowe, 105

stan obiektu, 526standard

ANSI C, 38ANSI/ISO C++, 1145C++11, 39C++98, 39

standardowa biblioteka szablonów,126, 1099

standardowy nagłówek, 48stany strumienia, 949–952statyczna

kontrola typów, 285składowa klasy, 555

statyczne metody klasy, 575statyczny licznik obiektów, 571sterta, heap, 161, 177, 403STL, Standard Template Library,

38, 126, 724, 1099stos, stack, 161, 178, 407, 488stos wskaźników, 729, 730stosowanie

abstrakcyjnych klas bazowych, 659destruktorów wirtualnych, 648

inteligentnych wskaźników, 838klas, 497konstruktora kopiującego, 674konstruktorów, 465manipulatorów formatu, 933referencji r-wartościowych, 1013stosu wskaźników, 729szablonu initializer_list, 910wyrażeń lambda, 1026

stóg, heap, 1133strategia dziel-i-rządź, 324Stroustrup Bjarne, 36struktura

pętli do while, 219pętli for, 193pętli while, 214

struktury, 142–146, 184, 310dynamiczne, 174jako parametr, 354

strumienie do odczytu i zapisu, 983strumień, 991

wejściowy, 63, 919wyjściowy, 55, 919z buforem, 920

surowy łańcuch znaków, rawstring, 141

sygnalizacja błędu, 72sygnatura wywołania, 1032symbol, Patrz znaksymulacja bankomatu, 612synonimy szablonów, 1001systemy liczbowe, 933, 1051szablon

array, 181, 903auto_ptr, 838basic_string, 837, 1080char_traits, 837function, 1033initializer_list (C++11), 908klasy, 727, 755

array, 881deque, 877list, 877priority_queue, 880queue, 880vector, 847, 876z funkcjami zaprzyjaźnionymi,

748less<>, 885lexical_cast, 1044multimap, 886shared_ptr, 847

1192 J�zyk C++. Szko�a programowania

szablonStack, 731std::initializer_list, 999unique_ptr, 844use_f(), 1032, 1034valarray, 903, 1005vector, 181, 876, 903

szablonu, 736konkretyzacja jawna, 740konkretyzacja niejawna, 739parametry domyślne, 739specjalizacja częściowa, 741specjalizacja jawna, 740

szablonydo obsługi tablic, 734, 903funkcji, 372–391jako parametry, 744jako składowe, 742klas, 724, 755klas inteligentnych wskaźników,

841niezależne funkcji, 751o zmiennej liczbie parametrów,

753, 1035stosu, 726tablic, 734variadic templates, 1035zaprzyjaźnione, 751zaprzyjaźnione niezależne, 746,

752zaprzyjaźnione z szablonem

klasy, 750zaprzyjaźnione zależne, 746

Śśrednia harmoniczna, 779średnik, 50środowisko IDE, 44, 45

Ttablic, 122, 185, 734

inicjalizacja, 124, 127wiązanie dynamiczne, 170wiązanie statyczne, 169

tablicaobiektów string, 319twodee, 736vtbl, 652

tablicedwuwymiarowe, 231, 306dynamiczne, 162, 295funkcji wirtualnych, 651jako parametr, 306obiektów, 482struktur, 148, 180wskaźników, 180zmienne, 1147

testowaniehierarchii klas Worker, 709interfejsu klasy Queue, 613klas, 672, 760klas do obsługi rachunków, 644nowej klasy String, 579operatorów new i delete, 559stosu, 490stosu wskaźników, 732szablonu klasy, 727

trybdołączania, 982tryb ios_base::in, 982ios_base::binary, 982ios_base::out, 982ios_base::app, 982

tryby otwarcia pliku, 971, 973, 982tworzenie

konstruktorów, 713nowego obiektu, 592obiektu string, 824programu, 39pustego stosu, 490specjalizacji, 755struktur dynamicznych, 174tablic, 122tablic dynamicznych, 162typów wskaźnikowych, 329zmiennej referencyjnej, 342

typ, 449arp, 180bad_exception, 806bool, 100, 614char, 92, 141char*, 308char16_t, 99, 141char32_t, 99, 141double, 67, 103float, 103int, 83long, 83, 89long double, 103long long, 83

short, 83, 87signed char, 98Stonewt, 539streamoff, 983streampos, 983unsigned char, 99unsigned short, 88wchar_t, 99

typybazowe, 99bez znaku, 87całkowitoliczbowe, 81definiowane przez użytkownika,

142funkcyjne, 1030języka, 925obiektowe, 64parametryzowalne, 724podstawowe, 946poszerzone, 1042referencyjne, 224skalarne, 1041stałych, 91własne, 492wskaźnikowe, 306, 329, 925wyjątków, 796wyliczeniowe, 151, 152, 1002

zakres wartości, 153z semantyką wywołania, 1030zdefiniowane dla kontenerów

asocjacyjnych, 1107zmiennoprzecinkowe, 931złożone, 122

Uukrywanie

danych, 453metod, 654

UML, Unified Modeling Language,1045

unia anonimowa, 150Unicode, 98unie, 149, 184uogólnione identyfikatory typów, 727uporządkowanie

częściowe, 385wyczerpujące, total ordering, 855

ustalanie wielkości łańcucha, 366ustawianie

precyzji, 937stanów, 949

Skorowidz 1193

bitu eofbit, 951bitu failbit, 949

usuwaniebłędów, 814obiektów, 561

uściślenie, refinement, 866użycie

adaptatorów, 894biblioteki STL, 899const, 351delete, 176enumeratorów jako etykiet, 258instrukcji switch, 256listy, 878new, 160, 174obiektów string, 830przeciążonych szablonów, 375referencji r-wartościowych, 1006referencji stałej, 351referencji z obiektami, 358tablic dynamicznych, 164

Wwartość

enumeratora, 153EOF, 956NULL, 581

warunek przerwania pętli, 246wątek thread_local, 1046wczytywanie

liczb do tablicy, 262łańcuchów, 130–132łańcuchów i liczb, 134wiersza, 131, 140wiersza po liczbie, 134znaków, 229znaków do końca pliku, 226znaków w pętli, 222

wejście i wyjście, 48plikowe, 268, 964tekstowe, 265, 266

wektor przesunięcia, displacementvector, 519

wektory, 519–521wiązanie

dynamiczne, 162, 648, 650obiektów z plikami, 271statyczne, 162, 648tablic, 169wewnętrzne, internal linkage, 1146

wielkość liter, 47

wielozbiory, 1108Windows 1250, 92wirtualne klasy bazowe, 712, 722własności stosu, 488właściwości

algorytmów, 896iteratorów, 865kontenerów, 873metod klas, 682referencji, 348sekwencji, 875wyjątków, 793zasięgu, 775

wprowadzaniedanych, 226liczb, 316

wrappery, 1030wskazywanie pól struktury, 175wskaźnik, 154, 185, 303, 330

auto_ptr, 839, 845do struktury, 179, 180do tablicy wskaźników, 180funkcji, 495jako iterator, 866na funkcję, 326, 327null, 572p_next, 859pusty, 572, 581pusty, null, 161pusty, null pointer, 424, 1001shared_ptr, 843, 846tablicy, 306this, 476, 477, 479, 575, 700typu char, 172, 555unique_ptr, 843, 844wskaźnika, 307znakowy char*, 334

wskaźnikideklarowanie, 168do tablic wskaźników do funkcji,

332na funkcje, 325obiektów, 587, 590przypisanie adresu, 169przypisywanie wartości, 168wyłuskiwanie, 169stałych, 305

współrzędnebiegunowe, 314, 526prostokątne, 313, 526

wybór typu, 815wyciek pamięci, 162, 179, 807

wyjątek, 782, 785, 789, 793, 951bad_alloc, 798invalid_argument, 797length_error, 797nieoczekiwany, unexpected

exception, 804nieprzechwycony, uncaught

exception, 804out_of_bounds, 797out_of_range, 875

wyjątkiout_of_bounds, 798typu exception, 796typu retort, 805w języku C++, 820w postaci obiektów, 784z rodziny logic_error, 798z rodziny runtime_error, 798zagnieżdżone, 802

wyliczenia, 487wyłuskiwanie wskaźników, 169wymagania dla kontenerów

(C++11), 874wymuszanie przeniesienia, 1015wyrażenia

lambda, 1024logiczne, 243regularne, 1041z deklaracjami, 195

wyrażenie, 194&stacks[0], 166*ptr, 157*this, 479continue, 784cout <<, 926stacks[1], 167throw, 793

wyszukiwaniebinarne, 1129wartości, 1135

wywołaniedelete, 594delete [], 594destruktora, 590funkcji, 66, 283, 333funkcji przez wskaźnik, 327kopiującego operatora

przypisania, 1018metody, 458przypisania przenoszącego, 1018rekurencyjne, 668Swap(), 378

wzór na liczbę kroków, 532

1194 J�zyk C++. Szko�a programowania

Zzaawansowane sposoby

indeksowania, 906zagnieżdżanie

w szablonie, 776wyrażeń warunkowych, 254struktury, 602

zakres, 851zakres elementów, 301zakresowe pętle for, 1004zamiana

funkcji zaprzyjaźnionych wszablony, 749

współrzędnych, 314zapis

do pliku, 267, 269, 967dwójkowy, 1053łańcuchów jako

obiekty klasy string, 334stałych tekstowych, 334tablic znakowych, 334wskaźników łańcuchów, 334

liczb zmiennoprzecinkowych, 102łańcuchów w tablicy, 128pliku binarnego, 979szesnastkowy, 1053tablicowy, 169, 170, 293wskaźnikowy, 170, 293z kropką, 140, 312

zarządzanie pamięcią dynamiczną,630

zasięg, scope, 404globalny, 404klasy, 404, 455, 485lokalny, 404potencjalny, 429

prototypu funkcji, 404przestrzeni nazw, 404wartości wyliczeniowych, 1002zmiennej, 430zmiennych automatycznych, 405

zawężanie typów, 998zawieranie, 687, 691, 696, 704, 753zestawy znaków, 92zintegrowane środowisko

programistyczne, 40złożoność

liniowa, 873stała, 873

zmianaformatowania, 461poziomu dostępu, 705rozmiaru łańcucha, 136

zmianyw klasach, 1003w specyfikacji wyjątków, 1002w szablonach, 1004

zmienna niemodyfikowalna,read-only, 100

zmienne, 60automatyczne, 177, 286, 407globalne, 415, 431lokalne, 286, 415proste, 80referencyjne, 342rejestrowe, 409statyczne, 409strukturalne, 147tymczasowe, 350typu inflatable, 142wyliczeniowe, 151zewnętrzne, 411, 416

znacznik końca pliku, 985

znak&, 221*, 55backspace, 96kropki, 94krzyżyka #, 224lewgo ukośnika \, 137, 141nowego wiersza, 57, 96NUL (\0), 126, 130, 309przecinka, 205przypisania =, 125, 145średnika, 50, 194wypełnienia, 935

znaki/* i */, 52//, 47[], 1025&&, 351ASCII, 1059końca wiersza, 273podkreślenia, 1056

zwalnianiepamięci, 161, 424, 570tablicy dynamicznej, 163zwolnionej pamięci, 567, 569

zwracanieobiektów, 584, 676przez wartość obiektu, 585przez wartość obiektu

niemodyfikowalnego, 586referencji, 356, 676referencji do struktury, 357referencji modyfikowalnej do

obiektu, 585referencji niemodyfikowalnej

obiektu, 584struktury, 312wartości, 282