c++/wersja do druku programowanie w c++download.recone.org/c++/cpp.pdf · jak tworzyć, możliwe...

91
C++/Wersja do druku 1 C++/Wersja do druku Programowanie w C++ Aktualna, edytowalna wersja tego podręcznika jest dostępna w Wikibooks, bibliotece wolnych podręczników pod adresem Całość tekstu jest objęta licencją CC-BY-SA 3.0 [1] i jednocześnie GNU FDL 1.2. Udziela się zezwolenia do kopiowania, rozpowszechniania lub modyfikacji tego dokumentu zgodnie z zasadami Licencji Creative Commons Uznanie autorstwa-Na tych samych warunkach 3.0 Unported lub dowolnej późniejszej wersji licencji opublikowanej przez Creative Commons, która zawiera te same elementy co niniejsza licencja. Treść licencji dostępna jest pod adresem http://creativecommons.org/licenses/ by-sa/3.0/legalcode. Udziela się zezwolenia do kopiowania, rozpowszechniania lub modyfikacji tego dokumentu zgodnie z zasadami Licencji GNU Wolnej Dokumentacji w wersji 1.2 lub dowolnej późniejszej, opublikowanej przez Free Software Foundation; nie zawiera Sekcji Niezmiennych, bez Tekstu na Przedniej Okładce i bez Tekstu na Tylnej Okładce. Kopia licencji załączona jest w sekcji zatytułowanej "GNU Free Documentation License". Spis treści Wstęp 1. O języku C++ Opis i historia 2. O podręczniku Autorzy, źródła, jak czytać ten podręcznik Część 1: Podstawowe mechanizmy C++ 1. Przestrzenie nazw Wprowadzenie pojęcia przestrzeni nazw, przestrzeń nazw std 2. Zmienne Nowe sposoby deklaracji, kontrola typów w C++, nowe sposoby rzutowania 3. Referencje Porównanie ze wskaźnikami, zastosowanie do przekazywania argumentów do funkcji 4. Funkcje inline Krótki opis funkcji inline 5. Przeciążanie funkcji Po co i jak można przeciążać funkcje i jak tego nie da się robić 6. Zarządzanie pamięcią Jak w C++ dynamicznie zarządzać pamięcią z użyciem operatorów new i delete 7. Strumienie Obsługa strumieni wejścia i wyjścia, czytanie i pisanie do plików, obiekty std::istream i std::ostream

Upload: others

Post on 19-Aug-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 1

C++/Wersja do druku

Programowanie w C++Aktualna, edytowalna wersja tego podręcznika jest dostępna w Wikibooks, bibliotece wolnych podręczników pod

adresemCałość tekstu jest objęta licencją CC-BY-SA 3.0 [1] i jednocześnie GNU FDL 1.2.

Udziela się zezwolenia do kopiowania, rozpowszechniania lub modyfikacji tego dokumentu zgodnie z zasadami Licencji Creative Commons

Uznanie autorstwa-Na tych samych warunkach 3.0 Unported lub dowolnej późniejszej wersji licencji opublikowanej przez Creative Commons,

która zawiera te same elementy co niniejsza licencja. Treść licencji dostępna jest pod adresem http:/ / creativecommons. org/ licenses/by-sa/ 3. 0/ legalcode.

Udziela się zezwolenia do kopiowania, rozpowszechniania lub modyfikacji tego dokumentu zgodnie z zasadami Licencji GNU Wolnej

Dokumentacji w wersji 1.2 lub dowolnej późniejszej, opublikowanej przez Free Software Foundation; nie zawiera Sekcji Niezmiennych, bez

Tekstu na Przedniej Okładce i bez Tekstu na Tylnej Okładce. Kopia licencji załączona jest w sekcji zatytułowanej "GNU Free Documentation

License".

Spis treści

Wstęp1. O języku C++

Opis i historia

2. O podręcznikuAutorzy, źródła, jak czytać ten podręcznik

Część 1: Podstawowe mechanizmy C++1. Przestrzenie nazw

Wprowadzenie pojęcia przestrzeni nazw, przestrzeń nazw std

2. ZmienneNowe sposoby deklaracji, kontrola typów w C++, nowe sposoby rzutowania

3. ReferencjePorównanie ze wskaźnikami, zastosowanie do przekazywania argumentów do funkcji

4. Funkcje inlineKrótki opis funkcji inline

5. Przeciążanie funkcjiPo co i jak można przeciążać funkcje i jak tego nie da się robić

6. Zarządzanie pamięciąJak w C++ dynamicznie zarządzać pamięcią z użyciem operatorów new i delete

7. StrumienieObsługa strumieni wejścia i wyjścia, czytanie i pisanie do plików, obiekty std::istream i std::ostream

Page 2: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 2

Część 2: Podstawy programowania obiektowego1. Czym jest obiekt

Wprowadzenie pojęcia klasy i obiektu, autorekursja, kontrola dostępu

2. Konstruktor i destruktorKonstruktor, konstruktor kopiujący, destruktor

3. DziedziczenieDziedziczenie prywatne, publiczne i chronione

4. Składniki statyczneAtrybuty i metody statyczne

Część 3: Zaawansowane programowanie obiektowe1. Funkcje wirtualne

Funkcje wirtualne i abstrakcyjne, wyjaśnienie polimorfizmu i dynamic_cast

2. Programowanie zorientowane obiektowoWyjaśnienie idei programowania zorientowanego obiektowo

3. Obiekty stałeJak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable

4. Przeciążanie operatorówWprowadzenie przykładu klasy z kompletnym przeciążeniem operatorów

5. Konwersje obiektówPrzeciążenie operatorów konwersji, konstruktor jako sposób konwersji, konstruktory typu explicit

6. Klasy i typy zagnieżdżoneTworzenie klas i typów zagnieżdżonych

7. Dziedziczenie wielokrotneDziedziczenie wielokrotne, dziedziczenie wirtualne oraz problemy z nimi związane

Część 4: Zaawansowane konstrukcje językowe1. Obsługa wyjątków

Obsługa wyjątków w C++, funkcje unexpected() i terminate()

2. Szablony funkcjiSzablony funkcji

3. Szablony klasSzablony klas, programowanie uogólnione

4. Wskaźniki do elementów składowychWykorzystnie wskaźników do elementów składowych klas

Page 3: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 3

Dodatek A: Biblioteka STL1. Filozofia STL

Jak skonstruowana jest biblioteka STL

2. StringKorzystanie z łańcuchów znaków

3. VectorKorzystanie z wektorów

4. List & SlistListy jedno- i dwukierunkowe

5. SetKorzystanie ze zbiorów

6. MapKorzystanie z odwzorowań

7. StackKorzystanie ze stosu

8. IteratoryKorzystanie z iteratorów

9. Algorytmy w STLJak działają algorytmy w STL

10. Inne klasy STLKrótkie omówienie pozostałych klas

Dodatek B1. Przykłady

Przykłady kodu z komentarzem

2. ĆwiczeniaZadania kontrolne

3. Różnice między C a C++Najważniejsze różnice między C a C++

Pozostałe1. Indeks

Indeks najważniejszych terminów

2. ZasobyKsiążki, linki do innych kursów i dokumentacji

3. Dla autorówWskazówki dla osób pragnących pomóc w rozwoju podręcznika

4. Wersja do druku Całość książki na jednej stronie, gotowa do druku

5. LicencjaPełny tekst GNU Free Documentation license

Wstęp

Page 4: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 4

O języku C++C++ jest językiem wieloparadygmatowym, mającym korzenie w popularnym języku C. Na jego rozwójoddziaływało wiele języków, z których należy przede wszystkim wspomnieć Simulę i Adę. Programiści cenią go zapołączenie bezkompromisowej wydajności programów wynikowych z zaawansowanymi mechanizmamiumożliwiającymi programowanie na wysokim poziomie abstrakcji i kontrolę zależności między komponentami wwielkich projektach.C++ stara się zachować kompatybilność z językiem C, ale jednocześnie udostępnia szeroki wachlarz nowychmechanizmów, m.in: programowanie obiektowe z wielokrotnym dziedziczeniem i kontrolą dostępu, dynamicznąkontrolę typów i precyzyjne rzutowanie, programowanie generyczne (uogólnione) dzięki wykorzystaniu szablonów,przeciążanie funkcji i operatorów, obsługę sytuacji wyjątkowych i zarządzanie przestrzeniami nazw. Od ostatniopowstałych konkurentów, takich jak Java i C#, wyróżnia się traktowaniem typów zdefiniowanych przezużytkownika na równi z typami wbudowanymi. Niestety, to bogactwo możliwości skutkuje rozbudowaną składniąjęzyka, stanowiąc problem nawet dla kompilatorów (żaden popularny kompilator nie jest w pełni zgodny zobowiązującym standardem języka; stan na 2010 rok).Nazwa C++ została zaproponowana przez Ricka Mascitti i wywodzi się z faktu, że w C wyrażenie zmienna++oznacza inkrementację, czyli zwiększenie o jeden.Autorem i twórcą języka C++ jest duński programista Bjarne Stroustrup, pierwsza wersja pojawiła się w 1979.

O podręczniku

O podręcznikuPodręcznik ten jest tworzonym na Wikibooks kursem języka C++. Początkowo pomyślany jako samodzielnajednostka, po dyskusji utracił część treści na rzecz podręcznika C i odtąd ma być jego w pewnym sensieprzedłużeniem, bez powielania występujących tam podstaw.

Tworzenie podręcznikaNiniejszy kurs cały czas jest w fazie rozwojowej. Jako otwarty podręcznik może być edytowany przez każdego ikażdy wkład będzie mile widziany. Przed wprowadzaniem poważniejszych zmian warto jednak przeczytać rozdziałDla autorów.

AutorzyZnaczący wkład w powstanie podręcznika mają:•• Derbeth•• Felix

Bibliografia• ISO/IEC 14882:2003 Programming Languages – C++

Jak czytać ten podręcznikJęzyk C++ powstał jako wzbogacenie języka C o cechy obiektowe. Ten podręcznik został podobnie pomyślany: nie jako osobna całość, ale logiczne przedłużenie podręcznika C. Co prawda, oba kursy są niezależne, ale pisząc ten podręcznik staraliśmy się nie powtarzać informacji, które były już zawarte w poprzedniej książce. Nie znajdzie tu się więc objaśnienie, czym jest kompilator, jak działa i który kompilator wybrać, czym jest funkcja i zmienna oraz

Page 5: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 5

podobne podstawowe informacje. Czytelnikowi zostawiamy swobodę w tym, jak podejdzie do tego podręcznika, alezalecamy jeden z przedstawionych dalej dwóch sposobów.Pierwszy z nich to dokładne przestudiowanie kursu C a następnie zaczęcie od pierwszej części tego podręcznika,gdzie pokazujemy, co C++ zmienia w składni i co oferuje nowego. Idąc dalej, czytelnik dojdzie do programowaniaobiektowego. Taki sposób jest co prawda bardziej pracochłonny i wymaga większego wysiłku, ale systematycznepodejście do nauki języka powinno zaowocować dobrym uporządkowaniem informacji i większym zrozumieniemfilozofii obu języków.Możliwa jest też droga dla osób bardziej niecierpliwych. Należy wtedy szybko przejrzeć kilka początkowychrozdziałów podręcznika C, ominąć część pierwszą tego kursu i od razu zacząć od programowania obiektowego, wrazie kłopotów zaglądając do Indeksu. Może to być dobra metoda dla osób zaznajomionych już z programowaniem,choć w C++ czyha na nieuważnych wiele pułapek, które trzeba nauczyć się omijać.

Część 1Podstawy języka

Przestrzenie nazw

Słowem wstępuJeśli użyjemy dowolnej wyszukiwarki internetowej, to powinniśmy bez problemu znaleźć prosty, szablonowy kodnapisany w C++, który wyświetla napis „Hello World!”, w tłumaczeniu na polski „Witaj Świecie!”. Spójrzmy naniego:

#include <iostream>

using namespace std;

int main ()

{

cout << "Hello World!" << endl;

return 0;

}

Zaleca się używanie znaku nowej linii (\n) zamiast manipulatora wyjścia "endl". Chyba że jest touzasadnione: endl wymusza opróżnienie bufora, ale na przykład przy wielokrotnym zapisie na dyskmoże to obciążyć jego pracę.Osoby, które już znają C, na pewno się domyślą, co mniej więcej się dzieje w tym kodzie. Najpierw,

pokrótce omówimy, co ten program właściwie robi.Za pomocą #include <iostream> dołączyliśmy plik nagłówkowy do obslugi strumieni I/O, dzięki czemumożemy wypisywać dane na ekran (ściślej: na standardowe wyjście). Dodatkowo istnieje plik nagłówkowyiostream.h. Jest to jednak nagłówek niestandardowy, pomagający zachować wsteczną zgodność.int main( ) {...} służy zdefiniowaniu funkcji głównej, która jest zawsze uruchomiana podczas startunaszego programu.Wyrażenie cout << umożliwia nam wypisywanie pewnych informacji. W naszym przypadku wypisaliśmy napis„Hello World!”, a następnie „przedłużyliśmy” to polecenie za pomocą operatora <<, i użyliśmy endl, który m.in.dodaje znak nowej linii.Za pomocą return 0 informujemy system, że program może zakończyć działanie bez zgłaszania błędów.

Page 6: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 6

Na koniec zostawiliśmy linię z kodem using namespace std. Aby wyjaśnić jej znaczenie, musimy omówićczym są przestrzenie nazw.

Przestrzenie nazwPodczas pracy nad dużymi projektami, w których używa się wielu bibliotek z licznymi deklaracjami, możemy wkońcu natknąć się na problem konfliktu nazw - gdy kilka obiektów, typów czy funkcji ma tę samą nazwę.Rozwiązaniem może być np. zamknięcie nazw w "zakresach", w celu oddzielenia ich. Z pomocą przychodzi nammechanizm przestrzeni nazw.Przestrzeń nazw jest zatem zbiorem obiektów, która ogranicza dostęp do nich - oprócz nazwy obiektu niezbędne jestteż wspomnienie, z której przestrzeni nazw chcemy go użyć, obchodząc tym samym problem konfliktu nazw.Spójrzmy na kolejny program, zmienioną wersję poprzedniego:

#include <iostream>

int main ()

{

std::cout << "Hello World!" << std::endl;

return 0;

}

Widzimy tu wyrażenie std:: pojawiające się przed cout i endl. Zapis ten oznacza, że wspomniane obiektychcemy zaczerpnąć z przestrzeni std, a przy okazji nie obchodzi nas, czy są jakieś inne obiekty o takich nazwach.Jeśli jednak pominiemy wzmiankę o std::, pojawi się informacja o błędzie.W przestrzeni nazw std znajdziemy mnóstwo, a wręcz cały arsenał różnych narzędzi, począwszy od pewnychbardzo przydatnych funkcji, np. sortowania, wyszukiwania, a kończywszy na tak zwanych pojemnikach(kolekcjach), które pozwalają nam w łatwy sposób przechowywać pewne wartości. Oczywiście, aby mieć dostęp dotych narzędzi, musimy dołączyć odpowiedni plik nagłówkowy, używając do tego dyrektywy #include.Przykład pierwszy ze wstępu pokazał nam, że nie musimy za każdym razem odwoływać się do przestrzeni nazw,kiedy chcemy użyć znajdujących się w niej rzeczy. Używając using namespace PrzestrzenNazw,podpowiadamy kompilatorowi, w którym miejscu może szukać używanych przez nas obiektów i funkcji, abyśmymogli swobodnie używać wszystkiego co się znajduje w danej przestrzeni nazw, tzn. bez dodatkowej wzmianki jaknp. std::.Oczywiście nie musimy naraz "udostępniać" wszystkiego, co jest w danej przestrzeni nazw, możemy wykorzystaćtakże pewne wybrane elementy. Używamy do tego operacji using PrzestrzenNazw::element. Zobaczmyprzykład użycia tej operacji:

#include <iostream>

using std::endl;

int main ()

{

std::cout << "Hello World!" << endl;

return 0;

}

Za pomocą using std::endl poinformowaliśmy kompilator, że będziemy mogli używać w kodzie endl i będziemy mieli na myśli właśnie to pochodzące z przestrzeni std. Nie wykonaliśmy tej operacji na elemencie

Page 7: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 7

cout (nie wstawiliśmy instrukcji using std::cout), więc musieliśmy go dalej poprzedzić nazwą przestrzeni.

W dalszej części podręcznika będzie używany zapis z przedrostkiem std::.

Tworzenie własnej przestrzeni nazwPrzestrzeń nazw tworzymy za pomocą słowa kluczowego namespace, ograniczając jej zawartość klamrami.Możemy na przykład stworzyć przestrzeń nazw HelloWorld zawierającą funkcję hello( ):

#include <iostream>

namespace HelloWorld

{

void hello ()

{

std::cout << "Hello World!" << std::endl;

}

}

int main ()

{

HelloWorld::hello ();

return 0;

}

Oczywiście, gdybyśmy wstawili using HelloWorld::hello lub ogólnie using namespace

HelloWorld przed funkcją main (a nawet wewnątrz tej funkcji), nie musielibyśmy odwoływać się jawnie doHelloWorld, wystarczyłoby samo hello( ).Co ciekawe, nie musimy zamieszczać zawartości naszej przestrzeni nazw w jednym, ciągłym bloku. Możemy rozbićto na kilka części:

namespace Matematyka

{

int dodaj (int a, int b)

{

return a+b;

}

int odejmij (int a, int b)

{

return a-b;

}

}

namespace Matematyka

{

int pomnoz (int a, int b)

{

return a*b;

Page 8: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 8

}

int podziel (int a, int b)

{

return a/b;

}

}

Wówczas wewnątrz przestrzeni nazw Matematyka znajdziemy wszystkie stworzone przez nas funkcje.Tworząc funkcję w przestrzeni nazw możemy wstawić samą deklarację, a potem w innym miejscu podać pełnądefinicję tej funkcji. Możemy na co najmniej dwa sposoby podać definicję pewnej funkcji - wewnątrz przestrzeninazw lub poza nią, pisząc typ_zwracany PrzestrzenNazw::nazwa_funkcji( ), na przykład:

#include <iostream>

namespace Matematyka

{

int dodaj (int a, int b);

int odejmij (int a, int b);

}

using namespace std;

int main ()

{

cout << Matematyka::dodaj (10, 20) << endl;

return 0;

}

namespace Matematyka

{

int dodaj (int a, int b)

{

return a+b;

}

int odejmij (int a, int b)

{

return a-b;

}

}

Jak wspomniano wcześniej, ostatnie dwie definicje funkcji moglibyśmy zapisać także w ten sposób:

int Matematyka::dodaj (int a, int b)

{...}

int Matematyka::odejmij (int a, int b)

{...}

Page 9: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 9

Przestrzeń nazw stdWróćmy ponownie do standardowej przestrzeni nazw, jaką jest std. Dzięki plikowi nagłówkowemu iostreammożemy operować na standardowym wejściu i wyjściu. Zobaczmy jak wczytywać pewne wartości do zmiennych,używając do tego cin:

#include <iostream>

int main ()

{

int a, b;

std::cout << "Podaj dwie liczby a i b" << std::endl;

// wypisujemy komunikat i czekamy na wpisanie liczby a

std::cout << "podaj a: ";

std::cin >> a;

// wypisujemy komunikat na wyjście i czekamy na wpisanie liczby b

std::cout << "podaj b: ";

std::cin >> b;

// wypisujemy sumę tych dwóch liczb

std::cout << "a+b= " << a+b << std::endl;

return 0;

}

Dzięki std::cin >> możemy wczytać pewną wartość do zmiennej. Zmienna ta nie musi być liczbą, może byćteż np. napisem. W C++ tekst (łańcuch znaków) będziemy często przechowywali w obiektach typu string (którytakże znajduje się w std). Do jego obsługi będziemy musieli dołączyć do projektu bibliotekę <string>.Spójrzmy na przykład:

#include <iostream>

#include <string>

using std::cout;

using std::cin;

using std::endl;

int main ()

{

std::string imie;

std::string email;

std::string informacja;

// wczytujemy imię

cout << "Podaj swoje imie: ";

cin >> imie;

// wczytujemy email

Page 10: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 10

cout << "Podaj swój email: ";

cin >> email;

informacja = imie + " (" + email + ")"; // suma (konkatenacja)

napisów

cout << "Witaj " << informacja << endl;

informacja += " czyta ten napis";

cout << informacja << endl;

return 0;

}

Zauważmy, jak prosto się korzysta zmienną typu string (dla wtajemniczonych jest to pewna klasa). Jeśli chcemydodać dwa napisy, wystarczy wykorzystać operator +. Możemy także wykorzystywać operator +=, jeśli chcemydokleić do tekstu dodatkowy napis.Podając swoje imię jako Zdzichu, a e-mail jako [email protected], zobaczymy wynik:

Podaj swoje imie: Zdzichu

Podaj swój email: [email protected]

Witaj Zdzichu ([email protected])

Zdzichu ([email protected]) czyta ten napis

Więcej o stringach można przeczytać w dodatku opisującym bibliotekę STL.

Korzystanie z biblioteki standardowej CPonieważ język C++ jest (w pewnym uproszczeniu) rozwinięciem C, w dalszym ciągu można korzystać z bibliotekistandardowej C (tzw. libc). Ze względu na zachowanie wstecznej kompatybilności, umożliwiono korzystanie z niejtak jak wcześniej w C.

#include <string.h>

int main (int argc, char **argv)

{

if (argc < 2)

return -1;

return strcmp (argv[0], argv[1]);

}

Jednak dostępna jest też wersja libc przygotowana specjalnie dla C++. Pliki nagłówkowe są w niej inaczejnazywane, wszystkie funkcje znajdują się dodatkowo w przestrzeni nazw std. Tak więc powyższy program napisanyw sposób właściwy dla C++ mógłby wyglądać następująco:

#include <cstring> // zamiast <string.h>

int main (int argc, char **argv)

{

if (argc < 2)

return -1;

return std::strcmp( argv[0], argv[1]);

}

Page 11: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 11

Zauważmy, że:1. dołączany plik nagłówkowy ma dodaną na początku literę c2.2. dostęp do funkcji jest możliwy przez pośrednictwo przestrzeni nazw stdReguła ta dotyczy wszystkich plików, z których składa się biblioteka standardowa C.W swoich programach lepiej jest używać wersji przygotowanej dla C++: #include <cxxxxx>. Po pierwsze, dziękiprzestrzeniom nazw unikniemy kolizji nazw z własnymi funkcjami. Po drugie, wersja ta ma wbudowaną obsługęwyjątków. Po trzecie, czasami libc przygotowana dla C wywołuje ostrzeżenia lub błędy kompilacji w kompilatorachC++.

Można też w C++ używać klasycznych nazw (z C) (stdio.h, stdlib.h, string.h, ...), ale czasami libc przygotowana dla Cwywołuje ostrzeżenia lub błędy kompilacji.

ZmienneZanim przystąpisz do czytania tego rozdziału upewnij się, że opanowałeś już wiedzę z podręcznika C. Jest tuwykorzystywanych wiele odniesień i pojęć z tego języka.

Deklarowanie zmiennychW języku C zmienne deklarowało się na początku bloku kodu (zwykle przed pierwszą instrukcją). Wprzeciwieństwie do C++ nie można było natomiast deklarować zmiennych np. w nagłówku pętli for. Poniższyprzykład bez problemu powinien zadziałać w kompilatorach języka C++, natomiast starsze kompilatory C mogą gouznać za błędny:

int main ()

{

for (int i = 0; i <= 10; ++i)

{

// instrukcje...

}

}

W C++ deklaracje zmiennych mogą znajdować się w dowolnym miejscu kodu w funkcji, nie obowiązuje już zasadaz C nakazująca ich deklarowanie przed właściwym kodem funkcji:

#include <iostream>

using namespace std;

int main ()

{

int i;

cin >> i;

int j = i*i;

cout << j;

return 0;

}

Page 12: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 12

Kontrola typówW C++ w stosunku do C została zaostrzona kontrola typów. Teraz za każdym razem, gdy przekażemy funkcjizmienną o innym typie dostaniemy błąd od kompilatora. Główna zmiana dotyczy wskaźników na typ void*. W Cbyły one zupełnie bezkarne i można było przydzielać wskaźniki void* do każdych innych, w C++ są na równi zinnymi typami. Teoretycznie kod napisany w C powinien zostać bez problemu skompilowany w kompilatorze C++,lecz istnieje kilka rozbieżności, które czasami to uniemożliwiają. Jedna z nich dotyczy właśnie typu void*. Kod w C,bez problemu skompilowany w kompilatorze tegoż języka:

int* wskaznik = malloc (sizeof(int));

nie zostanie skompilowany w kompilatorze C++, z powodu zaostrzonej kontroli typów. Aby sprawić, że ten kodbędzie się kompilować musimy go odrobinę zmodyfikować:

int* wskaznik = (int*) malloc (sizeof(int));

Problem został rozwiązany przy użyciu rzutowania. Co to takiego? Odpowiedź znajdziesz w dziale poniżej.

RzutowanieW języku C rzutowanie wyglądało w następujący sposób:

int zmienna_calkowita = (int)zmienna_rzeczywista;

W C++ nadal można używać takiego rzutowania, jest ono nazywane "rzutowaniem w stylu C". Oprócz tego C++oferuje "rzutowanie w stylu funkcyjnym":

int zmienna_calkowita = int(zmienna_rzeczywista);

które działa dokładnie tak samo.Oba zapisy mają jedną istotną wadę - ciężko wypatrzeć je w kodzie. Każde rzutowanie jest potencjalnym miejscemwystąpienia błędów. Jeśli byśmy chcieli przejrzeć kod źródłowy w poszukiwaniu wszystkich rzutowań, nie byłobyto łatwe, przez co usuwanie błędów z programu w stylu języka C jest utrudnione.C++ wprowadza inny sposób zapisu, który od razu rzuca się w oczy. Dodatkowo rzutowanie podzielono na czterytypy:static_cast

proste rzutowanieconst_cast

rzutowanie ze zmiennych z modyfikatorem const i volatile na zmienne bez tych modyfikatorówreinterpret_cast

niebezpieczne rzutowania, które zmieniają zupełnie sens interpretacji bitów w zmiennychdynamic_cast

rzutowanie wskaźników na obiektyOstatnie z tych rzutowań będzie opisane później, w rozdziale ../Funkcje wirtualne/.Powodem takiego podziału jest potrzeba zwiększenia bezpieczeństwa przez wyeliminowanie pomyłek. Jak to działa?Jeśli chcielibyśmy dokonać pewnego rodzaju rzutowania operatorem, który nie jest do niego przewidziany,kompilator zgłosi nam błąd. Dodatkowo, jeśli podejrzewamy, że jakiś błąd w działaniu programu wynika zrzutowania, najczęściej chodzi nam o rzutowanie konkretnego rodzaju, zatem podział rzutowań ułatwia znajdywanietakich błędów.Nowych operatorów rzutowania używa się w następujący sposób:

Page 13: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 13

int zmienna_całkowita = static_cast<int>(zmienna_rzeczywista);

podając w nawiasach ostrych typ, na który rzutujemy.Omówimy teraz dłużej pierwsze trzy z nowych rzutowań.

Static_cast

Operator static_cast zapewnia wysoki poziom bezpieczeństwa, gdyż widząc static_cast kompilator używa całejswojej mądrości, żeby zagwarantować jak najsensowniejszy rezultat rzutowania, w razie potrzeby zmieniającreprezentację wartości poddanej rzutowaniu. Przykładowo przy rzutowaniu zmiennej typu int na float, bitywewnętrznej reprezentacji zostaną zmienione, tak aby reprezentowały tę samą wartość matematyczną, ale wedługformatu używanego dla float.

Static_cast służy w szczególności do:

•• Konwersji podstawowych typów liczbowych, np. int na float.•• Konwersji zdefiniowanych przez użytkownika.•• Konwersji wskaźnika na obiekt klasy pochodnej na wskaźnik na obiekt klasy podstawowej (tak zwane rzutowanie

do góry hierarchii dziedziczenia).•• Konwersji wskaźnika na obiekt klasy podstawowej na wskaźnik na obiekt klasy pochodnej (tak zwane rzutowanie

w dół hierarchii).Są też inne zastosowania, np. rzutowanie zmiennej za pomocą wyrażenia static_cast<void>(nazwa_zmiennej), którena niektórych kompilatorach pozwala uniknąć ostrzeżenia o nieużywaniu tej zmiennej.Nie przejmuj się, jeżeli trzy ostatnie punkty powyższej listy są niezrozumiałe. Staną się zrozumiałe po przeczytaniurozdziału o dziedziczeniu i definiowaniu konwersji typów. Ważny jest morał z przytoczenia tych zastosowań, amianowicie fakt, że static_cast służy do najczęściej wykorzystywanych, zdefiniowanych przez standard języka ibezpiecznych rzutowań. Czwarty punkt na powyższej liście przypomina jednak o tym, że nie zawsze rzutowaniestatic_cast jest bezpieczne w czasie wykonania programu.Wyjaśnienie dla zaawansowanych:

Jeśli wykonamy rzutowanie w dół na typ, który nie jest zgodny z rzeczywistym (dynamicznym) typem obiektu,rezultatem może być wysypanie się programu.

Do czego static_cast nie służy:

• Do rzutowania wskaźników na różne typy, jeśli nie ma specjalnie zdefiniowanej konwersji między tymiwskaźnikami. Przykładowo nie skompiluje się static_cast<int*>(i), jeśli zmienna i jest typu unsigned int* Nie udasię też rzutowanie ze wskaźnika na typ stały (z modyfikatorem const) na wskaźnik na typ niestały.

•• Do dynamicznego sprawdzania, czy rzutowanie mogłoby się powieść (czy ma sens). Nie miałoby to sensu, bo dlastatic_cast sposób rzutowania jest ustalany w czasie kompilacji. Zresztą nie ma żadnej informacji o błędzie, którąmożna by było sprawdzić.

Przykłady poprawnego użycia static_cast:

#include <iostream>

int main ()

{

int liczba = 5, liczba2 = 2;

std::cout << "5/2 int(bez rzutowania): " << liczba/liczba2 << std::endl;

std::cout << "5/2 float(static_cast): "

Page 14: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 14

<< static_cast<float>(liczba)/static_cast<float>(liczba2) << std::endl;

return 0;

}

Przykłady niepoprawnego użycia static_cast:

#include <iostream>

int main ()

{

std::string str = "ciag";

std::cout << "string --> char: " << static_cast<char>(str) << std::endl;

return 0;

}

Inne Cechy static_cast

Standard języka stwierdza również, że wyrażenia, które nie dokonują żadnej konwersji mogą być również opisaneoperatorem static_cast, np. int i = static_cast<int>(8);. Takie static_cast może być bezpiecznieusunięte z kodu, należy jednak uważać na usuwanie go z kodu generycznego, korzystającego z szablonów.W powyższym wstępie i przykładach wszędzie, gdzie jest mowa o wskaźnikach, można by również mówić oreferencjach. Obowiązują je te same reguły.Należy pamiętać, że działanie rzutowania static_cast zależy tylko od takich informacji o typach, które są dostępneczasie kompilacji. Stąd słowo "static" w "static_cast". Kompilator nie dodaje "z własnej inicjatywy" kodu binarnego,więc static_cast można używać również w tzw. wąskich gardłach programu. Poprzednie zdanie celowo używawyrażenia w cudzysłowie, bo jakiś kod oczywiście jest dodawany przez kompilator. Zazwyczaj jest to jednak tylkozmiana reprezentacji liczby lub wywołanie zdefiniowanej przez użytkownika (czyli z naszej inicjatywy) funkcjikonwertującej.

reinterpret_cast#include <iostream>

using namespace std;

int main(void)

{

typedef unsigned long long ULL;

typedef unsigned int UI;

ULL a = 137438953600;

//Liczba a w pamięci komputera:

//00000000000000000000000000100000

//00000000000000000000000010000000

ULL* wsk_a_ll = &a;

//ULL* wsk_a_int = static_cast<UI*>(&a); //błąd kompilatora -

niedozwolone rzutowanie static_cast

UI* wsk_a_int = reinterpret_cast<UI*>(&a);

cout << *wsk_a_ll << "\n" << wsk_a_int[0] << " " << wsk_a_int[1] << "\n";

Page 15: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 15

return 0;

}

Wyjście programu:

137438953600

128 32

W powyższym przykładzie próbujemy udowodnić, że dowolny zaalokowany obszar pamięci możemy potraktowaćjako tablicę, a interpretacja danych zależy od tego, jaki jest typ wskaźnika, którym się posługujemy; tutaj kolejno(long long *) oraz (int *).Stosowane jest rzutowanie typu "reinterpret_cast", ponieważ "static_cast" skutkuje błędem kompilacji. Samorzutowanie jest niecodzienne i udowadnia, że "reinterpret_cast" należy używać jedynie w uzasadnionychokolicznościach.

Ćwiczenia#include <iostream>

int main (int argc, char *argv[])

{

int liczba, liczba2;

std::cin >> liczba >> liczba2;

double wynik = liczba / liczba2;

std::cout << wynik << std::endl;

return 0;

}

Po uruchomieniu powyższego programu i podaniu wejścia

5 2

Otrzymamy

2

Dlaczego jako wynik wyświetlana jest liczba 2 a nie 2.5? Rozwiąż problem przy użyciu rzutowania.

Page 16: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 16

Referencje

Czym jest referencja?Referencja w swym działaniu przypomina wskaźniki. Różnica polega jednak na tym, że do referencji możnaprzypisać adres tylko raz, a jej dalsze używanie niczym się nie różni od używania zwykłej zmiennej. Operacje jakiewykona się na zmiennej referencyjnej, zostaną odzwierciedlone na zmiennej zwykłej, z której pobrano adres.Można by pokusić się o stwierdzenie, że:

Referencja jest inną nazwą danej zmiennej.

Deklaracja referencjiReferencje deklaruje się jak zmienne z podaniem znaku &:

TypDanych & referencja

Taki zapis byłby możliwy w liście argumentów funkcji, jednak w ciele funkcji referencja musi być od razuzainicjalizowana. Zapisujemy do niej adres innej zmiennej (robi się to trochę inaczej niż w wypadku wskaźników):

TypDanych & referencja = innaZmienna;

Od tej pory można używać obu tych zmiennych zamiennie.Poniższe przypisania dadzą więc ten sam efekt:

innaZmienna = 9;

referencja = 9;

Zobaczmy działanie referencji na konkretnym przykładzie:

int i=0;

int &ref_i=i;

cout << i; // wypisuje 0

ref_i = 1;

cout << i; // wypisuje 1

cout << ref_i; // wypisuje 1

Porównajmy to z sytuacją, gdybyśmy użyli wskaźników:

int i=0;

int * wsk_i=&i;

cout << i; // wypisuje 0

*wsk_i = 1;

cout << i; // wypisuje 1

cout << *wsk_i; // wypisuje 1

Zauważmy, o ile wygodniejsze jest użycie referencji. Nie musimy ani pobierać adresu zmiennej (&i) by przypisaćgo do referencji ani też używać gwiazdki by dostać wskazywaną wartość.Jeszcze jedną różnicą ze wskaźnikami jest ograniczenie, że referencji po przypisaniu nie można przestawić na innązmienną. Referencja musi też być zainicjalizowana w momencie utworzenia:

Page 17: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 17

int a,b;

int *wsk_a=&a, *wsk_b=&b;

int &ref_a=a, &ref_b=b;

int &ref_c; // kompilator nie zezwoli na to - referencja

niezainicjalizowana

wsk_b = &a; // ok

ref_b = &a; // tak się nie da

Przykład przypomina też, że analogicznie jak w przypadku wskaźników znak & nie łączy się z typem tylko zezmienną i przy deklarowaniu kilku referencji na raz trzeba wstawiać & przed każdą z nich:

int &ref_x = x, &ref_y = y; // referencje

char *wsk_1, *wsk2; // wskazniki

Stałe referencjeMożliwe jest zadeklarowanie referencji do obiektów stałych - wtedy obiektu, do którego odnosi się referencja niebędzie można zmienić.

int i=0;

const int &ref_i=i;

cout << ref_i; // wypisze 0

ref_i = 1; // kompilator nie pozwoli na to i zgłosi błąd

Powody, dla jakich możemy chcieć używać stałych referencji są analogiczne jak dla stałych wskaźników.

Przekazywanie argumentów przez referencjęAby w C zmodyfikować parametr przekazywany do funkcji, musieliśmy używać wskaźników. C++ proponujebezpieczniejszą i wygodniejszą w użyciu metodę - przekazywanie przez referencję.Różnica między przekazywaniem przez referencję a przekazywaniem przez wskaźnik jest taka jaka miedzyreferencjami i wskaźnikami, nie ma tu żadnej rewolucji. Przykład zastosowania pokazany jest poniżej:

void nie_zwieksz (int i)

{

++i; // tak naprawdę funkcja nie robi nic, bo zmieniona zostaje

tylko lokalna kopia

}

void zwieksz_c (int *i)

{

++(*i); // ta funkcja jest napisana w stylu C

}

void zwieksz_cpp (int& i)

{

++i; // ta funkcja wykorzystuje możliwości C++

}

int main ()

{

int a=0, b=0, c=0;

nie_zwieksz (a);

Page 18: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 18

zwieksz_c (&b);

zwieksz_cpp (c);

cout << a << " " << b << " " << c;

// wypisze "0 1 1"

return 0;

}

Funkcje inlineFunkcje inline jak można by się domyśleć z nazwy są funkcjami "w linii" w C++ znaczy to, że kompilator widząc żefunkcja jest inline w miejsce jej wywołania nie wstawia jak w normalnym przypadku wskaźnika do tej funkcji wpamięci, lecz wpisuje jej kod w miejsce jej wystąpienia. Takie funkcje dalej jednak występują w pamięci komputera,dzięki czemu możemy tworzyć do nich wskaźniki i używać ich jak w przypadku zwykłych funkcji.Użycie funkcji inline:

inline int dodaj (int, int); //deklaracja

int main ()

{

int a, b, c;

c = dodaj (a,b);

return 0;

}

inline int dodaj (int a,int b) //definicja

{

return a+b;

}

Rzeczywiste działanie:

int main ()

{

int a, b, c;

{

c = a+b; //podstawianie kodu funkcji

}

return 0;

}

Ma to zastosowanie, gdy zależy programiście na szybkości działania programu. Status inline zazwyczaj dodaje siękrótkim funkcjom, nie mającym więcej niż kilkanaście linijek kodu. Czasami gdy kompilator uzna, że nasza funkcjajest zbyt długa lub wywołuje się rekurencyjnie ignoruje nasze inline. Gdy chcemy wymusić takie zachowanie toużywamy np. __forceinline dla MS Visual C++. Funkcja składowa klasy zostaje natomiast automatycznieuznana za inline jeśli napiszemy jej kod bezpośrednio po jej deklaracji we wnętrzu klasy. Warto dodać że słowoinline jest słowem kluczowym w C++.Teoretycznie makroinstrukcje języka C mają dość podobne działanie, lecz funkcje inline mają nad nimi kilkaprzewag:

Page 19: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 19

czytelnośćmakra języka C są znacznie mniej czytelne i są tak jakby "czarną owcą" w kodzie, gdyż są wplecione międzyróżnorakie funkcje, które stanowią większość kodu, od których znacznie różnią się zapisem.

konwersja argumentówjako, że funkcja inline imituje zwykłą funkcję, posiada argumenty z kontrolą typów, dzięki czemu inniprogramiści mogą z nich łatwiej korzystać w stosunku do makroinstrukcji.

argumenty jako zmiennew przypadku makroinstrukcji argumenty nie są traktowane jako zmienne; to co przekażemy jako argument,jest po prostu kopiowane w miejsca użycia danego argumentu w kodzie makroinstrukcji. Funkcje inlineposiadają argumenty, które są zmiennymi, co wyklucza wiele błędów.

Gdzie tu jest haczyk? Otóż jako, że kod funkcji jest wstawiany w miejsce wywołania, to jeśli wywołamy tę funkcjęw 3 miejscach, dostaniemy 3 kopie kodu tejże funkcji. Jeśli przesadzimy i będziemy dodawać przedrostek inline dozbyt wielu funkcji (zwłaszcza tych dużych i często wywoływanych), plik wykonywalny może urosnąć doniebotycznych rozmiarów (co dodatkowo przedłuża czas jego uruchamiania).

Przeciążanie funkcjiW języku C++ możliwe jest utworzenie kilku różnych funkcji, które posiadają tę samą nazwę. Takie funkcje musząróżnić się od siebie ilością lub typem argumentów. Dzięki temu kompilator będzie wiedział dokładnie, którą funkcjęnależy wywołać. Takie funkcje nazywamy przeciążonymi (czasem również – przeładowanymi).

Przeciążanie (przeładowanie) funkcji to zabieg polegający na utworzeniu kilku funkcji o tej samej nazwie, nazywanych funkcjamiprzeciążonymi. Takie funkcje muszą różnić się liczbą lub typem argumentów przekazywanych do tej funkcji, dodatkowo mogą różnić sięzwracanym typem.

Oto przykłady funkcji przeciążonych:

void funkcja (int);

void funkcja (std::string);

void funkcja (std::string, std::string);

// int funkcja (int); //niedozwolone, funkcje różnią się tylko

zwracanym typem

int funkcja (bool); //dozwolone

Czasami kompilator może zabronić przeładowania, gdy uzna, że typy argumentów są zbyt podobne. Może tak siędziać na przykład w przypadku, gdy:• użyjemy typu const T i T,•• użyjemy argumentów domyślnych.

void funkcja (int arg1, int arg2 = 0);

void funkcja (int arg1); //to ta sama funkcja, zostanie zgłoszony błąd

Kompilator obsługuje przeciążanie przez dodanie do nazwy każdej z przeciążonych funkcji specjalnegoidentyfikatora, który związany jest z liczbą i typem argumentów - tak więc po etapie kompilacji wszystkie funkcjemają unikalne nazwy.

Page 20: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 20

ZastosowaniePrzeciążenie funkcji stosuje się przy np. potęgowaniu:

int pot (int, int);

double pot (double, int);

void pot (int&, int);

int pot (int podstawa, int wykladnik)

{

int wynik = 1;

for (int i = 0; i < wykladnik; ++i)

wynik = podstawa*wynik;

return wynik;

}

// przeładowana funkcja I: zwraca inny typ danych i są inne parametry

double pot (double podstawa, int wykladnik)

{

double wynik = 1;

for (int i = 0; i < wykladnik; ++i)

wynik = podstawa*wynik;

return wynik;

}

// przeładowana funkcja II: nie zwraca danych tylko modyfikuje podstawę

która jest podana przez referencję

void pot (int& podstawa, int wykladnik)

{

int wynik = 1;

for (int i = 0; i < wykladnik; ++i)

wynik = podstawa*wynik;

podstawa = wynik;

}

Argumenty domyślnePierwszym sposobem przeładowania są argumenty domyślne. Deklaracja funkcji wygląda tak:

int potega (int podstawa, int wykładnik = 2);

W tym przypadku, kiedy funkcje wywołamy poprzez potega(2), zostanie dodany parametr domyślny. Będzie towięc znaczyło to samo, co potega(2, 2).Nie możemy już jednak przeciążyć tej funkcji poniższą:

int potega (int podstawa)

{

return podstawa*podstawa;

}

Page 21: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 21

W tym przypadku, gdy podamy jeden argument, kompilator nie będzie mógł określić o którą funkcję nam chodzi -dwuargumentową z jednym argumentem domyślnym, czy zwykłą jednoargumentową.

Typ argumentówCzasem możemy chcieć, by funkcja zachowywała się zależnie od tego, jakie argumenty jej dano. Załóżmy, żepiszemy własną bibliotekę do obsługi standardowego wyjścia stdout. Chcemy zrobić funkcję wypisującą różne typydanych, w tym typ łańcuchów C++.

void pisz (char);

void pisz (std::string);

void pisz (void);

void pisz (char a){

printf ("%c", a);

}

void pisz (std::string a)

{

printf ("%s", a.c_str());

}

void pisz ()

{

printf ("\n");

}

Uwaga!Zwróćmy uwagę na wywołanie printf("%s", a.c_str()). Należy pamiętać, że pole %s oczekuje łańcucha znaków typuchar*. Nie obsłuży zatem typu string, dlatego posługujemy się funkcją c_str() zwracającą napis jako tablicę char.

Szablony funkcjiW C++ dostępne są szablony. Wyobraź sobie sytuację: programista pisze funkcję obsługującą sporo typów danych.Możemy rozwiązać to przez kopiowanie funkcji w kodzie. jednak byłoby to dość uciążliwe. Możemy to zrobićkrócej:

template <typename T>

T nazwaFunkcji (argumenty typu T)

{

//do funkcji można przekazać dowolny typ danych

}

Page 22: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 22

Zarządzanie pamięciąW języku C++ do alokowania pamięci służy operator new a do zwalniania - delete. W C++ można równieżstosować funkcje malloc i free, jednak należy być ostrożnym. Najczęstszym błędem jest mieszanie operatorów new idelete z funkcjami malloc i free, np. zwalnianie pamięci zaalokowanej przez new przy pomocy free.Rozważmy prosty przykład. Załóżmy, że chcemy stworzyć wektor 10 liczb typu całkowitego. Możemy to zrobić nadwa sposoby. W stylu znanym z języka C:

int *wektor = (int*) malloc (sizeof(int)*10);

free (wektor);

Albo w stylu C++:

int *wektor = new int[10];

delete [] wektor;

Od razu widać, że drugi zapis jest łatwiejszy i przyjemniejszy w użyciu. To jest podstawowa zaleta operatora new -krótszy zapis. Wystarczy wiedzieć jakiego typu ma być obiekt, który chcemy powołać do życia, nie martwiąc się orozmiar alokowanego bloku pamięci. Za pomocą operatora new można również tworzyć tablice wielowymiarowe:

int **wektory = new int *[5];

for (int i = 0; i < 5; ++i)

wektory[i] = new int [10];

W ten sposób stworzono tablicę dwuwymiarową którą statycznie zadeklarowalibyśmy jako:

int wektory[5][10];

Jenak w przeciwieństwie do int wektory[5][10], która jest tablicą dwuwymiarową, nasze int**wektory jest tablicą tablic i może być rozrzucone po całej pamięci.Ilość elementów poszczególnych wymiarów nie musi być jednakowa. Można np zadeklarować tablicę taką:

int **wektory = new int *[2];

wektory[0] = new int [5];

wektory[1] = new int;

Przy takiej deklaracji pierwszy wiersz ma 5 elementów (tablica) a drugi to jeden element. Deklaracja tablic owiększej ilości wymiarów przebiega podobnie:

int ***wektory; // deklarujemy tablicę 3-wymiarową

wektory = new int **[5]; // pierwszy wymiar

wektory[0] = new int *[10]; // pierwszy element pierwszego wymiaru

wektory[1] = new int *[3]; // drugi element pierwszego wymiaru

....

wektory[0][0] = new int [3] // wymiar I = 0 -> wymiar II = 1 -> 3

elementy(tablica)

wektory[0][1] = new int [5] // wymiar I = 0 -> wymiar II = 3 -> 5

elementów(tablica)

wektory[1][0] = new int; // wymiar I = 1 -> wymiar II = 2 -> 1 element

...

Stosując ten sposób, ogólnie można deklarować tablice n-wymiarowe bez większego problemu. Usuwanie tablicwielowymiarowych przebiega podobnie jak jednowymiarowych, z tą różnicą, że usuwanie zaczynamy od

Page 23: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 23

"najgłębszego" wymiaru:

delete wektory[1][0]; // kasujemy pojedynczą zmienną

delete [] wektory[0][1];

delete [] wektory[0][0];

// II wymiar

delete [] wektory[0];

delete [] wektory[1];

// I wymiar

delete [] wektory;

Zwrócić uwagę trzeba na dwie rzeczy:• delete [] używamy dla zmiennych tablicowych, a delete dla pojedynczych zmiennych•• Kolejność zwalniania wymiarów jest odwrotna niż ich tworzeniaDrugą zaletą jest fakt, że przy okazji alokacji pamięci możemy wywołać odpowiedni konstruktor inicjując wartościzmiennych obiektu, np.

Test *test = new Test(1,2);

zakładając, że obiekt Test posiada dwie zmienne typu całkowitego i zdefiniowany konstruktor Test(int,int).Kolejną korzyścią jest możliwość przeciążania. Jednak to już jest temat na inny rozdział.

Część 2Podstawy programowania obiektowego

Czym jest obiekt?Aby odpowiedzieć na pytanie zadane w temacie, zadajmy sobie inne:

Co nazywamy obiektem w świecie rzeczywistym?

Otóż wszystko może być obiektem! Drzewa, zwierzęta, miasta, auta, ludzie...W programowaniu również obiektem może być dowolny twór, o jakim pomyślimy. Tworząc "świat programu"można stworzyć obiekt, którego użycie będzie bardziej "namacalne" od szeregu parametrów, porozrzucanych wróżnych zmiennych. To różni programowanie strukturalne od programowania obiektowego.

Projekt i twór – klasa i obiektZanim stworzymy jakiś obiekt, trzeba ustalić czym ten obiekt będzie. W zależności od tego, czy chcemy stworzyćwirtualny samochód, czy samolot, należy określić dwie rzeczy:• jakie właściwości będzie miał ten obiekt,• jakie będzie miał metody działania.W związku z tym, przed stworzeniem jakiegokolwiek obiektu należy przedstawić kompilatorowi jegoprojekt(wzorzec), czyli określić jego klasę.

Klasa to byt programistyczny określający jakie właściwości i metody będą miały obiekty, które zostaną utworzone na jej podstawie.

Jednak sam projekt nie sprawi jeszcze, że dostaniemy obiekty (to tak jakby po narysowaniu projektu domu chciećzamieszkać na kartce papieru :-)). Trzeba jeszcze obiekt utworzyć, co oznacza po prostu deklarację obiektu napodstawie pewnej klasy:

Page 24: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 24

NazwaKlasy MojObiekt;

Wygląda to jak deklaracja zwykłej zmiennej i tak jest w istocie – w C++ tworząc klasę definiuje się nowy typdanych. Podobnie jak w przypadku zmiennych, można utworzyć wiele obiektów danej klasy.

Definicja klasyOgólny szablon definiowania klas w C++ wygląda następująco:

class NaszaNazwaKlasy {

... // pola i metody składowe klasy

};

Po słowie kluczowym class następuje nazwa naszej klasy (prawidła jej nazywania są takie same jak dla zmiennych).W nawiasach klamrowych umieszcza się definicje składowych klasy: pól i metod określając dla nich specyfikatorydostępu.Należy pamiętać o średniku za klamerką zamykającą definicję klasy.Oto przykładowa definicja klasy:

class NazwaKlasy {

public: //pola i metody są publicznie dostępne

//definiowanie pól

int poleInt;

float poleFloat;

//deklarowanie metod

int Metoda1();

void Metoda2();

}; //pamiętaj o średniku!

Użycie klasySama definicja klasy nie wystarczy, aby uzyskać dostęp do jej składowych. Należy stworzyć obiekt. Można przyjąć,że obiekt to zmienna typu klasowego. Deklaracja obiektu:

NazwaKlasy Obiekt;

Dostęp do pól i metod uzyskuje się operatorem (.):

Obiekt.poleInt = 0;//przypisanie wartości polom

Obiekt.poleFloat = 9.04;

Obiekt.Metoda1();//wywołanie metody obiektu

W przypadku deklaracji wskaźnika do obiektu:

NazwaKlasy *ObiektWsk = new NazwaKlasy;

Analogicznie jak w przypadku wskaźników na struktury operatorem dostępu do pola/metody klasy poprzezwskaźnik do obiektu staje się ->:

Page 25: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 25

ObiektWsk->poleInt = 0; //przypisanie wartości polom

ObiektWsk->poleFloat = 9.04;

ObiektWsk->Metoda1(); //wywołanie metody obiektu

Należy pamiętać o zniszczeniu obiektu przed zakończeniem działania programu (lub kiedy nie jest nam jużpotrzebny):

delete ObiektWsk;

PrzykładStwórzmy klasę kostki do gry:

class Kostka{

public:

unsigned int wartosc;

unsigned int maks;

void Losuj();

};

Po definicji klasy, zdefiniujmy jeszcze metodę Losuj() zadeklarowaną w tej klasie:

void Kostka::Losuj()

{

wartosc = rand()%maks + 1;

}

Warto zwrócić uwagę w jaki sposób się to robi. Nazwą metody dowolnej klasy jest NazwaKlasy::NazwaMetody.Poza tym aby uzyskać dostęp do pól klasy, w której istnieje dana metoda nie stosuje się operatora wyłuskania.Po tym można napisać resztę programu:

int main()

{

Kostka kostkaSzescienna; //utworzenie obiektu

kostkaSzescienna.maks = 6; //określenie maksymalnej ilosci oczek

kostkaSzescienna.Losuj(); //losowanie

cout << "Wylosowano:" << kostkaSzescienna.wartosc << endl;//wypisanie wyniku

return 0;

}

AutorekursjaWskaźnik this umożliwia jawne odwołanie się zarówno do atrybutów, jak i metod klasy. Poniższy programwymusza użycie wskaźnika this, gdyż nazwa pola jest taka sama jak nazwa argumentu metody wczytaj:

#include <iostream>

class KlasaThis {

int liczba;

public:

void wczytaj(int liczba) {this->liczba=liczba;}

void wypisz() {cout << liczba <<endl;}

Page 26: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 26

};

int main()

{

KlasaThis ObiektThis;

ObiektThis.wczytaj(11);

ObiektThis.wypisz();

return 0;

}

Kontrola dostępuIstnieją trzy specyfikatory dostępu do składowych klasy:• private (prywatny) - dostępne tylko z wnętrza danej klasy i klas/funkcji zaprzyjaźnionych.• protected (chroniony) - dostępne z wnętrza danej klasy, klas/funkcji zaprzyjaźnionych i klas pochodnych.• public (publiczny) - dostępne dla każdego.Jeśli sekwencja deklaracji składowych klasy nie jest poprzedzona żadnym z powyższych specyfikatorów, todomyślnym specyfikatorem (dla kompilatora) będzie private.Dzięki specyfikatorom dostępu inni programiści mają ułatwione korzystanie z utworzonej przez nas klasy, gdyżmetody i pola, których nie powinni modyfikować, bo mogłoby to spowodować niepoprawne działanie obiektu, sąoznaczone jako private lub protected i nie mogą z nich korzystać. Funkcje, które zapewniają pełną funkcjonalnośćklasy oznaczone są jako public i tylko do nich ma dostęp użytkownik klasy (do protected również, ale zograniczeniami). Oto zmodyfikowany przykład z kostką, który zobrazuje cele kontroli dostępu:

class Kostka{

public :

void Losuj();

void Wypisz();

int DajWartosc();

void ZmienIloscScian(unsigned argMax);

protected:

unsigned wartosc;

unsigned max;

};

int Kostka::DajWartosc()

{

return this->wartosc;

}

void Kostka::ZmienIloscScian(unsigned argMax)

{

if(argMax> 20)

max = 20;

else

max = argMax;

}

Page 27: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 27

Zmodyfikowana klasa zezwala tylko na kostki maksymalnie dwudziestościenne. Ręczne modyfikacje zmiennej maxsą zabronione, można tego dokonać jedynie poprzez funkcję ZmienIloscScian, która zapobiega przydzieleniuwiększej ilości ścianek niż 20. Prywatny jest też atrybut wartość. Przecież nie chcemy aby była ona ustawionainaczej niż przez losowanie! Dlatego możemy udostępnić jej wartość do odczytu poprzez metodę DajWartosc(), alemodyfikowana może być tylko na skutek działania metody Losuj().

Ćwiczenia1. Napisz klasę reprezentującą człowieka. Musi on być opisany przy pomocy: imienia, nazwiska, płci, wieku,partnerki/partnera(jako wskaźnik).2. Rozwiń klasę napisaną w 1. ćwiczeniu dodając ograniczenie, między partnerami nie może być większa niż 25%wieku starszej z nich.3*. Jeśli zaznajomiłeś się z wektorami, dodaj kolejny parametr opisujący ludzi - zainteresowania, dodaj odpowiedniąfunkcję do dodawania nowych zainteresowań do listy oraz funkcję porównującą zainteresowania obu ludzi izwracającą procent identycznych zainteresowań.

Konstruktor i destruktor

Teoria

WstępPisząc klasy każdy kiedyś dotrze do momentu, w którym będzie odczuwał potrzebę napisania funkcji wykonującejjakieś niezbędne instrukcje na początku lub na końcu istnienia obiektu. W takim momencie programista powiniensięgnąć po dwa niezwykle przydatne narzędzia: konstruktory i destruktory.

KonstruktorKonstruktor jest to funkcja w klasie, wywoływana w trakcie tworzenia każdego obiektu danej klasy. Funkcja możestać się konstruktorem gdy spełni poniższe warunki•• Ma identyczną nazwę jak nazwa klasy• Nie zwraca żadnej wartości (nawet void)Należy dodać że każda klasa ma swój konstruktor. Nawet jeżeli nie zadeklarujemy go jawnie zrobi to za naskompilator (stworzy wtedy konstruktor bezparametrowy i pusty).Mamy na przykład klasę Miesiac. Chcielibyśmy, aby każdy obiekt tej klasy tuż po utworzeniu wygenerował tablicę znazwami dni tygodnia w zależności od miesiąca i roku. A może dało by się to zrobić w trakcie tworzenia klasy?Przyjrzyj się poniższej klasie, oraz funkcji konstruktora:

class Miesiac

{

public:

int dni[31];

int liczbaDni;

string nazwa;

Miesiac();//deklaracja konstruktora

};

Miesiac::Miesiac()//definicja konstruktora

{

Page 28: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 28

// instrukcje tworzące

}

Konstruktor może też przyjmować argumenty. Jak?To zależy od sposobu w jaki tworzymy obiekt:•• jako obiekt

MojaKlasa obiekt(argumenty);

•• jako wskaźnik do obiektu:

MojaKlasa* wsk = new MojaKlasa(argumenty);

Teraz powyższa klasa miesiąca może być stworzona z uwzględnieniem numeru miesiąca i roku:

class Miesiac

{

public:

int dni[31];

int liczbaDni;

string nazwa;

Miesiac(int numer,int rok);

};

Miesiac::Miesiac(int numer,int rok)

{

/* instrukcje tworzące */

}

Aby utworzyć nowy obiekt tej klasy trzeba będzie napisać:

Miesiac styczen2000(1,2000);

lub jako wskaźnik do obiektu:

Miesiac* styczen2000 = new Miesiac(1,2000);

otrzymawszy w ten sposób kalendarz na styczeń.Najczęstszą funkcją konstruktora jest inicjalizacja obiektu oraz alokacja pamięci dla dodatkowych zmiennych (wtym celu lepiej jest użyć instrukcji inicjujących, które poznasz już za chwilę).

Instrukcje inicjująceInstrukcje inicjujące to instrukcje konstruktora spełniające specyficzne zadanie. Mianowicie mogą one zostaćwywołane przez kompilator zaraz po utworzeniu klasy. Służą do inicjowania pól klasy, w tym stałych i referencji.Jeśli nie zaimplementujemy instrukcji inicjujących, niczego nie będą one robiły.Jeżeli chcemy zaimplementować instrukcje inicjujące, musimy po liście argumentów konstruktora, użyćdwukropka, podać nazwę pola, które chcemy zainicjować i jego wartość ujętą w nawiasy okrągłe.

Rok()

: miesiace(new Miesiac[12])

, liczbaDni(7)

Page 29: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 29

/*

zamiast średników stosuje się przecinki

przy ostatniej instrukcji przecinka nie stosuje się

*/

{}

Działa to podobnie jak użycie inicjowania w konstruktorze, jednak w przypadku instrukcji inicjujących pola będązainicjowane w trakcie tworzenia klasy, a nie po utworzeniu jej obiektu.

Konstruktor kopiującyKonstruktor kopiujący to konstruktor spełniający specyficzne zadanie. Mianowicie może on zostać wywoływanyprzez kompilator niejawnie jeżeli zachodzi potrzeba stworzenia drugiej instancji obiektu (np. podczas przekazywaniaobiektu do funkcji przez wartość).Jeżeli nie zaimplementujemy konstruktora kopiującego, kompilator zrobi to automatycznie. Konstruktor taki będziepo prostu tworzył drugą instancję wszystkich pól obiektu. Możemy go jawnie wywołać np. tak:

Miesiac miesiac(12,2005);

Miesiac kopia(miesiac); //tu zostanie wywołany konstruktor kopiujący

/* obiekt kopia będzie miał taką samą zawartość jak obiekt miesiąc */

Jeżeli chcemy sami zaimplementować konstruktor kopiujący musimy zadeklarować go jako konstruktor ojednym parametrze będącym referencją na obiekt tej samej klasy.

class Miesiac

{

public:

int numer;

int rok;

Miesiac(const Miesiac &miesiac)

{

numer=miesiac.numer;

rok=miesiac.rok;

}

};

Page 30: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 30

PoradaJeżeli dokonujemy w instrukcjach inicjujących alokacji pamięci, np:

class Rok { protected: Miesiac *miesiace; public: Rok() : miesiace(new Miesiac[12]) {} virtual ~Rok() {delete[] miesiace;} };

to nie możemy się zdać na konstruktor kopiujący tworzony niejawnie. Jeżeli tak zrobimy, to w obiekcie stworzonym przez konstruktorkopiujący pole miesiace będzie wskazywać na ten sam fragment pamięci, co w obiekcie wzorcowym. Jeżeli nie jest to zamierzony efekt(a zwykle nie jest) musimy sami zaimplementować konstruktor kopiujący, np. tak:

class Rok { protected: Miesiac *miesiace; public: Rok() : miesiace(new Miesiac[12]) {} Rok(const Rok &rok) { //musimy sami zaalokować pamięć na pole ''miesiace'' miesiace=new Miesiac[12]; //oraz przypisać temu polu odpowiednie wartości for (int i=0; i<12; ++i) miesiace[i]=Miesiac(rok.miesiace[i]); } virtual ~Rok() {delete[] miesiace;} };

DestruktorDestruktor jest natomiast funkcją, którą wykonuje się w celu zwolnienia pamięci przydzielonej dodatkowymobiektom lub innych zasobów.Zasady "przemiany" zwykłej funkcji do destruktora, są podobne do tych tyczących się konstruktora. Jedyna zmianatyczy się nazwy funkcji: Musi się ona zaczynać od znaku tyldy - ~.

class MojaKlasa

{

MojaKlasa();//to oczywiście jest konstruktor

~MojaKlasa();//a to - destruktor

};

Najczęstszą funkcją destruktora jest zwolnienie pamięci (zwykle poprzez zniszczenie wszystkich pól używanychprzez ten obiekt).

Page 31: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 31

PoradaNależy pamiętać, że jeżeli zamierzamy implementować dziedziczenie po klasie dla której piszemy destruktor to powinniśmy stworzyćdestruktor wirtualny!

class MojaKlasa { MojaKlasa(); virtual ~MojaKlasa();//to jest destruktor wirtualny };

Początkujący programiści często o tym zapominają, doprowadzając w ten sposób czasami do tzw. wycieków pamięci. Dobrą praktyką jesttworzenie tylko destruktorów wirtualnych (patrz Funkcje wirtualne).

Ćwiczenia

Ćwiczenie 1Napisz definicje instrukcji inicjujących do poniższej klasy:

class Vector

{

public:

double x;

double y;

public:

Vector();

Vector(double, double);

};

Klasa ma reprezentować wektor w przestrzeni dwuwymiarowej, a instrukcje inicjujące mają realizować inicjalizacjętego wektora. Pierwsze instrukcje inicjujące powinny ustawiać wektor na wartość domyślną (0,0).

Ćwiczenie 2Dopisz do kodu z poprzedniego ćwiczenia konstruktor kopiujący.

Vector(const Vector&);

Po wykonaniu tego ćwiczenia zastanów się, czy napisanie konstruktora kopiującego było konieczne. Jeżeli nie jesteśpewien - napisz program który testuje działanie Twojego konstruktora kopiującego i sprawdź jak program działa bezniego. Wyjaśnij dlaczego konstruktor kopiujący nie jest potrzebny.

Ćwiczenie 3Poniższa klasa miała implementować dowolnej wielkości tablicę obiektów klasy Vector z poprzednich ćwiczeń.Niestety okazało się że powoduje wycieki pamięci - programista zapomniał o napisaniu destruktora:

class VectorsArray

{

public:

Vector* vectors;

VectorsArray(size_t);

Vector GetVector(size_t);

Page 32: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 32

size_t GetSize();

size_t size;

};

VectorsArray::VectorsArray(size_t argSize)

: size(argSize)

, vectors(new Vector[argSize])

{

}

Vector VectorsArray::GetVector(size_t i)

{

return vectors[i];

}

size_t VectorsArray::GetSize()

{

return size;

}

Do powyższej klasy dopisz definicję destruktora. Nie zapomnij o dealokacji pamięci!

Dziedziczenie

Wstęp - Co to jest dziedziczenieCzęsto podczas tworzenia klasy napotykamy na sytuację, w której klasa ta powiększa możliwości innej klasy,nierzadko precyzując jednocześnie jej funkcjonalność. Dziedziczenie daje nam możliwość wykorzystania nowychklas w oparciu o stare klasy. Nie należy jednak traktować dziedziczenia jedynie jako sposobu na współdzieleniekodu między klasami. Dzięki mechanizmowi rzutowania możliwe jest interpretowanie obiektu klasy tak, jakby byłobiektem klasy z której się wywodzi. Umożliwia to skonstruowanie szeregu klas wywodzących się z tej samej klasyi korzystanie w przejrzysty i spójny sposób z ich wspólnych możliwości. Należy dodać, że dziedziczenie jest jednymz czterech elementów programowania obiektowego (obok abstrakcji, enkapsulacji i polimorfizmu).Klasę z której dziedziczymy nazywamy klasą bazową, zaś klasę, która po niej dziedziczy nazywamy klasąpochodną. Klasa pochodna może korzystać z funkcjonalności klasy bazowej i z założenia powinna rozszerzać jejmożliwości (poprzez dodanie nowych metod, lub modyfikację metod klasy bazowej).

SkładniaSkładnia dziedziczenia jest bardzo prosta. Przy definicji klasy należy zaznaczyć po których klasach dziedziczymy.Należy tu zaznaczyć, że C++ umożliwia Wielodziedziczenie, czyli dziedziczenie po wielu klasach na raz. Jest onoopisane w rozdziale Dziedziczenie wielokrotne.

class nazwa_klasy :[operator_widocznosci] nazwa_klasy_bazowej,

[operator_widocznosci] nazwa_klasy_bazowej ...

{

definicja_klasy

};

operator_widoczności może przyjmować jedną z trzech wartości: public, protected, private. Operator widocznościprzy klasie, z której dziedziczymy pozwala ograniczyć widoczność elementów publicznych z klasy bazowej.

Page 33: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 33

• public - oznacza, że dziedziczone elementy (np. zmienne lub funkcje) mają taką widoczność jak w klasiebazowej.

public public

protected protected

private brak dostępu w klasie pochodnej

• protected - oznacza, że elementy publiczne zmieniają się w chronione.public protected

protected protected

private brak dostępu w klasie pochodnej

• private - oznacza, że wszystkie elementy klasy bazowej zmieniają się w prywatne.public private

protected private

private brak dostępu w klasie pochodnej

• brak operatora - oznacza, że niejawnie (domyślnie) zostanie wybrany operator private..public private

protected private

private brak dostępu w klasie pochodnej

Dostęp do elementów klasy bazowej można uzyskać jawnie w następujący sposób:

[klasa_bazowa::...]klasa_bazowa::element

Zapis ten umożliwia dostęp do elementów klasy bazowej, które są "przykryte" przez elementy klasy nadrzędnej(mają takie same nazwy jak elementy klasy nadrzędnej). Jeżeli nie zaznaczymy jawnie o który element nam chodzikompilator uzna że chodzi o element klasy nadrzędnej, o ile taki istnieje (przeszukiwanie będzie prowadzone w głąbaż kompilator znajdzie "najbliższy" element).

Przykład 1

Definicja i sposób wykorzystania dziedziczeniaNajczęstszym powodem korzystania z dziedziczenia podczas tworzenia klasy jest chęć sprecyzowaniafunkcjonalności jakiejś klasy wraz z implementacją tej funkcjonalności. Pozwala to na rozróżnianie obiektów klas ijednocześnie umożliwia stworzenie funkcji korzystających ze wspólnych cech tych klas. Załóżmy że piszemyprogram symulujący zachowanie zwierząt. Każde zwierze powinno móc jeść. Tworzymy odpowiednią klasę:

class Zwierze

{

public:

Zwierze();

void jedz();

};

Następnie okazuje się, że musimy zaimplementowac klasy Ptak i Ryba. Każdy ptak i ryba jest zwierzęciem. Oprócztego ptak może latać, a ryba płynąć. Wykorzystanie dziedziczenia wydaje się tu naturalne.

class Ptak : public Zwierze

{

Page 34: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 34

public:

Ptak();

void lec();

};

class Ryba : public Zwierze

{

public:

Ryba();

void plyn();

};

Co istotne tworząc takie klasy możemy wywołać ich metodę pochodzącą z klasy Zwierze:

Ptak ptak;

ptak.jedz(); //metoda z klasy Zwierze

ptak.lec(); //metoda z klasy Ptak

Ryba *ryba=new Ryba();

ryba->jedz(); //metoda z klasy Zwierze

ryba->plyn(); //metoda z klasy Ryba

Możemy też zrzutować obiekty klasy Ptak i Ryba na klasę Zwierze:

Ptak *ptak=new Ptak();

Zwierze *zwierze;

zwierze=ptak;

zwierze->jedz();

Ryba ryba;

((Zwierze)ryba).jedz();

Jeżeli tego nie zrobimy, a rzutowanie jest potrzebne, kompilator sam wykona rzutowanie niejawne:

Zwierze zwierzeta[2];

zwierzeta[0]=Ryba(); //rzutowanie niejawne

zwierzeta[1]=Ptak(); //rzutowanie niejawne

for (int i=0; i<2; ++i)

zwierzeta[i].jedz();

Elementy chronione - operator widoczności protected

Sekcja protected klasy jest ściśle związana z dziedziczeniem - elementy i metody klasy, które się w niej znajdują,mogą być swobodnie używane w klasie dziedzicznej ale poza klasą dziedziczną i klasą bazową nie są widoczne.

Elementy powiązane z dziedziczeniemChciałbym zwrócić uwagę na inne, bardzo istotne elementy dziedziczenia, które są opisane w następnychrozdziałach tego podręcznika, a które mogą być wręcz niezbędne w prawidłowym korzystaniu z dziedziczenia(przede wszystkim Funkcje wirtualne).

Page 35: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 35

Funkcje wirtualnePrzykrywanie metod, czyli definiowanie metod w klasie pochodnej o nazwie i parametrach takich samych jak wklasie bazowej, ma zwykle na celu przystosowanie metody do nowej funkcjonalności klasy. Bardzo częstowywołanie metody klasy bazowej może prowadzić wręcz do katastrofy, ponieważ nie bierze ona pod uwagę zmianmiedzy klasą bazową a pochodną. Problem powstaje, kiedy nie wiemy jaka jest klasa nadrzędna obiektu, achcielibyśmy żeby zawsze była wywoływana metoda klasy pochodnej. W tym celu język C++ posiada funkcjewirtualne. Są one opisane w rozdziale Funkcje wirtualne.

Wielodziedziczenie - czyli dziedziczenie wielokrotneJęzyk C++ umożliwia dziedziczenie po wielu klasach bazowych na raz. Proces ten jest opisany w rozdzialeDziedziczenie wielokrotne.

Przykład 2 #include <iostream>

class Zwierze

{

public:

Zwierze()

{ }

void jedz( )

{

for ( int i=0; i<10; ++i )

std::cout << "Om Nom Nom Nom\n";

}

void pij( )

{

for ( int i=0; i<5; ++i )

std::cout << "Chlip, chlip\n";

}

void spij( )

{

std::cout << "Chrr...\n";

}

};

class Pies : public Zwierze

{

public:

Pies()

{ }

void szczekaj()

Page 36: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 36

{

std::cout << "Hau! hau!...\n";

}

void warcz()

{

std::cout << "Wrrrrrr...\n";

}

};

...

Za pomocą

...

class Pies : public Zwierze

{

...

utworzyliśmy klasę Psa, która dziedziczy klasę Zwierze. Dziedziczenie umożliwia przekazanie zmiennych, metoditp. z jednej klasy do drugiej. Możemy funkcję main zapisać w ten sposób:

...

int main()

{

Pies burek;

burek.jedz();

burek.pij();

burek.warcz();

burek.pij();

burek.szczekaj();

burek.spij();

return 0;

}

Składniki statyczne

WstępCzasami zachodzi potrzeba dodania elementu, który jest zwiazany z klasą, ale nie z konkretną instancją tej klasy.Możemy wtedy stworzyć element statyczny. Element statyczny jest właśnie elementem, który jest powiązany zklasą, a nie z obiektem tej klasy, czyli np. statyczna metoda nie może się odwołać do niestatycznej zmiennej lubfunkcji.

SkładniaElementy statyczne poprzedza się podczas definicji słówkiem static. Statyczne mogą być zarówno funkcje, jak ipola należące do klasy.

Page 37: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 37

class Klasa

{

protected:

static int iloscInstancji; // pole statyczne

public:

Klasa()

{

iloscInstancji++;

}

virtual ~Klasa()

{

iloscInstancji--;

}

static int IloscInstancji()

{

return iloscInstancji;

}

};

int Klasa::iloscInstancji=0;

Jak widać do obiektów statycznych z wewnątrz klasy możemy się odwołać tak samo jak do innych pól. PoleIloscInstancji w powyższym przykładzie nie jest jednak zwykłym polem - jest polem statycznym. Oznacza to, żepowstanie tylko jedna instancja tego pola. W powyższym przykładzie iloscInstancji ma za zadanie zliczania ilepowstało obiektów klasy Klasa.W powyższym przykładzie ponadto istnieje metoda statyczna. Z takiej metody nie można się odwołać doniestatycznych elementów klasy. Zarówno do klasy statycznej jak do statycznego pola możemy się odwołać nawetjeżeli nie został stworzony żaden obiekt klasy Klasa.Odwołanie się do metody statycznej IloscInstancji z programu wymaga następująco:

int i=Klasa::IloscInstancji();

Gdyby zaś pole iloscInstancji było publiczne, a nie chronione, to moglibyśmy się do niego odwołać poprzez:

int i=Klasa::iloscInstancji;

Ponieważ jednak w powyższym przykładzie pole iloscInstancji jest chronione możemy się do niego odwołać jedyniez klasy Klasa bądź z klas które po niej dziedziczą.Oczywiscie metody statyczne nie mogą być wirtualne.

Część 3Zawansowane programowanie

obiektowe

Page 38: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 38

Funkcje wirtualne

WstępFunkcje wirtualne to specjalne funkcje składowe, które przydają się szczególnie, gdy używamy obiektów posługującsię wskaźnikami lub referencjami do nich. Dla zwykłych funkcji z identycznymi nazwami to, czy zostanie wywołanafunkcja z klasy podstawowej, czy pochodnej, zależy od typu wskaźnika, a nie tego, na co faktycznie on wskazuje.Dysponując funkcjami wirtualnymi będziemy mogli użyć prawdziwego polimorfizmu - używać metod klasypochodnej wszędzie tam, gdzie spodziewana jest klasa podstawowa. W ten sposób będziemy mogli korzystać zmetod klasy pochodnej korzystając ze wskaźnika, którego typ odnosi się do klasy podstawowej. W tej chwili możesię to wydawać niepraktyczne, lecz za chwilę przekonasz się, że funkcje wirtualne niosą naprawdę sporo nowychmożliwości.

OpisNa początek rozpatrzymy przykład, który pokaże, dlaczego zwykłe, niewirtualne funkcje składowe nie zdająegzaminu gdy posługujemy się wskaźnikiem, który może wskazywać i na obiekt klasy podstawowej i na obiektdowolnej z jej klas pochodnych.Mając klasę bazową wyprowadzamy od niej klasę pochodną:

class Baza

{

public:

void pisz()

{

std::cout << "Tu funkcja pisz z klasy Baza" << std::endl;

}

};

class Baza2 : public Baza

{

public:

void pisz()

{

std::cout << "Tu funkcja pisz z klasy Baza2" << std::endl;

}

};

Jeżeli teraz w funkcji main stworzymy wskaźnik do obiektu typu Baza, to możemy ten wskaźnik ustawiać nadowolne obiekty tego typu. Można też ustawić go na obiekt typu pochodnego, czyli Baza2:

int main()

{

Baza *wsk;

Baza objB;

Baza2 objB2;

wsk = &objB;

Page 39: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 39

wsk -> pisz();

// Teraz ustawiamy wskaźnik wsk na obiekt typu pochodnego

wsk = &objB2;

wsk -> pisz();

return 0;

}

Po skompilowaniu na ekranie zobaczymy dwa wypisy: "Tu funkcja pisz z klasy Baza". Stało się tak dlatego, żewskaźnik jest do typu Baza. Gdy ustawiliśmy wskaźnik na obiekt typu pochodnego (wolno nam), a następniewywołaliśmy funkcję składową, to kompilator "na ślepo" sięgnął po funkcję pisz z klasy bazowej (bo wskaźnikwskazuje na klasę bazową).Można jednak określić żeby kompilator nie sięgał po funkcję z klasy bazowej, ale sam się zorientował na cowskaźnik pokazuje. Do tego służy przydomek virtual, a funkcja składowa nim oznaczona nazywa się wirtualną.Różnica polega tylko na dodaniu słowa kluczowego virtual, co wygląda tak:

class Baza

{

public:

virtual void pisz()

{

std::cout << "Tu funkcja pisz z klasy baza" << std::endl;

}

};

class Baza2 : public Baza

{

public:

virtual void pisz()

{

std::cout << "Tu funkcja pisz z klasy Baza2" << std::endl;

}

};

KonsekwencjeGdy funkcja jest oznaczona jako wirtualna, kompilator nie przypisuje na stałe wywołania funkcji z tej klasy, na którąpokazuje wskaźnik, już podczas kompilacji. Pozostawia decyzję co do wyboru właściwej wersji funkcji aż domomentu wykonania programu - jest to tzw. późne wiązanie. Wtedy program skorzysta z krótkiej informacjizapisanej w obiekcie a określającej klasę, do jakiej należy dany obiekt. Dopiero po odczytaniu informacji o klasiedanego obiektu wybierana jest właściwa metoda.Jeśli klasa ma choć jedną funkcję wirtualną, to do każdego jej obiektu dopisywany jest identyfikator tej klasy a dowywołania funkcji dopisywany jest kod, który ten identyfikator czyta i odnajduje odpowiednią funkcję. Gdy klasafunkcji wirtualnych nie posiada, takie informacje nie są dodawane, bo nie są potrzebne.Zauważmy też, że nie zawsze decyzja o wyborze funkcji jest dokonywana dopiero na etapie wykonania. Gdy doobiektów odnosimy się przez zmienną, a nie przez wskaźnik lub referencję to kompilator już na etapie kompilacjiwie, jaki jest typ (klasa) danej zmiennej (bo do zmiennej w przeciwieństwie do wskaźnika lub referencji nie można

Page 40: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 40

przypisać klasy pochodnej). Tak więc wirtualność nie gra roli gdy nie używamy wskaźników; kompilator generujewtedy taki sam kod, jakby wszystkie funkcje były niewirtualne. Przy wskaźnikach musi orientować się czytającinformację o klasie obiektu, na który wskazuje wskaźnik, bo moglibyśmy np. losować, czy do wskaźnikaprzypiszemy klasę bazową czy jej pochodną - wtedy przy każdym uruchomieniu programu byłaby wywoływana innafunkcja.Jak widać, za wirtualność się płaci - zarówno drobnym narzutem pamięciowym na każdy obiekt (identyfikatorklasy), jak i drobnym narzutem czasowym (odnajdywanie przy każdym wywołaniu odpowiedniej klasy i jej funkcjiskładowej). Jednak zyskujemy możliwośc płynnego rozwoju naszego programu przez zastępowanie klas ichpodklasami, co bez wirtualności jest niewykonalne. Przy możliwościach obecnych komputerów koszt wirtualnościjest zaniedbywalny, ale wciąż warto przemyśleć, czy potrzebujemy wirtualności dla wszystkich funkcji.

PrzykładPoniższy program zawiera deklaracje 3 klas: Figura, Kwadrat i Kolo. W klasie Figura zostałazadeklarowana metoda wirtualna (słowo kluczowe virtual) virtual float pole(). Każda z klas pochodnychod klasy Figura ma zaimplementowane swoje metody float pole(). Następnie (w funkcji main) znajdują siędeklaracje obiektów każdej z klas i wskaźnika mogącego pokazywać na obiekty klasy bazowej Figura.

#include <iostream>

const float pi = 3.14159;

class Figura

{

public:

virtual float pole() const

{

return -1.0;

}

};

class Kwadrat : public Figura

{

public:

Kwadrat( const float bok ) : a( bok ) {}

float pole() const

{

return a * a;

}

private:

float a; // bok kwadratu

};

class Kolo : public Figura

{

public:

Kolo( const float promien ) : r( promien ) {}

Page 41: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 41

float pole() const

{

return pi * r * r;

}

private:

float r; // promien kola

};

void wyswietlPole( Figura& figura )

{

std::cout << figura.pole() << std::endl;

return;

}

int main()

{

// deklaracje obiektow:

Figura jakasFigura;

Kwadrat jakisKwadrat( 5 );

Kolo jakiesKolo( 3 );

Figura* wskJakasFigura = 0; // deklaracja wskaźnika

// obiekty -------------------------------

std::cout << jakasFigura.pole() << std::endl; // wynik: -1

std::cout << jakisKwadrat.pole() << std::endl; // wynik: 25

std::cout << jakiesKolo.pole() << std::endl; // wynik: 28.274...

// wskazniki -----------------------------

wskJakasFigura = &jakasFigura;

std::cout << wskJakasFigura->pole() << std::endl; // wynik: -1

wskJakasFigura = &jakisKwadrat;

std::cout << wskJakasFigura->pole() << std::endl; // wynik: 25

wskJakasFigura = &jakiesKolo;

std::cout << wskJakasFigura->pole() << std::endl; // wynik: 28.274...

// referencje -----------------------------

wyswietlPole( jakasFigura ); // wynik: -1

wyswietlPole( jakisKwadrat ); // wynik: 25

wyswietlPole( jakiesKolo ); // wynik: 28.274...

return 0;

}

Wywołanie metod składowych dla każdego z obiektów powoduje wykonanie metody odpowiedniej dla klasy danego obiektu. Następnie wskaźnikowi wskJakasFigura zostaje przypisany adres obiektu jakasFigura i zostaje wywołana metoda float pole(). Wynikiem jest "-1" zgodnie z treścią metody float pole() w klasie Figura. Następnie przypisujemy wskaźnikowi adres obiektu klasy Kwadrat - możemy tak zrobić ponieważ klasa Kwadrat jest klasą pochodną od klasy Figura - jest to tzw. rzutowanie w górę. Wywołanie teraz metody

Page 42: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 42

float pole() dla wskaznika nie spowoduje wykonania metody zgodnej z typem wskaźnika - który jest typuFigura* lecz zgodnie z aktualnie wskazywanym obiektem, a więc wykonana zostanie metoda float pole() zklasy Kwadrat (gdyż ostatnie przypisanie wskaźnikowi wartości przypisywało mu adres obiektu klasyKwadrat). Analogiczna sytuacja dzieje się gdy przypiszemy wskaźnikowi adres obiektu klasy Kolo. Następniezostaje wykonana funkcja void wyswietlPole(Figura&) która przyjmuje jako parametr obiekt klasyFigura przez referencję. Tutaj również zostały wykonane odpowiednie metody dla obiektów klas pochodnych anie metoda zgodna z obiektem jaki jest zadeklarowany jako parametr funkcji czyli float Figura::pole().Takie działanie jest spowodowane przez przyjmowanie obiektu klasy Figura przez referencję. Gdyby obiektybyły przyjmowane przez wartość (parametr bez &) zostałaby wykonana 3 krotnie metoda float

Figura::pole() i 3 krotnie wyświetlona wartość -1.Wyżej opisane działanie zostało spowodowane przez określenie metody w klasie bazowej jako wirtualnej. Gdybyzostało usunięte słowo kluczowe virtual w deklaracji metody w klasie bazowej, zostałyby wykonane metody zgodnez typem wskaźnika lub referencji, a więc za każdym razem zostałaby wykonana metoda float pole() z klasyFigura.

Przeciążanie operatorówPrzeładowanie (przeciążanie) operatorów polega na nadaniu im nowych funkcji.

UżycieNie możemy przeładowywać operatorów dla typów wbudowanych (int, char, float).Przeciążanie polega na zdefiniowaniu tzw. funkcji operatorowej. Identyfikatorem funkcji operatorowej jest zawszesłowo kluczowe operator, bezpośrednio po którym następuje symbol operatora

typ_zwracany operator@ (argumenty)

{

// operacje

}

np.: operator+, operator-, operator<< itd. Co najmniej jeden argument tej funkcji musi być obiektemdanej klasy.

Przykład zastosowaniaMamy daną klasę Student

class Student

{

int nr_indeksu;

float srednia_ocen;

public:

Student(int nr=0, float sr=0) : nr_indeksu(nr), srednia_ocen(sr)

{}

};

i chcemy przeładować operator wyjścia <<Robimy to w następujący sposób:

Page 43: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 43

class Student

{

int nr_indeksu;

float srednia_ocen;

public:

Student(int nr=0, float sr=0) : nr_indeksu(nr), srednia_ocen(sr)

{}

friend ostream & operator<< (ostream &wyjscie, const Student &s);

};

ostream & operator<< (ostream &wyjscie, const Student &s)

{

return wyjscie << "Nr indeksu: " <<s.nr_indeksu << endl << "Srednia ocen: " <<s.srednia_ocen<<endl;

}

Aby zobaczyć, jak to działa, wystarczy że funkcja main() będzie miała następującą postać:

int main()

{

Student st, stu(10,5);

cout << st; // wypisze nr indexu = 0, srednia ocen=0,

// poniewaz st to wartosci konstruktora domyślnego :)

cout << stu; // wypisze nr indexu = 10, srednia ocen=5

return 0;

}

W powyższym przykładzie wprowadzone zostało także nowe pojęcie - zaprzyjaźnianie (friend). Funkcję F()deklarujemy jako zaprzyjaźnioną z klasą X, jeśli chcemy, aby F() miała dostęp do prywatnych lub chronionychdanych składowych klasy X.Ale weźmy przykład nieco prostszy. Chcemy sprawdzić czy to jest ten sam student - przeciążamy operator:

class Student

{

//...

public:

int operator==(const Student &q) {return

nr_indeksu==q.nr_indeksu};

int operator==(const int &q) {return nr_indeksu==q};

};

I niby wszystko jest pięknie, ale tu zacznają się schody... My, jako twórcy klasy wiemy, że porównanie dotyczytylko i wyłącznie numeru indeksu. Przy różnych średnich i tych samych indeksach dostaniemy wynik pozytywny. Anuż niech ktoś sobie ubzdura, że == odnosi się do wszystkich składowych...Dalsze zamieszanie wprowadzą kolejne zaproponowane przeze mnie operatory.

class Student

{

Page 44: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 44

//...

public:

bool operator< ( Student const &q) const {return srednia < q.srednia;}

bool operator< (int const &q) const {return srednia < q};

// itd dla kolejnych operatorów.

};

Samo w sobie nie jest groźne. Dopiero przy konfrontacji z poprzednim operatorem zaczyna wprowadzaćzamieszanie. Wszystko jest dobrze kiedy pamiętamy, jakie operacje dane operatory wykonują. Ale pamiętajmy:pamięć ludzka jest ulotna i ktoś inny (albo my) może spędzić kilka dni zanim dojdzie do tego, dlaczego todziała nie tak jak powinno.

Ale powyższy przykład wygląda naprawdę blado w porównaniu z tym:

class Student

{

//...

public:

int operator+ ( Student &q) {return (srednia + q.srednia +11) };

int operator+ ( int &q) {return (srednia - q / 30) };

};

Jak widzicie operator + wcale nas nie zmusza do wykonywania operacji dodawania. Możemy równie dobrzewewnątrz odejmować. A przy odejmowaniu porównywać. Lecz takie postępowanie nie jest intuicyjne. Takiepostępowanie jest dozwolone jedynie w przypadkach, kiedy startujecie w konkursie na najbardziej nieczytelny kod.Ale dość już straszenia. Teraz należy pokazać jak prawidłowo przeciążać operatory jednoargumentowe (++, --) orazjak prawidłowo zwracać obiekt np. przy dodawaniu.Oprócz operatorów arytmetycznych oraz działających na strumieniach można przeciążać również operatorylogiczne.

Operatory "bool" i "!"W języku C++ jest również możliwość przeciążania operatorów bool i !. Dzięki temu możemy w instrukcjiwarunkowej używać nazwy obiektu do testowania, czy spełnia on jakieś określone kryteria. Poniżej znajduje sięprosty przykład, który to ilustruje:

#include <iostream>

using namespace std;

template <typename T, int el>

class Tablica

{

public:

Tablica() : L_elementow(el) {}

operator bool() const {return (L_elementow != 0);}

bool operator!() const {return (L_elementow == 0);}

private:

T Tab[el];

size_t L_elementow;

};

Page 45: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 45

int main()

{

const n = 5;

Tablica <short, n> tab;

if(tab)

cout << "Tablica nie jest pusta." << endl;

if(!tab)

cout << "Tablica jest pusta." << endl;

Tablica <short, 0> tab2;

if(tab2)

cout << "Tablica nie jest pusta." << endl;

if(!tab2)

cout << "Tablica jest pusta." << endl;

return 0;

}

W efekcie na ekranie otrzymamy dwa wpisy:

Tablica nie jest pusta.

Tablica jest pusta.

W pierwszym przypadku tablica zawierała niezerową liczbę elementów i prawdę zwrócił operator bool. W drugimnatomiast liczba elementów wynosiła zero i prawdę zwrócił operator !.Oczywiście ładniej by było, gdybyśmy sprawdzali rozmiar przy użyciu if-else, zamiast dwóch if-ów, ale chciałempokazać wywołanie obu operatorów.

Operator "[]"W niektórych przypadkach bardzo przydatny jest operator indeksu []. Można go przeciążyć oczywiście w dowolnysposób, ale chyba najbardziej intuicyjne jest przypisanie mu funkcji dostępu do konkretnego elementu np. w tablicy.Posługując się przykładem klasy Tablica możemy dopisać do niej następujące dwa operatory:

T & operator[](size_t el) {return Tab[el];}

const T & operator[](size_t el) const {return Tab[el];}

oraz testową funkcję main

int main()

{

const n = 5;

TablicaInt <short, n> tab;

for(int i = 0; i < n; ++i)

{

tab[i] = i;

cout << tab[i] << endl;

Page 46: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 46

}

return 0;

}

Operatory nie muszą wykonywać dokładnie tych samych czynności. Można sobie wyobrazić przykład w którymoperator do zapisu zapisuje coś do tablicy, a w przypadku braku miejsca alokuje dodatkową pamięć. Operator stałynie będzie posiadał takiej funkcjonalności ponieważ nie może zmieniać obiektu na rzecz którego został wywołany.Zwróc uwage na słowo kluczowe const w definicji funkcji składowej klasy: " operator[] (int el) const ". Modifikatorconst stanowi część sygnatury funkcji. const zapewnia nam nie tylko właściwości funkcji skladowej const, alerównież umożliwia przeładowanie (przeciażenie) operatora.

Operator "()"Ten operator służy do tworzenia tzw. funktorów czyli klas które naśladują funkcje:

class Foo

{

public:

int operator() (int a, int b) {

return (a+b);

}

};

Nie jest to może najmądrzejszy przykład gdyż jest dostępny do przeciążania operator "+" ale oddaje zasadędziałania. Trzeba zaznaczyć że ten operator może zwracać dowolną wartość oraz przyjmować dowolną liczbęparametrów dowolnego typu. Niestety musi być zadeklarowany jako niestatyczna metoda klasy (gdyż inneoperatory które mogą być statyczne zwracają obiekt (&Foo operator...), choć możliwość udawania przez klasęfunkcji z pewnością to wynagrodzi.

Dziedziczenie wielokrotneDziedziczenie wielokrotne stanowi właściwość niektórych obiektowych języków programowania w których klasamoże dziedziczyć metody i pola z więcej niż jednej klasy bazowej.Języki wspierające dziedziczenie wielokrotne to: Eiffel, C++, Python, Perl, Curl, Common Lisp (via CLOS).

Część 4Zawansowane konstrukcje językowe

Obsługa wyjątków

WstępWyjątki pozwalają reagować na różne sytuacje wyjątkowe. Używa się ich tam gdzie istnieje ryzyko wystąpieniawyjątku.

Zarys wyjątkówJeżeli w jakimś miejscu programu zajdzie nieoczekiwana sytuacja, programista piszący ten kod powinien zasygnalizować o tym. Dawniej polegało to na zwróceniu specyficznej wartości, co nie było zbyt szczęśliwym rozwiązaniem, bo sygnał musiał być taki jak wartość zwracana przez funkcję. W przypadku obsługi sytuacji

Page 47: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 47

wyjątkowej mówi się o obiekcie sytuacji wyjątkowej, co często zastępowane jest słowem "wyjątek". W C++ wyjątkisię "rzuca", służy do tego instrukcja throw.

Szkielet obsługi wyjątkówTam gdzie spodziewamy się wyjątku umieszczamy blok try, w którym umieszczamy "podejrzane" instrukcje. Zatym blokiem muszą (tzn. musi przynajmniej jedna) pojawić się bloki catch. Wygląda to tak:

//jakaś zwykła funkcja, lub funkcja main

try // w instrukcjach poniżej może coś się nie udać

{

fun();

fun2(); //podejrzane funkcje

}

catch(std::string obj)

{

//tu coś robimy, na przykład piszemy o błędzie

}

W instrukcji catch umieszczamy typ jakim będzie wyjątek. Rzucić możemy wyjątek typu int, char, std::string iinne, dlatego tu określamy co nas interesuje. Nazwa tego obiektu nie jest konieczna, ale jeżeli chcemy znać wartośćmusimy ten obiekt nazwać. Bloków catch może być więcej, najczęściej tyle ile możliwych typów do złapania. Coważne jeżeli rzucimy wyjątek konkretnego typu to "wpadnie" on do pierwszego dobrego catch nawet jeżeli innenadają się lepiej (podobnie jak z instrukcjami if else). Dotyczy to zwłaszcza klas dziedziczonych. Przykładowo,jeżeli mamy klasę Pies, która dziedziczy z klasy Zwierze, to jeśli pierwszy pojawi się blok:

'''catch(Zwierze obj)'''

to on zostanie użyty do obsługi wyjątku.Zawsze dobrze jest się zabezpieczyć blokiem

'''catch'''(...)

Taki blok łapie wszystko. Dlatego kompilator nie dopuści by wszystko łapiący catch był przed innymi instrukcjamicatch.

Rzucanie wyjątkuPisząc funkcję możemy stwierdzić że coś poszło nie tak i chcemy zasygnalizować wyjątek. Jak to zrobic przedstawiakod:

double Dziel(double a, double b) //funkcja zwraca iloraz a / b

{

if(b == 0) {

std::string wyjatek = "dzielenie przez zero!"; //przez

zero się nie dzieli

throw wyjatek; //rzucamy wyjątek

}

return a / b;

}

Page 48: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 48

Po instrukcji throw umieszczamy obiekt który chcemy rzucić (u nas jest to std::string). W tym miejscu działaniefunkcji jest natychmiast przerywane i nasz łańcuch znaków wędruje do bloków catch.Pełny program ilustrujący wyjątki:

#include<iostream>

using namespace std;

double Dziel(double, double) throw(std::string);

int main()

{

try

{

Dziel(10, 0);

}

catch(std::string w)

{

cout<<"Wyjatek: "<<w;

}

cin.get();

return 0;

}

double Dziel(double a, double b) //funkcja zwraca iloraz a / b

{

if (b == 0) {std::string wyjatek = "dzielenie przez

zero!";throw wyjatek;}

return a / b;

}

Jak widać, utworzony jest tylko jeden blok catch, a to dlatego że funkcja Dziel rzuca tylko wyjątki typu std::string.Dobrym zwyczajem jest pisanie obok deklaracji funkcji jakie wyjątki może ona rzucać:

void fun(int) throw(std::string)

zapis ten oznacza że funkcja fun może zwrócić wyjątek typu std::string.

Szablony funkcji

Szablon funkcjiSzablon funkcji pozwala stworzyć wiele funkcji różniących się tylko typem argumentów przyjmowanych. Załóżmy,że chcielibyśmy napisać funkcję pisz, której jedynym argumentem byłaby zmienna, którą chcemy wypisać. Aby mócwypisywać wiele typów zmiennych możemy skorzystać z przeładowania (inna nazwa na przeciążenie) funkcji.

void pisz(char a)

{

cout<<a;

}

void pisz(double a)

{

cout<<a;

}

Page 49: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 49

void pisz(int a)

{

cout<<a;

}

Można tak wiele razy przeładowywać funkcję pisz, ale można też wskazać kompilatorowi w jaki sposób możestworzyć nowe funkcje. Są to właśnie szablony.Szablon naszej funkcji wyglądałby tak:

template <typename T> void pisz(T a)

{

cout<<a;

}

Pierwszą linijkę można też złamać po nawiasie '>':

template <typename T>

void pisz(T arg)

Pierwszym słowem jest template czyli szablon. Następnie w ostrych nawiasach umieszcza się słowo typename(zamiennie struct lub class)- które może oznaczać dowolny typ danych. Można także stosować słowo typedef przyzagnieżdżaniu szablonów, tak aby uniknąć nieprecyzyjności. Można także stosować nazwy typów, takich jak int czychar Nazwa użyta po słowach kluczowych zastępuje w deklaracji funkcji typ argumentu.Nie można stosować szablonów dla metod wirtualnych.

Szablony klas

Czym są?Szablony (wzorce) są czymś podobnym do makr, tyle że wykonywane są przez kompilator, a nie przez preprocesor.Cechują je pewne własności, których jednak nie ma preprocesor, np. można tworzyć rekurencyjne wywołania. Alezobaczmy najpierw taką sytuację: chcemy utworzyć klasę o nazwie Punkt o trzech współrzędnych: x, y, z.Potrzebujemy trzy różne implementacje. Jedna ma działać na liczbach typu unsigned int, druga na liczbach typu int,a trzecia na float. Pierwsze, o czym pomyślimy, to napisać coś takiego (jeśli dostrzeżesz błąd, to się nie przejmuj).Mamy przykładową klasę:

// ...

class PunktUInt

{

public:

PunktUInt( unsigned argX, unsigned argY, unsigned argZ )

: x(argX), y(argY), z(argX)

{ }

unsigned x, y, z;

};

// ...

I potem co? kopiujemy i zmieniamy unsigned na int:

Page 50: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 50

// ...

class PunktInt

{

public:

PunktInt( int argX, int argY, int argZ )

: x(argX), y(argY), z(argZ)

{ }

int x, y, z;

};

// ...

Następnie zamieniamy na float:

// ...

class PunktFloat

{

public:

PunktFloat( float argX, float argY, float argZ )

: x(argX), y(argY), z(argX)

{ }

float x, y, z;

};

// ...

Uff! Wreszcie napisaliśmy - pomyślisz sobie. Jednak pisanie wymaga trochę wysiłku. No dobrze, teraz tworzymyfunkcję main:

int main(void)

{

PunktInt A(0,-10,0);

PunktUInt B(0,10,5);

std::cout << "A(" << A.x << "," << A.y << "," << A.z << ")" << std::endl;

std::cout << "B(" << B.x << "," << B.y << "," << B.z << ")" << std::endl;

}

I oto po naszych ciężkich staraniach otrzymujemy dość ciekawy i niespodziewany wynik:

A(0,-10,0)

B(0,10,0)

Ojej! - zapewne krzykniesz. Musiał gdzieś się tu zjawić błąd. Trzeba znaleźć go. Aha, mamy go:

// ...

PunktUInt( unsigned argX, unsigned argY, unsigned argZ )

: x(argX), y(argY), '''z(argX)'''

// ...

Zamiast z(argX) powinno być z(argZ). I trzeba teraz wszystko poprawiać, w tym także resztę funkcji... Dobrze, że totylko tyle. Ale na szczęście C++ daje nam prostszy sposób.

Page 51: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 51

Wyróżniamy różne możliwości szablonów:

Wykorzystywanie szablonówNapiszmy teraz jeszcze raz nasz program. Tym razem z wykorzystaniem szablonów.

// ...

template <typename T>

class Punkt

{

public:

Punkt( T argX, T argY, T argZ )

: x(argX), y(argY), z(argX)

{ }

T x, y, z;

};

// ...

Za pomocą template<typename T> tworzymy nasz szablon. Parametrem jest typ, jaki chcemy użyć, tworzymy gopoprzez <typename T>. Teraz, aby utworzyć nasze punkty możemy zapisać:

Punkt<int> A(0,-10,0);

Punkt<unsigned> A(0,10,5);

Czyli nasz main będzie wyglądał tak:

int main(void)

{

Punkt<int> A(0,-10,0);

Punkt<unsigned> B(0,10,5);

std::cout << "A(" << A.x << "," << A.y << "," << A.z << ")" << std::endl;

std::cout << "B(" << B.x << "," << B.y << "," << B.z << ")" << std::endl;

}

Ale nie podoba nam się ta notacja, bo na przykład program za bardzo zaczyna nam przypominać HTML. Co mamyzrobić? Nic innego, jak tylko przed funkcją main skorzystać z typedef:

typedef Punkt<int> PunktInt;

typedef Punkt<unsigned> PunktUInt;

typedef Punkt<float> PunktFloat;

I tyle. Main zamieni nam się wtedy w pierwotną formę:

int main(void)

{

PunktInt A(0,-10,0);

PunktUInt B(0,10,5);

std::cout << "A(" << A.x << "," << A.y << "," << A.z << ")" << std::endl;

std::cout << "B(" << B.x << "," << B.y << "," << B.z << ")" << std::endl;

Page 52: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 52

}

Powiesz:

No dobra, ale jak teraz uruchomię ten program to nadal mam ten sam, zły wynik.

I masz rację, bo zobaczysz:

A(0,-10,0)

B(0,10,0)

Powód jest ten sam co poprzedni.

Punkt( T argX, T argY, T argZ )

: x(argX), y(argY), '''z(argX)'''

{ }

Musimy zamienić z(argX) na z(argZ) i będzie wszystko w porządku. Tylko tyle. Nie wierzysz? Ale to jest prawda.To zobacz cały nasz program powinien wyglądać w ten sposób:

#include <iostream>

template <typename T>

class Punkt

{

public:

Punkt( T argX, T argY, T argZ )

: x(argX), y(argY), z(argZ)

{ }

T x, y, z;

};

typedef Punkt<int> PunktInt;

typedef Punkt<unsigned> PunktUInt;

typedef Punkt<float> PunktFloat;

int main(void)

{

PunktInt A(0,-10,0);

PunktUInt B(0,10,5);

std::cout << "A(" << A.x << "," << A.y << "," << A.z << ")" << std::endl;

std::cout << "B(" << B.x << "," << B.y << "," << B.z << ")" << std::endl;

}

Page 53: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 53

Szablony z wieloma parametramiSzablon może także posiadać więcej niż jeden parametr. Na przykład chcielibyśmy napisać klasę Para, zawierającądwa elementy pierwszy o nazwie pierwszy, a drugi o nazwie drugi, jednakże nie wiemy jakie mają one mieć typ.Możemy to zrobić w ten sposób:

#include <iostream>

#include <string>

template <typename T1, typename T2>

class Para

{

public:

Para()

{ }

Para( T1 a, T2 b )

: pierwszy(a), drugi(b)

{ }

T1 pierwszy;

T2 drugi;

};

int main(void)

{

// tworzymy nasz obiekt

Para<std::string,int> zmienna("Liczba",10);

std::cout << zmienna.pierwszy << " " << zmienna.drugi << std::endl;

return 0;

}

Za pomocą template<typename T1, typename T2> utworzyliśmy szablon o dwóch parametrach.

Można też liczbySzablonem może być także liczba. Zilustrujmy to przykładem:

#include <iostream>

#include <cstddef>

template <typename T, std::size_t N>

class Tablica

{

public:

T &operator[]( std::size_t i )

{

return tabl[i];

}

Page 54: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 54

private:

T tabl[N];

};

int main(void)

{

Tablica<int,10> A;

for ( int i=0; i<10; ++i )

{

A[i]=100+i;

}

for ( int i=0; i<10; ++i )

{

std::cout << "A[" << i << "]=" << A[i] << std::endl;

}

return 0;

}

W powyższym przykładzie użyto typu std::size_t w nagłówku <cstddef>.

Wskażniki do elementów składowychDodatek A

Biblioteka STL

String

StringŁańcuchy znaków w stylu języka C są częstą przyczyną błędów programu, a na dodatek ich używanie jest dosyćkłopotliwe. Nic wiec dziwnego, że biblioteka standardowa posiada zaimplementowaną uogólnioną klasę napisówzwaną string. Taka klasa daje jednolity, niezależny od systemu i bezpieczny interfejs do manipulowanianapisami.Aby móc korzystać z klasy string należy dołączyć plik nagłówkowy:

#include <string>

Tworzenie nowych obiektów tego typu wygląda następująco:

string napis1;

napis1 = "text";

//inicjowanie łańcucha znaków w miejscu jego tworzenia

//jawne wywołanie konstruktora

string napis2( "text" );

//operator przypisania

string napis3 = "text";

Page 55: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 55

cout << napis1 << endl

<< napis2 << endl

<< napis3 << endl;

string napis4(10,'X');

cout << napis4;

Uwaga:Aby kompilator widział typ string, należy powiadomić go w jakiej przestrzeni nazw ten typ się znajduje:

using std::string;

lub ogólnie:

using namespace std;

Klasa string ma zdefiniowanych wiele operatorów, co ułatwia niektóre działania na napisach. Dla przykładu, dawniejaby skopiować napis z jednej zmiennej do drugiej, trzeba było używać dodatkowej funkcji strcpy(). Wprzypadku klasy string wystarczy operator przypisania '=' :

string a, b;

a = '1';

b = '2';

a = b;

cout << a;

Możemy z powodzeniem używać także operatorów: ==, !=, +, <, > oraz indeksowego []:

string a,b,c;

a = "gosia";

b = "iza";

c = "gosia";

// porównywanie napisów

if (a == c) cout << "a i c sa takie same\n";

if (a != b) cout << "a i b sa rozne\n" ;

// porządek leksykograficzny

cout << "napis a ("<<a<<") poprzedza napis b("<<b<<"): ";

if (a < b) cout << "prawda\n";

else cout << "nieprawda\n";

// łączenie łańcuchów

a = "mal"+ a;

cout << "napis a ("<<a<<") poprzedza napis b("<<b<<"): ";

if (a < b) cout << "prawda\n";

else cout << "nieprawda\n";

Page 56: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 56

// modyfikacja

b[0] = '_';

cout << "zmieniony wyraz b: "<<b<<'\n';

Po czym w konsoli zobaczymy:

a i c sa takie same

a i b sa rozne

napis a (gosia) poprzedza napis b(iza): prawda

napis a (malgosia) poprzedza napis b(iza): nieprawda

zmieniony wyraz b: _za

Jak widać, manipulacje obiektami string są intuicyjne. Oprócz wygodnych w stosowaniu operatorów, klasastring posiada jeszcze więcej metod.

Metoda Opis

empty() Zwraca wartość true jeżeli napis jest pusty.

size(),length() Zwraca ilość znaków w napisie.

at() Zwraca znak o podanym położeniu, tak jak operator [], z tym że ta metoda jest bezpieczniejsza - wyrzuca wyjątek wprzypadku wyjścia poza zakres stringa.

clear() Usuwa wszystkie znaki z napisu.

erase(...) Usuwa wybrane znaki.

find(...) Znajduje podciąg w ciągu, są też bardziej rozbudowane funkcje tego typu.

swap(...) Zamienia miejscami dwa stringi, a staje się b, a b staje się a.

substr(...) Zwraca podciąg na podstawie indeksu początkowego i długości podciągu.

append(...) Dodaje zadany napis na końcu istniejącego ciągu.

c_str() Zwraca napis w stylu języka C (stały wskaźnik typu const char*).

Omówione dotychczas operatory i metody to tylko część dostępnych; wymienione zostały tylko te najczęściejużywane. Teraz przedstawię różnice jakie występują między C a C++ w obsłudze napisów. Po lewej zmienne a i bsą typu (const char *), a po prawej - (std::string).

C C++

strcpy(a,b) a = b

!strcmp(a,b) a == b

strcat(a,b) a += b

strlen(a) a.size(), a.length()

strstr(a,b) a.find(b)

Page 57: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 57

Vector

VectorKlasa vector reprezentuje obudowaną, zwykłą tablicę znaną z C, wyposażoną w kilka dodatkowychmechanizmów. Elementy wektora mogą być dowolnego typu.Obiekt vector ma kilka odmian konstruktorów. Z reguły będziemy tworzyć wektor pusty.

vector <typ_elemetow> nazwa_tablicy;

Możemy też podać wielkość, co wcale nas nie ogranicza do tej wielkości, aby zarezerwować pamięć na kilkaelementów od razu. Może to być zabieg optymalizacyjny.

vector <int> tab(20);

Dodatkowo istnieje konstruktor przyjmujący liczbę elementów oraz wartość, jaką ma mieć każdy z nich.

vector <string> tablica( 20, "przykladowy tekst" );

Ta tablica będzie miała dwadzieścia elementów, z czego wszystkie mają wartość: "przykladowy tekst".

Dodawanie elementówDodawanie elementów umożliwia metoda push_back(). Dodaje ona nowy element na koniec tablicy. Po dodaniunowych elementów możemy się do nich odwoływać indeksowo [] lub metodą at(). Możemy też sprawdzić ileobecnie jest elementów metodą size(), a metoda empty() powie nam czy wektor nie jest pusty.

vector<int> tab;

cin >> n;

for( int i=0; i<n; ++i )

{

int element;

cin >> element;

tab.push_back(element);

}

Jak działa powiększanie się tablicy vector?Poniższy akapit dotyczy szczegółów technicznych, jeśli nie jesteś nimi zainteresowany, możesz go pominąć.Metoda push_back() dodając nowy element do tablicy, dba o to, aby tablica była odpowiedniego rozmiaru. Zakażdym razem, gdy brakuje miejsca, tablica jest powiększana - rezerwowana jest nowa, większa przestrzeń, stareelementy są kopiowane, aby do większej tablicy móc dodać dany element. Z reguły rezerwowana jest pamięć dwarazy większa od poprzedniej. W skrajnej sytuacji, może się zdarzyć, że zarezerwowana pamięć jest prawie dwa razywiększa niż ilość elementów znajdujących się w niej!Jeśli zależy nam na szybkości działania, powinniśmy zarezerwować przy pomocy odpowiedniego konstruktorapamięć na pewną ilość elementów. Przykładowo, jeśli wiemy że w tablicy będzie około 50 elementów, możemykonstruować wektor o wielkości 50, dzięki czemu unikniemy kilku kopiowań całej tablicy podczas dodawaniaelementów. Warto też szukać złotego środka, aby przypadkiem nie marnować pamięci. Na przykład, aby niestworzyć wektora o rozmiarze 100, jeśli dodane do niego będą tylko 2 elementy.Ręczne zarezerwowanie pamięci dla wektora jest możliwe dzięki metodzie reserve(size_t n), natomiastmetoda capacity() zwróci nam wielkość aktualnie zarezerwowanego miejsca.

Page 58: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 58

Ponieważ kopiowanie tablicy jest powolnym procesem, w przypadku tablicy dużych struktur, na przykład klas,warto zastanowić się nad utworzeniem tablicy wskaźników na te struktury.

IteratoryJak wiemy, do poruszania się po elementach zwykłej tablicy takiej jak int a[10] można używać wskaźnika.Podobnie, operacje na obiekcie vector można dokonać używając iteratorów działających podobnie jak wskaźniki.Więcej o iteratorach znajduje się w dalszych rozdziałach.

MetodyLista metod klasy vector. Użyte słowo iterator zastępuje poprawne vector<T>::iterator, podmienionezostało dla zwiększenia czytelności.

Modyfikacja

prototyp opis działania złożoność czasowa

void swap(vector<T>& vec) zamienia zawartości dwóch wektorów miejscami stała

void push_back(const T obj) dodaje na końcu wektora kopię przekazanego argumentu stała, czasem liniowa*

void pop_back() usuwa ostatni element z wektora stała

void clear() usuwa wszystkie elementy z wektora liniowa (destruktory)

void assign(size_t n, const T obj) czyści wektor i wypełnia go n kopiami argumentu obj liniowa (jak clear) + liniowa względemwstawianych elementów

void assign(iterator poczatek,iterator koniec)

czyści wektor i wypełnia go elementami z innego wektora zprzedziału <poczatek;koniec>

jw.

iterator insert(iterator pos, T obj) wstawia element obj przed wskazywaną przez iterator pospozycją i zwraca iterator do dostawionego elementu

liniowa (przenoszenie elementów między posa ostatnim elementem tablicy)

void insert(iterator pos, size_t n,const T obj)

wstawia n kopii argumentu obj przed pozycją wskazywanąprzez iterator pos

jw. + liniowa względem ilości dodanychelementów

void insert(iterator pos, iteratorpoczatek, iterator koniec)

wstawia przed pozycją wskazywaną przez iterator poselementy między iteratorami początek i koniec (włącznie)

jw.**

iterator erase(iterator pos) usuwa element wskazywany przez pos i zwraca iterator donastępnego elementu

liniowa względem ilości elementów zausuwanym elementem

iterator erase(iterator poczatek,iterator koniec)

usuwa elementy z przedziału <poczatek;koniec> i zwracaiterator do elementu za nimi

liniowa względem ilości usuwanychelementów + przenoszenie elementów zakońcem

* może występować kopiowanie wektora, gdy rozmiar jest zbyt mały** w rzadkim przypadku (dla iteratorów najniższego typu w hierarchi: Input lub Output) złożoność bliższakwadratowej (ilość wstawianych elementów razy ilość elementów od pozycji do końca tablicy)

Dostęp

Page 59: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 59

prototyp opis działania

T& front() zwraca referencję do pierwszego elementu wektora

T& back() zwraca referencję do ostatniego elementu wektora

iterator begin() zwraca iterator do pierwszego elementu wektora (często mylone z front())

iterator end() zwraca iterator ustawiony za ostatnim elementem wektora

iterator rbegin() zwraca odwrócony iterator do pierwszego elementu

iterator rend() zwraca odwrócony iterator do ostatniego elementu

Inne

prototyp opis działania

size_t size() zwraca obecną ilość elementów wektora.

size_t capacity() zwraca ilość elementów, którą wektor jest w stanie pomieścić przed przeniesieniem go do większego obszaru pamięci.

size_t max_size() zwraca ilość elementów, którą maksymalnie może pomieścić wektor

bool empty() zwraca true jeśli wektor nie przechowuje żadnych zmiennych

void reserve(size_t n) rezerwuje pamięć na n elementów, co zapobiega przenoszeniu wektora w pamięci przed osiągnięciem tej liczby

void resize(size_t n, Tobj)

zmienia rozmiar wektora do n; jeśli jest większy od obecnego, dodawane są nowe elementy będące kopiami obj

void resize(size_t n) zmienia rozmiar wektora do n; jeśli jest większy od obecnego, dodawane są nowe elementy o przypadkowychwartościach

Set

OpisZbiory są jednym z kontenerów biblioteki STL, których struktura oparta jest na drzewach. Elementy które są w nichprzechowywane są posortowane, według pewnego klucza.Zarówno typ wartosci tych elementów jak i typ wartości klucza są takie same. Drzewiasta struktura zapewniaszybkie wyszukiwanie, jednak są z tym związane także pewne mankamenty, mianowicie modyfikacja elementu jestmożliwa tylko w taki sposób, że kasujemy stary element, a następnie wstawiamy w to miejsce nowy.Korzystając z terminologii STL-a zbiory sa tzw. kontenerami asocjacyjnymi (o zmiennej długości, pozwalającymi naoperowanie elementami przy użyciu kluczy).

Prosty przykładOpis użytych tu iteratorów znajduje się w rozdziale Iteratory.

#include <iostream>

#include <string>

#include <set>

using namespace std;

int main()

{

Page 60: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 60

set<string> mapa;

mapa.insert("Lublin");

mapa.insert("Lódź");

mapa.insert("Warszawa");

mapa.insert("Kraków");

set<string>::iterator result, it;

// szuka elementu "Warszawa"

result = mapa.find("Warszawa");

if( result!=mapa.end() )

cout << "Znalazłem! " << *result<< '\n';

// wyświetlenie zawartości

for( it=mapa.begin(); it!=mapa.end(); ++it)

cout << *it<< '\n';

return 0;

}

Map

OpisMapa to posortowany kontener asocjacyjny, czyli zbiornik o zmiennej długości gromadzący dane, które możnadodawać i usuwać. Nie można jednak dodawać danych na konkretną pozycje, ponieważ kolejność ustalana jest wgdanej klucz. Mapa jest również parowym zbiornikiem asocjacyjnym, czyli jej elementami są pary wartości klucz idana. Pierwszej wartości key_type, czyli klucza mapy, nie można zmieniać, natomiast druga wartość danej jestprzypisywalna (np.(*i).second=2). Mapa jest w końcu unikalnym kontenerem asocjacyjnym, co oznacza, że każdedwa elementy mają różny klucz.Mapa zdefiniowana jest w standardowym nagłówku map oraz w niestandardowym, wstecznie kompatybilnymnagłówku map.h.

Przykład#include<iostream>

#include<map>

using namespace std;

int main()

{

map<const int, string> tydzien;

tydzien[1] = "niedziela";

tydzien[2] = "poniedzialek";

tydzien[3] = "wtorek";

tydzien[4] = "sroda";

tydzien[5] = "czwartek";

Page 61: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 61

tydzien[6] = "piatek";

tydzien[7] = "sobota";

cout << "trzeci dzien tygodnia: " << tydzien[3] << '\n';

map<const int, string>::iterator cur;

// zwrocenie elementu o kluczu 3

cur = tydzien.find(3);

// elementy o kluczach większych i mniejszych

map<const int, string>::iterator prev = cur;

map<const int, string>::iterator next = cur;

++next;

--prev;

cout << "Wczesniejszy: " << prev->second << '\n';

cout << "Nastepny: " << next->second << '\n';

}

Algorytmy w STL

WstępCóż znaczą biblioteki bez <algorithm>? Na pewno mniej, ponieważ każde modyfikacje na wektorach czy ciągachznaków są bardziej uciążliwe i wymagają od użytkownika dodatkowego wkładu pracy na napisanie algorytmu dowykonania określonego problemu. Weźmy pod uwagę przykładowo problem sortowania. Poniżej przedstawiona jestfunkcja sortująca bąbelkowo n-elementową tablicę 1-wymiarową.

void sortowanie_babelkowe(int tab[], int n)

{

for (int j=n-1; j>0; --j)

for (int i=0; i<j; ++i)

if (tab[i]>tab[i+1])

{

int temp=tab[i];

tab[i]=tab[i+1];

tab[i+1]=temp;

}

}

Kod nie jest długi, ale wygodniej jest napisać:

sort(tab,tab+n);

Page 62: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 62

Lista funkcji zawartych w bibliotece <algorithm>

•• accumulate() •• lexicographical_compare() •• replace_copy()•• adjacent_difference() •• lower_bound() •• replace_copy_if()•• adjacent_find() •• make_heap() •• replace_if()•• binary_search() •• max() •• reverse()•• copy() •• max_element() •• reverse_copy()•• copy_backward() •• merge() •• rotate()•• count() •• min() •• rotate_copy()•• count_if() •• min_element() •• search()•• equal() •• mismatch() •• search_n()•• equal_range() •• next_permutation() •• set_difference()•• fill() •• nth_element() •• set_intersection()•• fill_n() •• partial_sort() •• set_symmetric_difference()•• find() •• partial_sort_copy() •• set_union()•• find_end() •• partial_sum() •• sort()•• find_first_of() •• partition() •• sort_heap()•• find_if() •• pop_heap() •• stable_partition()•• for_each() •• prev_permutation() •• stable_sort()•• generate() •• push_heap() •• swap()•• generate_n() •• random_shuffle() •• swap_ranges()•• includes() •• remove() •• transform()•• inner_product() •• remove_copy() •• unique()•• inplace_merge() •• remove_copy_if() •• unique_copy()•• is_heap() •• remove_if() •• upper_bound()•• iter_swap() •• replace()

Lista tematyczna

Operacje niemodyfikujące• for_each — wykonuje operację na każdym elemencie ciągu• count — liczy ilość wystąpień danej wartości w ciągu• count_if — zlicza w ciągu ilość wystąpień wartości spełniających warunek• equal — określa czy dwa zbiory elementów są takie same

Operacje niemodyfikujące - szukanie• mismatch — znajduje pierwszą parę różnych elementów dwóch ciągów• find — znajduje pierwsze wystąpienie wartości w ciągu• find_if — znajduje w ciągu pierwsze wystąpienie wartości spełniającej warunek• find_end — znajduje ostatnie wystąpienie ciągu jako podciągu• find_first_of — znajduje jakikolwiek element ze zbioru w danym ciągu• adjacent_find — znajduje sąsiadującą parę wartości• search — znajduje pierwsze wystąpienie ciągu jako podciągu• search_n — znajduje pierwsze wystąpienie ciągu n-tu wartości w ciągu

Page 63: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 63

Operacje modyfikujące• copy — kopiuje elementy jednego ciągu do drugiego• copy_backward — kopiuje ciąg do drugiego ciągu, wstawiając elementy na jego koniec• fill — zastępuje elementy ciągu podaną wartością• fill_n — zastępuje n elementów ciągu podaną wartością• generate — zastępuje elementy ciągu wartościami będącymi wynikiem funkcji• generate_n — zastępuje n elementów ciągu wartościami będącymi wynikiem funkcji• transform — wykonuje podaną funkcję dla argumentów ze zbioru i zapisuje wyniki w nowym ciągu• remove — usuwa elementy o podanej wartości• remove_if — usuwa elementy spełniające warunek• remove_copy — kopiuje ciąg, usuwając elementy o podanej wartości• remove_copy_if — kopiuje ciąg, usuwając elementy spełniające warunek• replace — zastępuje elementy o danej wartości inną wartością• replace_if — zastępuje elementy spełniające warunek• replace_copy — kopiuje ciąg, zastępując elementy o danej wartości inną wartością• replace_copy_if — kopiuje ciąg, zastępując elementy spełniające warunek

Operacje zmieniające kolejność• partition — umieszcza elementy spełniające warunek przed tymi które go nie spełniają• stable_partition — umieszcza elementy spełniające warunek przed tymi które go nie spełniają, zachowuje

wzajemną kolejność• random_shuffle — w losowy sposób zmienia kolejność elementów ciągu• reverse — odwraca kolejność elementów w ciągu• reverse_copy — kopiuje ciąg, odwracając kolejność elementów• rotate — dokonuje rotacji elementów ciągu• rotate_copy — kopiuje ciąg, przesuwając elementy (rotacja)• unique — usuwa powtórzenia, w taki sposób że wśród sąsiadujących elementów nie ma dwóch takich samych• unique_copy — kopiuje ciąg, w taki sposób że wśród sąsiadujących elementów nie ma dwóch takich samych• swap — zamienia ze sobą dwa elementy• swap_ranges — zamienia ze sobą dwa zbiory elementów• iter_swap — zamienia ze sobą dwa elementy wskazywane przez iteratory

Operacje sortujące• sort — sortuje ciąg rosnąco• partial_sort — sortuje pierwsze N najmniejszych elementów w ciągu• partial_sort_copy — tworzy kopię N najmniejszych elementów ciągu• stable_sort — sortuje ciąg zachowując wzajemną kolejność dla równych elementów• nth_element — ciąg jest podzielony na dwie nieposortowane części elementów mniejszych i większych od

wybranego elementu

Page 64: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 64

Operacje wyszukiwania binarnegoOperacje na posortowanych ciągach

• lower_bound — zwraca iterator do pierwszego elementu równego lub większego od podanego• upper_bound — zwraca iterator do pierwszego elementu większego od podanego• binary_search — stwierdza czy element występuje w ciągu• equal_range — zwraca parę określającą przedział wewnątrz którego występuje dana wartość (lub ich ciąg).

Operacje na zbiorzeOperacje na posortowanych ciągach

• merge — łączy dwa posortowane ciągi• inplace_merge — łączy dwie posortowane części ciągu• includes — zwraca prawdę jeśli pierwszy ciąg jest podciągiem drugiego• set_difference — tworzy różnicę dwóch zbiorów• set_intersection — tworzy przecięcie dwóch zbiorów• set_symmetric_difference — tworzy zbiór złożony z elementów występujących w tylko jednym z dwóch ciągów• set_union — tworzy sumę zbiorów

Operacje na kopcu• is_heap — zwraca prawdę jeśli ciąg tworzy kopiec• make_heap — przekształca ciąg elementów tak aby tworzyły kopiec• push_heap — dodaje element do kopca• pop_heap — usuwa element ze szczytu kopca• sort_heap — przekształca ciąg o strukturze kopca w ciąg posortowany

Operacje min max• max — zwraca większy z dwoch elementów• max_element — zwraca największy z elementów w ciągu• min — zwraca mniejszy z elementów• min_element — zwraca najmniejszy z elementów w ciągu• lexicographical_compare — sprawdza czy jeden ciąg poprzedza leksykograficznie drugi ciąg• next_permutation — przekształca ciąg elementów w leksykograficznie następną permutację• prev_permutation — przekształca ciąg elementów w leksykograficznie poprzedzającą permutację

Operacje numeryczneZdefiniowane w nagłówku <numeric>

• accumulate — sumuje ciąg elementów• inner_product — oblicza iloczyn skalarny na elementach dwóch ciągów• adjacent_difference — oblicza różnice pomiędzy sąsiadującymi elementami w ciągu• partial_sum — oblicza sumy częściowe ciągu elementów

Page 65: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 65

Operacje niemodyfikującePoniższe przykłady wymagają dołączenia bibliotek i przestrzeni nazw

#include <iostream>

#include <vector>

#include <string>

using namespace std;

for_each()for_each( iterator początek, iterator koniec, funkcja )

Działaniewykonuje operację na każdym elemencie ciągu.

Przykładponiższy program wywołuje dla każdego elementu dodanego do vectora funkcję echo.

void echo(short num)

{

cout << num << endl;

}

int main()

{

vector<short> vect;

vect.push_back(5);

vect.push_back(4);

vect.push_back(3);

for_each(vect.begin(), vect.end(), echo);

return 0;

}

Na wyjściu pojawi się:

5

4

3

Page 66: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 66

count()count( iterator początek, iterator koniec, wartość )

Działanieliczy ilość wystąpień danej wartości w ciągu.

Przykładprogram przekształca tablicę liczb w wektor i zlicza ilość znajdujących się w nim dwójek.

int main()

{

int tablica[] = { 2, 5, 7, 9, 2, 9, 2 };

vector<int> v(tablica, tablica+7);

cout << "Ilosc dwojek w tablicy: " << count( v.begin(), v.end(), 2 );

return 0;

}

count_if()count_if( iterator początek, iterator koniec, funkcja f )

Działanieliczy w ciągu ilość wystąpień wartości spełniających warunek

Przykładprogram przekształca tablicę liczb w wektor i zlicza ilość znajdujących się w nim liczb parzystych.

bool czyParzysta(int n){

return (n%2 ? false : true);

}

int main(){

int tablica[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

vector<int> v(tablica, tablica+10);

cout << "Ilosc parzystych: " << count_if(v.begin(), v.end(), czyParzysta);

return 0;

}

equal()bool equal( iterator początek, iterator koniec, iterator początek_drugiego )

bool equal( iterator początek, iterator koniec, iterator początek_drugiego, funkcja_porownująca )

Działanieporównywany jest pierwszy zakres elementów (początek, koniec) z drugim zakresem (zaczynającym się wpoczątek_drugiego).

Przykładprogram porównuje łańcuch str z napis oraz z napis2. Porównanie ogranicza się do 2 znaków (długość str).

int main()

{

Page 67: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 67

string str= "bc";

string napis= "abcde";

string napis2= "bcde";

if( equal(str.begin(), str.end(), napis.begin()) )

cout << "Takie same\n";

else

cout << "Rozne\n";

if( equal(str.begin(), str.end(), napis2.begin()) )

cout << "Takie same";

else

cout << "Rozne";

return 0;

}

Wynikiem jest kolejno Rozne oraz Takie same.

Operacje niemodyfikujące - szukanie

mismatch()pair<> mismatch( iterator1 początek, iterator1 koniec, iterator2 początek_drugi )

pair<> mismatch( iterator1 początek, iterator1 koniec, iterator2 początek_drugi, funkcja )

znajduje pierwsze różne elementy dwóch ciągówWartość zwracana

para znalezionych różniących się wartości, typu pair<iterator1, iterator2>

Działanieporównuje kolejne elementy w dwóch zbiory (pierwszy określony przez początek, koniec oraz drugizaczynający się w początek_drugi). Zwraca pierwsze wystąpienie dwóch różnych elementów, lub parę<koniec, odpowiedni_koniec_drugi> jeśli nie znajdzie.

find()iterator find( iterator początek, iterator koniec, wartość )

Działanieznajduje pierwsze wystąpienie wartości w ciągu i zwraca iterator do niej, lub jeśli nie zostanie znaleziona,zwraca iterator koniec.

Przykładprogram tworzy tablicę liczb 0, 10, 20, ..., 90 i sprawdza, czy znajdują się w nim liczby 30 i 33.

int main()

{

vector<int> zbior;

vector<int>::iterator wynik1, wynik2;

Page 68: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 68

for( int i = 0; i < 10; ++i )

zbior.push_back(i*10);

wynik1 = find(zbior.begin(), zbior.end(), 30);

wynik2 = find(zbior.begin(), zbior.end(), 33);

if( wynik1 != zbior.end() )

cout << "Znaleziono 30.";

if( wynik2 != zbior.end() )

cout << "Znaleziono 33.";

}

Wynikiem jest: Znaleziono 30.

find_if()iterator find_if( iterator początek, iterator koniec, funkcja )

Działanieznajduje w ciągu pierwsze wystąpienie wartości spełniającej warunek

find_end()iterator find_end( iterator początek, iterator koniec, iterator początek_szukany, iterator koniec_szukany )

iterator find_end( iterator początek, iterator koniec, iterator początek_szukany, iterator koniec_szukany, funkcja )

Działanieznajduje ostatnie wystąpienie ciągu (początek_szukany, koniec_szukany) jako podciągu w przedziale(początek, koniec). Można dostarczyć własną funkcję do porównywania elementów.

find_first_of()iterator find_first_of( iterator początek, iterator koniec, iterator początek_zbiór, iterator koniec_zbiór)

iterator find_first_of( iterator początek, iterator koniec, iterator początek_zbiór, iterator koniec_zbiór, funkcja )

Działanieznajduje choć jeden element ze zbioru (początek_zbiór, koniec_zbiór) w podanym ciągu (początek, koniec).Można dostarczyć własną funkcję do porównywania elementów.

adjacent_find()iterator adjacent_find( iterator początek, iterator koniec )

iterator adjacent_find( iterator początek, iterator koniec, funkcja )

Działanieporównuje kolejne wartości w obu ciągach, aż znajdzie parę tych samych - zwraca wówczas iterator do tejwartości, lub koniec jeśli nie ma pary identycznych wartości. Można dostarczyć własną funkcję doporównywania elementów.

Page 69: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 69

search()iterator search( iterator początek, iterator koniec, iterator początek_szukany, iterator koniec_szukany )

iterator search( iterator początek, iterator koniec, iterator początek_szukany, iterator koniec_szukany, funkcja )

Działanieznajduje pierwsze wystąpienie ciągu (początek_szukany, koniec_szukany) jako podciągu w przedziale(początek, koniec). Można dostarczyć własną funkcję do porównywania elementów.

search_n()iterator search_n( iterator początek, iterator koniec, n, wartość )

iterator search_n( iterator początek, iterator koniec, n, wartość, funkcja )

Działanieznajduje pierwsze wystąpienie ciągu złożonego z n-tu wartości jako podciągu w przedziale (początek, koniec).Można dostarczyć własną funkcję do porównywania elementów.

Operacje modyfikujące

copy()copy( iterator początek, iterator koniec, iterator początek_kopia )

Działaniekopiuje ciąg (początek, koniec) do drugiego ciągu

Przykładtworzy ciąg 10 liczb, po czym kopiuje je do drugiego (pustego) ciągu

int main()

{

vector<int> ciag;

for (int i = 0; i < 10; i++)

ciag.push_back(i);

vector<int> kopia(10);

copy( ciag.begin(), ciag.end(), kopia.begin() );

}

Page 70: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 70

copy_backward()copy_backward( iterator początek, iterator koniec, iterator koniec_kopia )

Działaniekopiuje ciąg (początek, koniec) do drugiego ciągu, tak aby kończyły się razem z jego końcem

Przykładtworzy ciąg 10 liczb, po czym kopiuje je do drugiego ciągu o długości 13 (elementy będą przesunięte, abywyrównać do końca)

int main()

{

vector<int> ciag;

for( int i = 0; i < 10; i++ )

ciag.push_back(i);

vector<int> kopia(13);

copy_backward( ciag.begin(), ciag.end(), kopia.end() );

for( int i = 0; i < kopia.size(); i++)

cout << kopia[i] << " ";

}

Wynikiem będzie 0 0 0 0 1 2 3 4 5 6 7 8 9.

fill()fill( iterator początek, iterator koniec, wartość )

Działaniezastępuje elementy ciągu podaną wartością.

fill_n()fill_n( iterator początek, n, wartość )

Działaniezastępuje n pierwszychh elementów ciągu podaną wartością.

Page 71: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 71

generate()generate( iterator początek, iterator koniec, funkcja )

Działaniezastępuje elementy ciągu wartościami będącymi wynikiem funkcji.

generate_n()generate_n( iterator początek, n, funkcja )

Działaniezastępuje n elementów ciągu wartościami będącymi wynikiem funkcji

transform()transform( iterator początek, iterator koniec, iterator nowy_początek, funkcja )

transform( iterator początek, iterator koniec, iterator początek_drugiego, iterator nowy_początek, funkcja_dwuargumentowa )

Działaniewykonuje podaną funkcję dla argumentów ze zbioru (początek, koniec) i zapisuje wyniki do zbioruzaczynającego się w nowy_początek. Druga wersja wykonuje funkcję dla pary argumentów, korzystając zdrugiego zbioru (wskazywanego przez początek_drugiego).

Przykładprogram wykonuje funkcję sqrt dla każdego z 5 elementów ciągu, zapisując wyniki w nowym ciągu.Niezbędne było określenie którego przeładowania funkcji sqrt uzywamy.

int main()

{

double tablica[5] = {2, 3, 9, 16, 25};

vector<double> v(tablica, tablica+5);

vector<double> wyniki(5);

transform(v.begin(), v.end(), wyniki.begin(), (double (*) (double))

sqrt );

for( int i=0; i<5; i++ )

cout << wyniki[i] << '\n';

}

Page 72: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 72

remove()Działanie

usuwa elementy o podanej wartości

remove_if()Działanie

usuwa elementy spełniające warunek

remove_copy()Działanie

kopiuje ciąg, usuwając elementy o podanej wartości

remove_copy_if()Działanie

kopiuje ciąg, usuwając elementy spełniające warunek

replace()Działanie

zastępuje elementy o danej wartości inną wartością

replace_if()Działanie

zastępuje elementy spełniające warunek

replace_copy()Działanie

kopiuje ciąg, zastępując elementy o danej wartości inną wartością

replace_copy_if()Działanie

kopiuje ciąg, zastępując elementy spełniające warunek

Operacje zmieniające kolejność

partition()partition( iterator początek, iterator koniec, funkcja )

Działanieumieszcza elementy spełniające warunek przed tymi które go nie spełniają

Page 73: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 73

stable_partition()stable_partition( iterator początek, iterator koniec, funkcja )

Działanieumieszcza elementy spełniające warunek przed tymi które go nie spełniają, zachowuje wzajemną kolejność

random_shuffle()random_shuffle( iterator początek, iterator koniec )

random_shuffle( iterator początek, iterator koniec, generator_liczb_pseudolosowych )

Działaniew losowy sposób zmienia kolejność elementów ciągu

reverse()reverse( iterator początek, iterator koniec )

Działanieodwraca kolejność elementów w ciągu

reverse_copy()reverse_copy( iterator początek, iterator koniec, iterator początek_kopia )

Działaniekopiuje ciąg do drugiego ciągu, odwracając kolejność elementów

rotate()rotate( iterator początek, iterator nowy_początek, iterator koniec )

Działanieprzesuwac elementy w taki sposób aby pierwszym elementem był nowy_początek; element go poprzedzającystaje się ostatnim

rotate_copy()rotate_copy( iterator początek, iterator nowy_początek, iterator koniec, iterator początek_kopia )

Działaniekopiuje ciąg do drugiego ciągu, przesuwając elementy w taki sposób aby pierwszym elementem byłnowy_początek; element go poprzedzający staje się ostatnim

Page 74: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 74

unique()Działanie

usuwa powtórzenia, w taki sposób że wśród sąsiadujących elementów nie ma dwóch takich samych

unique_copy()iterator unique_copy( iterator początek, iterator koniec, iterator początek_kopia )

iterator unique_copy( iterator początek, iterator koniec, iterator początek_kopia, funkcja )

Działaniekopiuje ciąg do drugiego ciągu, w taki sposób że wśród sąsiadujących elementów nie ma dwóch takichsamych

swap()swap( element1, element2 )

Działaniezamienia ze sobą dwa elementy.

swap_ranges()swap_ranges( iterator początek, iterator koniec, iterator początek_drugiego )

Działaniezamienia ze sobą dwa zbiory elementów: zbiór (początek, koniec) z drugim (o podanym początku).

iter_swap()iter_swap( iterator element1, iterator element2 )

Działaniezamienia ze sobą dwa elementy wskazywane przez iteratory.

Operacje sortujące

sort()void sort( RandomAccessIterator start, RandomAccessIterator end ) void sort( RandomAccessIterator start,RandomAccessIterator end, Compare cmp )Działanie

sortuje ciąg rosnącoJak używać:

#include<algorithm>

...

sort( iterator start, iterator koniec );

//albo

Page 75: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 75

sort( iterator start, iterator koniec, cmp ); //cmp - funkcja porównująca

...

W pierwszym przypadku algorytm sort() ustawia elementy w zakresie [start,koniec) w porządku niemalejącym. Gdywywołujemy sortowanie z trzema parametrami to sortowanie odbywa się względem funkcji porównującej, którądefiniujemy.Przykładowe kody źródłowe

vector<int> v;

v.push_back( 23 );

v.push_back( -1 );

v.push_back( 9999 );

v.push_back( 0 );

v.push_back( 4 );

cout << "Przed sortowaniem: ";

for( int i = 0; i < v.size(); ++i ) {

cout << v[i] << " ";

}

cout << endl;

sort( v.begin(), v.end() );

cout << "Po sortowaniu: ";

for( int i = 0; i < v.size(); ++i ) {

cout << v[i] << " ";

}

cout << endl;

Efektem działania tego programu będzie:

Przed sortowaniem: 23 -1 9999 0 4

Po sortowaniu: -1 0 4 23 9999

Inny przykład, tym razem z funkcją definiowaną przez programistę:

bool cmp( int a, int b ) {

return a > b;

}

...

vector<int> v;

for( int i = 0; i < 10; ++i ) {

v.push_back(i);

}

cout << "Przed: ";

for( int i = 0; i < 10; ++i ) {

Page 76: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 76

cout << v[i] << " ";

}

cout << endl;

sort( v.begin(), v.end(), cmp );

cout << "Po: ";

for( int i = 0; i < 10; ++i ) {

cout << v[i] << " ";

}

cout << endl;

Wyniki działania takiego programu będą następujące:

Przed: 0 1 2 3 4 5 6 7 8 9

Po: 9 8 7 6 5 4 3 2 1 0

partial_sort()void partial_sort( random_access_iterator start, random_access_iterator middle, random_access_iterator end )

void partial_sort( random_access_iterator start, random_access_iterator middle, random_access_iterator end, StrictWeakOrdering cmp )

Działaniesortuje pierwsze N najmniejszych elementów w ciągu

partial_sort_copy()random_access_iterator partial_sort_copy( input_iterator start,

input_iterator end, random_access_iterator result_start,

random_access_iterator result_end )

random_access_iterator partial_sort_copy( input_iterator start,

input_iterator end, random_access_iterator result_start,

random_access_iterator result_end, StrictWeakOrdering cmp )

Działanietworzy kopię N najmniejszych elementów ciągu

stable_sort()void stable_sort( random_access_iterator start, random_access_iterator end )

void stable_sort( random_access_iterator start, random_access_iterator end, StrictWeakOrdering cmp )

Działaniesortuje ciąg zachowując wzajemną kolejność dla równych elementów

nth_element()void nth_element( random_access_iterator start, random_access_iterator nth, random_access_iterator end )

void nth_element( random_access_iterator start, random_access_iterator nth, random_access_iterator end, StrictWeakOrdering cmp )

Działanie

Page 77: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 77

ciąg jest podzielony na nieposortowane grupy elementów mniejszych i większych od wybranego elementu Nth(odpowiednio po lewej i prawej jego stronie)

Inne klasy w STL

Dodatek B

Przykłady programów w C++ // Arithmetic - proste działania matematyczne

#include <iostream>

int main()

{

using namespace std;

int Liczba1 = 0;

int Liczba2 = 0;

int Wynik = 0;

// koniec linii to '\n'

cout << "Podaj pierwsza liczbe calkowita\n";

cin >> Liczba1;

// czasem mozna linie konczyc uzywajac ::std::endl

// ale nalezy pamietac, ze endl oczyszcza bufor

// co wiaze sie ze spadkiem wydajnosci

// kiedy wykonywanych jest duzo takich operacji

cout << "Podaj druga liczbe calkowita" << endl;

cin >> Liczba2;

Wynik = Liczba1 + Liczba2;

cout << '\n' << Liczba1 << '+' << Liczba2 << '=' << Wynik << '\n';

return 0;

}

Page 78: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 78

Ćwiczenia

Ćwiczenie 1Napisać program, który czyta ze strumienia standardowego wejścia i zmienia małe litery na wielkie i odwrotnie (niebierzemy pod uwagę polskich znaków), wypisując wynik do strumienia standardowego wyjścia. Następniezmodyfikować go tak, aby pracował na plikach: wejściowym oraz wyjściowym, podanych jako parametry programu.

Ćwiczenie 2Mamy tablice kwadratowa, której wymiarem jest liczba nieparzysta. Napisz program, który wypełni ja zerami ijedynkami w następujący sposób:

0 0 1 0 0

0 1 0 1 0

1 0 0 0 1

0 1 0 1 0

0 0 1 0 0

Ćwiczenie 3Dany jest plik:

+-----------+----+-----------+--------------+

|PESEL |Płeć|Imię |Nazwisko |

+-----------+----+-----------+--------------+

45679815845 M Tomasz Buczakowski

48491848438 M Andzej Krzemień

81871681861 K Karolina Cubisz

78168181348 M Aleksander Buczykowski

48618481389 M Jerzy Apostoł

56884864684 K Magdalena Koczkowska

84184864889 M Patryk Rutkowski

93818984869 M Daniel Dworkowski

48498386181 K Karina Puszkowski

78681864864 M Jakub Buczakowski

48648848646 M Stefan Buczakowski

84681846844 K Anna Stepaniuk

45784698943 M Grzegorz Warszawski

1.1. Wypisać wszystkie nazwiska kończące się na "ski" w porządku alfabetycznym, bez powtórzeń.2.2. Wypisać w odwrotnym porządku alfabetycznym osobno imiona żeńskie i męskie.

Page 79: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 79

Ćwiczenie 4Napisz funkcje wypisującą liczby pierwsze (lub złożone) z podanego zakresu liczb.

Podpowiedź: Należy użyć algorytmu sita Eratostenesa.

Ćwiczenie 5Mamy taki oto plik źrodłowy: #include <iostream>

using namespace std;

class wydatkiMiesieczne

{

public:

//w tym miejscu wpisz brakujace pola klasy

private:

int cenaChleb; //

int cenaMleko; //ceny produktow (przypisz je w konstruktorze)

int cenaGazeta; //

bool zakupChleba; //

bool zakupMleka; //zmienne true/false

bool zakupGazety; //

int pensja; //pensja obiektu (człowieka)

int sumaWydatkowMiesiecznych; //zmienna ktora sumuje wydatki

bool czyWystarczaNaMiesiac; //zmienna przechowuje true jesli

pensja jest wieksza niz wydatki; jesli jest odwrotnie - false

};

int main(int argc, char** argv)

{

wydatkiMiesieczne Marian;

Marian.zarobek(200);

Marian.czyKupujeChleb(true);

Marian.czyKupujeMleko(true);

Marian.czyKupujeGazete(true);

Marian.obliczanieWydatkowMiesiecznych();

cout << "Pensja Mariana " << (Marian.czyWystarczaPieniedzyNaWydatkiMiesieczne() ? "jest wystarczajaca" : "nie jest wystarczajaca");

cout << " na pokrycie kosztow codziennych zakupow" << endl;

wydatkiMiesieczne* Jadwiga = new wydatkiMiesieczne;

Jadwiga->zarobek(12000);

Jadwiga->czyKupujeChleb(true);

Jadwiga->czyKupujeMleko(true);

Page 80: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 80

Jadwiga->czyKupujeGazete(false);

Jadwiga->obliczanieWydatkowMiesiecznych();

cout << "Pensja Jadwigi " << (Jadwiga->czyWystarczaPieniedzyNaWydatkiMiesieczne() ? "jest

wystarczajaca" : "nie jest wystarczajaca");

cout << " na pokrycie kosztow codziennych zakupow" << endl;

delete Jadwiga;

getchar();

return 0;

}

Program ten oblicza czy pensja, ktorą dostaje człowiek zdefiniowany jako obiekt klasy wydatkiMiesieczne,jest w stanie pokryc uproszczone wydatki miesięczne. Jednak program nie jest kompletny. Uzupełnij brakujące polaklasy w sekcji public stosując się do poniższych instrukcji:•• Użyj konstruktora do przypisania wartosci polom danych:

+-----------+-----------------+

|nazwa pola |wartosc ktora ma |

| danych |zostac przypisana|

+-----------+-----------------+

| cenaChleb | 2 |

| cenaMleko | 3 |

| cenaGazeta| 2 |

| | |

| sumaWydat-| |

| kowMiesie-| 0 |

| cznych | (opcjonalnie) |

+-----------+-----------------+

• Pamiętaj że zmienne cenaChleb, cenaMleko i cenaGazeta reprezentują cene za jedną sztukę danegoproduktu, a my bedziemy potrzebować ceny za zakup tych towarów przez cały miesiąc (przyjmijmy ze miesiącma 30 dni).

• Nie tworz innych funkcji w sekcji public niż te, które zostały wywołane w funkcji main().• Funkcja zarobek() ma pobierac liczbe reprezentującą zarobek danej osoby (w zł) i wpisywac tą wartość w

zmienną pensja.• Funkcje z przedrostkami czyKupuje- mają przypisywac wartość (true - jeśli kupuje dany produkt; false - jeśli nie)

do swoich odpowiednikow z sekcji private (np. funkcja czyKupujeMleko() przypisze wartość zmiennejzakupMleka).

• Funkcja obliczanieWydatkowMiesiecznych() ma obliczyć kwotę jaka bedzie wydana przez miesiąckupowania ustalonych przez obiekt produktów i przypisać wynik zmiennej sumaWydatkowMiesiecznych.

• Funkcja czyWystarczaPieniedzyNaWydatkiMiesieczne() ma obliczyc czy pensja danego obiektujest wystarczająca na pokrycie kosztów zakupów, przekazać wynik true, albo false do zmiennejczyWystarczaNaMiesiac i zwrocić go.

Page 81: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 81

Różnice między C a C++

"Hello World" program w C i C++.

Komentarze

W ANSI C (C89) nie jest dozwolone używanie komentarzyzaczynających się od //. Zostały jednak dodane w standardzie C99.

Stałe

Stałe w C++ (obiekty zadeklarowane ze słowem const) są stałe wpełnym tego słowa znaczeniu. Np. stałe typów całkowitych mogą byćstosowane tam, gdzie wymaga się stałych wyrażeń (tzn. jako etykietycase, jako rozmiar tablic itp.). W C już nie i tam do takich stałychtrzeba stosować dyrektywę preprocesora #define. W C stała jestdokładnie tym samym co zmienna, z tym tylko zastrzeżeniem, że niemożna jej jawnie modyfikować (ale można zmodyfikować zawartośćwskaźnika do adresu stałej, czyli de facto zmodyfikować stałą).

Konsekwencją tego po części jest fakt, że globalnie deklarowane stałe w języku C mają to samo wiązanie (ang.linkage) co zmienne, czyli zewnętrzne. W języku C++ stałe mają domyślnie wiązanie lokalne i aby były onezewnętrzne (dzielone między jednostkami kompilacji), muszą być zadeklarowane razem z inicjalizacją i słowemextern.

Zmienne•• możliwość deklarowania zmiennych np. w instrukcji sterującej petli for•• możliwość mieszania instrukcji i deklaracji zmiennych w jednym bloku kodu (w ANSI C zmienne muszą być

deklarowane przed pierwszą instrukcją)

Wiązanie (ang. linkage) i obiekty niejawneW języku C wiązanie symboli z obiektami, które są przez nie oznaczane, czyli odwoływanie się w jednychjednostkach kompilacji do obiektów lub funkcji z innych jednostek kompilacji, jest opisane luźniejszymi regułami,niż w C++.W języku C obowiązuje "słabe wiązanie" (ang. vague linkage), przy czym nie istnieją w tym języku żadne obiektyniejawne. To oznacza, że funkcja lub zmienna globalna o określonej nazwie może wystąpić dowolną ilość razy wcałym zbiorze kompilacji (zbiorze jednostek kompilacji składających się na jeden plik wykonywalny lub bibliotekędynamiczną). Podczas procesu wiązania wybierany jest w takim wypadku "pierwszy lepszy" ze wszystkich takichobiektów. Język C pozwala również na wielokrotne definicje zmiennej globalnej w tym samym pliku - definicje te,jak też definicje zmiennej o tej samej nazwie w innych jednostkach kompilacji będą się odnosić do dokładnie tejsamej zmiennej. Właściwość ta pochodzi prawdopodobnie z czasów, gdy w C nie było słowa extern, więc deklaracjęzmiennej globalnej można było bez dodatkowych oznaczeń zamieścić w pliku nagłówkowym.W języku C++ obowiązuje "silne wiązanie" (ang. strict linkage) dla obiektów jawnych, natomiast słabe dla obiektówniejawnych. Obiekty niejawne w C++ to są tablice metod wirtualnych tworzonych dla określonej klasy oraz funkcjeinline. Silne wiązanie oznacza, że jeśli w zbiorze kompilacji zostaną znalezione dwa obiekty o tej samej nazwie, tolinker zgłosi błąd i odmówi wiązania.

Page 82: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 82

Typ stałej znakowejW języku C literał znakowy (stała znakowa), np. 'a' jest traktowana jako int, natomiast w C++ jest uważana za char.

Typ boolW C++ istnieje oficjalny typ bool i dwie stałe tego typu true i false, które służą do przechowywania wartościlogicznych. Jest typem zwracanym operatorów porównawczych i relacji oraz typem przyjmowanym i zwracanymprzez operatory && i ||. Ten typ musi mieć również wyrażenie podawane do if, while i drugiego wyrażenia w for.Ze względu na wsteczną zgodność jednak pozostawiono domyślną konwersję typu bool na int, przy czym false i truesą konwertowane odpowiednio na 0 i 1, natomiast w drugą stronę 0 konwertuje się na false i każda inna wartośćcałkowita na true.

Typy wskaźnikówW języku ANSI C dozwolone są niejawne konwersje pomiędzy różnymi typami wskaźnikowymi oraz pomiędzytypami wskaźnikowymi i typami całkowitymi. Co prawda wiele kompilatorów zgłasza ostrzeżenia przy próbachdokonania takiej konwersji bez jawnego rzutowania (za wyjątkiem konwersji, w których uczestniczy void*), nie jestona jednak w ANSI C błędem.W języku C++ niejawne konwersje pomiędzy wskaźnikami i referencjami do różnych typów są możliwe tylko wprzypadku typów spokrewnionych, tzn. wskaźnik do klasy pochodnej może być niejawnie konwertowany nawskaźnik do klasy bazowej (w tym również niejako uważa się "typ void" za bazę dla wszystkich typów, zatem każdywskaźnik na dane można niejawnie konwertować na void*). Wszelkie inne konwersje pomiędzy wskaźnikami dodanych różnych typów oraz wskaźnikami i typami całkowitymi muszą być jawnie zrzutowane.Warto zaznaczyć, że rzutowanie pomiędzy typami klasowymi, które są shierarchizowane (np. rzutowanie wskaźnikado klasy bazowej na wskaźnik do klasy pochodnej) mogą się odbywać wyłącznie poprzez operator static_cast lubdynamic_cast. Użycie do tego celu rzutowania ogólnego "(typ)obiekt" lub reinterpret_cast może spowodowaćniezdefiniowane zachowanie.

Alternatywne słowa kluczoweW języku C++ dodano dodatkowe słowa kluczowe opisujące niektóre operatory. Operatory &&, || i ! możemy teżzapisywać jako and, or i not. Istnieją także słowa dla operatorów bitowych &, | i ^: bitand, bitor i xor. Podobnierównież dla operatorów połączonych z przypisaniem: and_eq , or_eq i xor_eq . Istnieje także, choć szczerze niewiem po co, not_eq .

Biblioteka standardowaC++ używa innych nazw plików nagłówkowych dla biblioteki standardowej odziedziczonej z języka C - np.cstdio zamiast stdio.h. Zobacz też rozdział Przestrzenie nazw.

FunkcjeW języku C pusta lista argumentów: funkcja() oznacza, że prototyp nie precyzuje argumentów przyjmowanychprzez funkcję, natomiast deklaracja funkcja(void) oznacza, że funkcja nie przyjmuje argumentów.W języku C++ puste nawiasy są tożsame z (void) - nie przyjmowanie argumentów, natomiast efekt taki, co pustenawiasy w C można uzyskać poprzez (...), czyli zmienną listę argumentów, ale bez określania argumentówpoczątkowych (to z kolei nie jest dostępne w języku C).Należy zwrócić szczególną uwagę, że jest to w istocie dość uciążliwe ułatwienie, że () jest tożsame z (void). W konsekwencji bowiem o ile wyrażenie (a) (gdzie 'a' jest jakąś zmienną) można odróżnić od nazwy typu w nawiasach, np. (int), to w przypadku () jest to nie do odróżnienia. Stąd mała niekonsekwencja w deklarowaniu obiektów wraz z

Page 83: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 83

argumentami konstruktora:

Klasa x( arg1, arg2 );

ale bez argumentów musi być deklarowane jako

Klasa x;

czyli bez nawiasów.Należy też pamiętać, że odróżnianie argumentu w nawiasach od typu w nawiasach źle działa w przypadku obiektówtymczasowych:

Klasa1 obiekt( Klasa2() );

które, wbrew pozorom, nie deklaruje obiektu klasy Klasa1 z podaniem obiektu tymczasowego typu Klasa2 dokonstruktora, lecz deklaruje funkcję o nazwie 'obiekt', która przyjmuje funkcję (bezparametrową, zwracającą Klasa2)i zwraca typ Klasa1. Rozwiązaniem jest dodanie nawiasów, więcej światła na ten problem rzucą poniższe przykłady:

Klasa1 o1( Klasa2 funkcja() ); // funkcja (przyjmująca funkcję)

Klasa1 o2( Klasa2 (int) ); // funkcja (przyjmująca funkcję przyjmującą jeden argument)

Klasa1 o3( Klasa2 (10) ); // obiekt (z podaniem tymczasowego obiektu, z podaniem wartości)

Klasa1 o4( (Klasa2()) ); // obiekt (z podaniem tymczasowego obiektu)

Manglowanie nazw funkcjiW związku z przeciążaniem funkcji, każda funkcja w C++ ma unikalną identyfikację, niezależną od jej nazwy. Tenidentyfikator służy również do rozpoznania odpowiedniej wersji funkcji na poziomie wiązania - nawet jeśli istniejeprototyp funkcji o określonej nazwie, ale z innymi parametrami, niż te, z którymi została ta funkcja zdefiniowana, tobłąd przy próbie wywołania takiej funkcji zostanie wykryty na etapie wiązania (w przypadku języka C nie zostałbywykryty w ogóle).To spowodowało niezgodność sposobu wiązania funkcji z językiem C. Żeby móc w C++ użyć funkcji zdefiniowanejw języku C, to jej prototyp musi być poprzedzony extern "C". Taka funkcja nie może wtedy podlegać przeciążaniu(tzn. może być wiele funkcji o takiej nazwie, ale tylko jedna z nich może być extern "C").

StrukturyJeśli mamy strukturę Struktura to w C zmienne definiujemy struct Struktura s1, s2;. W C++ możemypominąć słowo kluczowe struct (i podobnie jest z union, enum i class). Dla zgodności z językiem C jednak niejest zabronione ponowne użycie nazwy struktury w innym znaczeniu (np. funkcji, co ma miejsce w przypadkustandardowej funkcji z języka C stat), tyle że jeśli się tak stanie, to wtedy nie można już pominąć słowa struct,jeśli się ma na myśli określony typ strukturalny.

Pozostałe

Page 84: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 84

Zasoby

Linki zewnętrzne• Thinking in C++ [2] - darmowy, bardzo dobry podęcznik Bruce'a Eckela

• Niekompletne, darmowe polskie tłumaczenie [3]

• http:/ / www. cppreference. com/ - bardzo zwięzły opis najważniejszych elementów C++• http:/ / it. hk. pl - kurs C++ od podstaw z przykładami• C++ bez cholesterolu [4]

• http:/ / cprogramming. com Tutoriale, zasoby, artykuły o c++• Oficjalna dokumentacja STL na stronie SGI [5]

• http:/ / xion. org. pl/ productions/ texts/ coding/ megatutorial/ polski tutorial "Od zera do gier kodera"

Książki• Jerzy Grębosz, Symfonia C++ [6]

• Jerzy Grębosz, Pasja C++ [7]

LicencjaVersion 1.2, November 2002

Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.

51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

Everyone is permitted to copy and distribute verbatim copies

of this license document, but changing it is not allowed.

0. PREAMBLEThe purpose of this License is to make a manual, textbook, or other functional and useful document "free" in thesense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it,either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way toget credit for their work, while not being considered responsible for modifications made by others.This License is a kind of "copyleft", which means that derivative works of the document must themselves be free inthe same sense. It complements the GNU General Public License, which is a copyleft license designed for freesoftware.We have designed this License in order to use it for manuals for free software, because free software needs freedocumentation: a free program should come with manuals providing the same freedoms that the software does. Butthis License is not limited to software manuals; it can be used for any textual work, regardless of subject matter orwhether it is published as a printed book. We recommend this License principally for works whose purpose isinstruction or reference.

1. APPLICABILITY AND DEFINITIONSThis License applies to any manual or other work, in any medium, that contains a notice placed by the copyrightholder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-freelicense, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers toany such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the licenseif you copy, modify or distribute the work in a way requiring permission under copyright law.

Page 85: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 85

A "Modified Version" of the Document means any work containing the Document or a portion of it, either copiedverbatim, or with modifications and/or translated into another language.A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with therelationship of the publishers or authors of the Document to the Document's overall subject (or to related matters)and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbookof mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter ofhistorical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical orpolitical position regarding them.The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of InvariantSections, in the notice that says that the Document is released under this License. If a section does not fit the abovedefinition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zeroInvariant Sections. If the Document does not identify any Invariant Sections then there are none.The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in thenotice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and aBack-Cover Text may be at most 25 words.A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specificationis available to the general public, that is suitable for revising the document straightforwardly with generic text editorsor (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor,and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input totext formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has beenarranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is notTransparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format,LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML,PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF andJPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors,SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generatedHTML, PostScript or PDF produced by some word processors for output purposes only.The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold,legibly, the material this License requires to appear in the title page. For works in formats which do not have any titlepage as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding thebeginning of the body of the text.A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or containsXYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific sectionname mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preservethe Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ"according to this definition.The Document may include Warranty Disclaimers next to the notice which states that this License applies to theDocument. These Warranty Disclaimers are considered to be included by reference in this License, but only asregards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has noeffect on the meaning of this License.

Page 86: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 86

2. VERBATIM COPYINGYou may copy and distribute the Document in any medium, either commercially or noncommercially, provided thatthis License, the copyright notices, and the license notice saying this License applies to the Document are reproducedin all copies, and that you add no other conditions whatsoever to those of this License. You may not use technicalmeasures to obstruct or control the reading or further copying of the copies you make or distribute. However, youmay accept compensation in exchange for copies. If you distribute a large enough number of copies you must alsofollow the conditions in section 3.You may also lend copies, under the same conditions stated above, and you may publicly display copies.

3. COPYING IN QUANTITYIf you publish printed copies (or copies in media that commonly have printed covers) of the Document, numberingmore than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers thatcarry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on theback cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front covermust present the full title with all words of the title equally prominent and visible. You may add other material on thecovers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Documentand satisfy these conditions, can be treated as verbatim copying in other respects.If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many asfit reasonably) on the actual cover, and continue the rest onto adjacent pages.If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include amachine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy acomputer-network location from which the general network-using public has access to download usingpublic-standard network protocols a complete Transparent copy of the Document, free of added material. If you usethe latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity,to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after thelast time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.It is requested, but not required, that you contact the authors of the Document well before redistributing any largenumber of copies, to give them a chance to provide you with an updated version of the Document.

4. MODIFICATIONSYou may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above,provided that you release the Modified Version under precisely this License, with the Modified Version filling therole of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses acopy of it. In addition, you must do these things in the Modified Version:• A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of

previous versions (which should, if there were any, be listed in the History section of the Document). You mayuse the same title as a previous version if the original publisher of that version gives permission.

• B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of themodifications in the Modified Version, together with at least five of the principal authors of the Document (all ofits principal authors, if it has fewer than five), unless they release you from this requirement.

• C. State on the Title page the name of the publisher of the Modified Version, as the publisher.• D. Preserve all the copyright notices of the Document.• E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.• F. Include, immediately after the copyright notices, a license notice giving the public permission to use the

Modified Version under the terms of this License, in the form shown in the Addendum below.

Page 87: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 87

• G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in theDocument's license notice.

• H. Include an unaltered copy of this License.• I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year,

new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled"History" in the Document, create one stating the title, year, authors, and publisher of the Document as given onits Title Page, then add an item describing the Modified Version as stated in the previous sentence.

• J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of theDocument, and likewise the network locations given in the Document for previous versions it was based on.These may be placed in the "History" section. You may omit a network location for a work that was published atleast four years before the Document itself, or if the original publisher of the version it refers to gives permission.

• K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preservein the section all the substance and tone of each of the contributor acknowledgements and/or dedications giventherein.

• L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbersor the equivalent are not considered part of the section titles.

• M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.• N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant

Section.• O. Preserve any Warranty Disclaimers.If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections andcontain no material copied from the Document, you may at your option designate some or all of these sections asinvariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. Thesetitles must be distinct from any other section titles.You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your ModifiedVersion by various parties--for example, statements of peer review or that the text has been approved by anorganization as the authoritative definition of a standard.You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-CoverText, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one ofBack-Cover Text may be added by (or through arrangements made by) any one entity. If the Document alreadyincludes a cover text for the same cover, previously added by you or by arrangement made by the same entity youare acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from theprevious publisher that added the old one.The author(s) and publisher(s) of the Document do not by this License give permission to use their names forpublicity for or to assert or imply endorsement of any Modified Version.

5. COMBINING DOCUMENTSYou may combine the Document with other documents released under this License, under the terms defined insection 4 above for modified versions, provided that you include in the combination all of the Invariant Sections ofall of the original documents, unmodified, and list them all as Invariant Sections of your combined work in itslicense notice, and that you preserve all their Warranty Disclaimers.The combined work need only contain one copy of this License, and multiple identical Invariant Sections may bereplaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, makethe title of each such section unique by adding at the end of it, in parentheses, the name of the original author orpublisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the listof Invariant Sections in the license notice of the combined work.

Page 88: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 88

In the combination, you must combine any sections Entitled "History" in the various original documents, formingone section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sectionsEntitled "Dedications". You must delete all sections Entitled "Endorsements."

6. COLLECTIONS OF DOCUMENTSYou may make a collection consisting of the Document and other documents released under this License, andreplace the individual copies of this License in the various documents with a single copy that is included in thecollection, provided that you follow the rules of this License for verbatim copying of each of the documents in allother respects.You may extract a single document from such a collection, and distribute it individually under this License, providedyou insert a copy of this License into the extracted document, and follow this License in all other respects regardingverbatim copying of that document.

7. AGGREGATION WITH INDEPENDENT WORKSA compilation of the Document or its derivatives with other separate and independent documents or works, in or ona volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilationis not used to limit the legal rights of the compilation's users beyond what the individual works permit. When theDocument is included in an aggregate, this License does not apply to the other works in the aggregate which are notthemselves derivative works of the Document.If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is lessthan one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket theDocument within the aggregate, or the electronic equivalent of covers if the Document is in electronic form.Otherwise they must appear on printed covers that bracket the whole aggregate.

8. TRANSLATIONTranslation is considered a kind of modification, so you may distribute translations of the Document under the termsof section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders,but you may include translations of some or all Invariant Sections in addition to the original versions of theseInvariant Sections. You may include a translation of this License, and all the license notices in the Document, andany Warranty Disclaimers, provided that you also include the original English version of this License and theoriginal versions of those notices and disclaimers. In case of a disagreement between the translation and the originalversion of this License or a notice or disclaimer, the original version will prevail.If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section4) to Preserve its Title (section 1) will typically require changing the actual title.

Page 89: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 89

9. TERMINATIONYou may not copy, modify, sublicense, or distribute the Document except as expressly provided for under thisLicense. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automaticallyterminate your rights under this License. However, parties who have received copies, or rights, from you under thisLicense will not have their licenses terminated so long as such parties remain in full compliance.

10. FUTURE REVISIONS OF THIS LICENSEThe Free Software Foundation may publish new, revised versions of the GNU Free Documentation License fromtime to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address newproblems or concerns. See http:/ / www. gnu. org/ copyleft/ .Each version of the License is given a distinguishing version number. If the Document specifies that a particularnumbered version of this License "or any later version" applies to it, you have the option of following the terms andconditions either of that specified version or of any later version that has been published (not as a draft) by the FreeSoftware Foundation. If the Document does not specify a version number of this License, you may choose anyversion ever published (not as a draft) by the Free Software Foundation.

How to use this License for your documentsTo use this License in a document you have written, include a copy of the License in the document and put thefollowing copyright and license notices just after the title page:

Copyright (c) YEAR YOUR NAME.

Permission is granted to copy, distribute and/or modify this document

under the terms of the GNU Free Documentation License, Version 1.2

or any later version published by the Free Software Foundation;

with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.

A copy of the license is included in the section entitled "GNU

Free Documentation License".

If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:

with the Invariant Sections being LIST THEIR TITLES, with the

Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.

If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those twoalternatives to suit the situation.If your document contains nontrivial examples of program code, we recommend releasing these examples in parallelunder your choice of free software license, such as the GNU General Public License, to permit their use in freesoftware.

Page 90: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

C++/Wersja do druku 90

Przypisy[1] http:/ / creativecommons. org/ licenses/ by-sa/ 3. 0/ deed. pl[2] http:/ / mindview. net/ Books/ TICPP/ ThinkingInCPP2e. html[3] http:/ / moria. ii. uj. edu. pl/ thinkcpp/[4] http:/ / www. intercon. pl/ ~sektor/ cbx/[5] http:/ / www. sgi. com/ tech/ stl/ table_of_contents. html[6] http:/ / www. ifj. edu. pl/ ~grebosz/ symfoniap. html[7] http:/ / www. ifj. edu. pl/ ~grebosz/ pasjap. html

Page 91: C++/Wersja do druku Programowanie w C++download.recone.org/C++/cpp.pdf · Jak tworzyć, możliwe niebezpieczeństwa, słowo kluczowe mutable 4. ... jednostka, po dyskusji utracił

Źródła i autorzy artykułu 91

Źródła i autorzy artykułuC++/Wersja do druku  Źródło: http://pl.wikibooks.org/w/index.php?oldid=173483  Autorzy: Derbeth, Felix, Lethern, Piotr

Źródła, licencje i autorzy grafikGrafika:Exquisite-print printer.png  Źródło: http://pl.wikibooks.org/w/index.php?title=Plik:Exquisite-print_printer.png  Licencja: GNU General Public License  Autorzy: Mindmatrix, SasaStefanovicGrafika:Crystal_ktip.png  Źródło: http://pl.wikibooks.org/w/index.php?title=Plik:Crystal_ktip.png  Licencja: GNU Lesser General Public License  Autorzy: Dake, Rocket000, RubySSGrafika:Fairytale messagebox info.png  Źródło: http://pl.wikibooks.org/w/index.php?title=Plik:Fairytale_messagebox_info.png  Licencja: GNU Lesser General Public License  Autorzy:Amada44, Anime Addict AA, Bayo, Dake, Jon Harald Søby, Rocket000, ZooFariGrafika:Plume ombre.png  Źródło: http://pl.wikibooks.org/w/index.php?title=Plik:Plume_ombre.png  Licencja: GNU Free Documentation License  Autorzy: Darkdadaah, Javierme, Rilegator,Rocket000Grafika:Nuvola apps important.svg  Źródło: http://pl.wikibooks.org/w/index.php?title=Plik:Nuvola_apps_important.svg  Licencja: GNU Lesser General Public License  Autorzy: BastiquePlik:Quote-alpha.png  Źródło: http://pl.wikibooks.org/w/index.php?title=Plik:Quote-alpha.png  Licencja: GNU Free Documentation License  Autorzy: Aotake, Kenmayer, PetjaTouru, SanguinezFile:C Cpp compare.png  Źródło: http://pl.wikibooks.org/w/index.php?title=Plik:C_Cpp_compare.png  Licencja: Creative Commons Attribution-Sharealike 3.0,2.5,2.0,1.0  Autorzy: JohannesSchneider

LicencjaCreative Commons Attribution-Share Alike 3.0 Unported//creativecommons.org/licenses/by-sa/3.0/