C++
C++ to obiektowo zorientowany język programowania. Został on zaprojektowany przez B.Stroustrupa z myślą o programowaniu systemowym oraz do zaawansowanych obliczeń matematycznych. Świadome używanie C++ do rozwiązywania problemów algorytmicznych daje dużo satysfakcji doświadczonym programistom, zwłaszcza dlatego że generowany przez kompilator kod jest bardzo efektywny.
Celem kursu jest zapoznanie studentów z bogatą składnią języka C++, najważniejszymi technikami stosowanymi w programowaniu z wykorzystaniem tego języka oraz z obszernymi fragmentami biblioteki standardowej STL.
wymagane przygotowanie
- Umiejętność programowania strukturalnego w języku ANSI C.
- Znajomość podstawowych struktur danych (tablice, listy, drzewa, grafy).
literatura
Literatura papierowa:
- B.Stroustrup: Język C++. WNT, Warszawa 2000.
- S.B.Lippman, J.Lajoie: Podstawy języka C++. WNT, Warszawa 2001.
- C.L.Tondo, B.P.Leung: Podstawy języka C++. Ćwiczenia i rozwiązania. WNT, Warszawa 2001.
- J.Grębosz: Symfonia C++ (tom 1, 2, 3). Oficyna Kallimach, Kraków 2002.
- J.Grębosz: Pasja C++ (tom 1, 2). Oficyna Kallimach, Kraków 2003.
- N.M.Josuttis: C++. Biblioteka standardowa. Podręcznik programisty. Wydawnictwo Helion, Gliwice 2003.
Literatura elektroniczna:
Terminarz
- wykład: czwartek 10-12 s.140 (P.Rzechonek)
-
laboratorium:
wtorek 12-14 s.107 (T.Cichocki)
czwartek 12-14 s.110 (P.Rzechonek)
Ogłoszenia
- 12.05.2008
-
Laureatami w konkursie na najskuteczniejszego programistę C++ zostali:
Kamil Graczyk, Marcin Sas-Szymański i Tomasz Wasilczyk.
Spośród tego grona wyłoniłem zwycięzcę - został nim Marcin Sas-Szymański! Laureatom a w szczególności zwyciężcy pragnę serdecznie pogratulować :) a wszystkim przybyłym uczestnikom konkursu podziękować za udział w spotkaniu. PRz. - 29.05.2008
- Postanowiłem zorganizować konkurs :) na najskuteczniejszego programistę C++. Kandydatów (minimum 3 osoby) wyznaczę na podstawie punktów zgromadzonych w czasie semestru i spośród nich arbitralnie wybiorę ostatecznego zwycięzcę. Przewidziane są drobne nagrody (raczej nie będzie to VW Passat z GPS)! Termin rozstrzygnięcia konkursu: 12 czerwca o godzinie 12:15 w sali 141 - zapraszam wszystkich uczestników kursu.
- 29.05.2008
- Zadanie 11 i 12 będę odbierał w mojej grupie 5 czerwca.
- 27.05.2008
- W dniach 10 i 12 czerwca nie odbędą się zajęcia z C++ z powodu odbywających się w naszym instytucie zajęć dla licealistów (mogą to być nasi przyszli studenci :).
- 9.05.2008
- Zadanie 9 będę odbierał w mojej grupie 15 maja w godzinach 12-14. Kto nie będzie mógł przyjść na te dodatkowe zajęcia, powinien dostarczyć program 9 dwa tygodnie póżniej, czyli 29 maja.
- 11.03.2008
- Postaram się zamieszczać listy z zadaniami w czwartek lub w piątek, chociaż nie zawsze będzie to możliwe. Na szczęście obecne zadania nie są ani trudne ani czasochłonne :)
- 1.03.2008
- W tym miejscu będą się pojawiać ogłoszenia organizacyjne dotyczące wszystkich studentów uczestniczących w kursie.
Laboratorium
zasady zaliczenia przedmiotu
- ogólnie:
- W semestrze będzie opublikowanych (na tej stronie) kilkanaście prostych zadań do zaprogramowania. Za każde poprawnie zaprogramowane zadanie i oddane w terminie można będzie dostać 10 punktów albo 5 punktów za zadanie oddane z tygodniowym opóźnieniem.
- terminy:
- Zadania do zaprogramowania będą ogłaszane w tygodniu poprzedzającym termin ich realizacji. Zadania należy oddawać w wyznaczonym terminie. Spóźnienia dłuższe niż tydzień nie będą tolerowane, za wyjątkiem uzasadnionych sytuacji: choroba potwierdzona zwolnieniem lekarskim, wezwanie na policję lub do sądu, itp.
- prezentacje:
- Programy należy prezentować osobiście w czasie pracowni (proszę nie wysyłać programów pocztą elektroniczną, ani nie przekazywać ich poprzez kolegów czy koleżanki). W trakcie prezentacji programu trzeba się liczyć z pytamiami dotyczącymi zadania: metoda rozwiązania, zastosowane konstrukcje językowe, wykorzystane technologie, itp.
- oceny:
- Aby zaliczyć laboratorium na ocenę dostateczną trzeba do końca semestru zdobyć 50% z wszystkich możliwych do uzyskania punktów; na ocenę bardzo dobra trzba będzie zgromadzić 90% punktów; oceny pośrednie pozostją w liniowej zależności od przedstawionych wymagań granicznych.
listy zadań
- 4/6.03.2008: liczby naturalne w zapisie słownym (ps/pdf)
- 11/13.03.2008: schowek na liczbę (ps/pdf)
- 18/20.03.2008: daty w kalendarzu gregoriańskim (ps/pdf)
- 1/3.04.2008: tablica bitów (ps/pdf)
- 8/10.04.2008: rozkład liczb naturalnych na czynniki pierwsze (ps/pdf)
- 15/17.04.2008: konwersja łańcuchów znakowych na liczby (ps/pdf)
- 22/24.04.2008: kalkulator ONP (ps/pdf)
- 29.04/8.05.2008: uniqueleton (ps/pdf)
- 13/15.05.2008: długie liczby całkowite (ps/pdf)
- 20/29.05.2008: sortowanie za pomocą obiektów funkcyjnych (ps/pdf)
- 27/29.05.2008: stos i kolejka (ps/pdf)
- 3/5.06.2008: bezpieczne pliki i manipulatory (ps/pdf)
- zadanie dodatkowe (tylko dla tych studentów, którym brakuje niewielu
punktów do zaliczenia przedmiotu)
17/19.06.2008: automaty skończone (ps/pdf)
rankingi
Wykład
- 28.02.2008 (wstęp do C++):
- Podstawowe cechy obiektowego programowania w C++.
- Referencje; przekazywanie parametrów do funkcji poprzez referancje.
- Definicja klasy; deklarowanie obiektów danej klasy.
- Odwołania do składowych w obiekcie.
- Pola i metody w klasie; definiowanie metod poza klasą.
- Konstruktory i destruktory.
- Standardowe wejście i wyjście (cin, cout, clog, cerr).
- Podstawowe manipulatory.
- Łańcuchy znakowe (string); dodawanie jako operator konkatenacji.
- 6.03.2008 (klasy i obiekty, stałe pola, lista inicjalizacyjna):
- Stałe (modyfikator const); stałe parametry funkcji; stałe pola.
- Wskaźniki do stałych i stałe wskaźniki.
- Referencje do stałych obiektów jako argumenty funkcji.
- Deklarowanie stałych obiektów.
- Usuwanie stałości (atrybut mutable).
- Tworzenie (operator new) i usuwanie (operator delete) obiektów ze sterty.
- Wskaźnik this.
- Przeciążanie metod i konstruktorów
- Konstruktor domyślny (bezargumentowy) i kopiujący.
- Lista inicjalizacyjna.
- Tablice obiektów.
- 13.03.2008 (metody wbudowane, przeciążanie metod, składowe statyczne):
- Funkcje i metody wbudowane (atrybut inline).
- Przeciążanie funkcji, metod i konstruktorów.
- Parametry domyślne.
- Składowe statyczne (atrybut static).
- Wyjątki - wstęp.
- Strumienie napisowe (strumień ostringstream oraz istringstream).
- 20.03.2008 (przeciążanie operatorów, przyjaźń):
- Funkcje i metody zaprzyjaźnione (atrybut friend).
- Przeciążanie operatorów dla typów zdefiniowanych przez użytkownika.
- Operatory składowe i zaprzyjaźnione funkcje operatorowe.
- Operatory których nie można przeciążać (operator zakresu (::), operator dostępu do składowych (. i .*), operator warunkowy ?:), operator rozmiaru danych (sizeof) i operatory rzutowania).
- Operatory predefiniowane (przypisanie (=), uzyskanie adresu (&), sekwencja obliczeń (,) oraz tworzenie i usuwanie obiektów w pamięci (new, new[], delete i delete[]).
- Jednoargumentowe operatory pre- i post- inkrementacji/dekrementacji (++ i --).
- Składowy niestatyczny operator przypisania (=).
- Składowy niestatyczny operator indeksowania ([]).
- Składowy niestatyczny operator wywołania funkcji (()).
- Składowy niestatyczny operator dostępu do składowych (->).
- Statyczne operatory zarządzania pamięcią wolną (new, new[], delete i delete[]).
- Zaprzyjaźnione operatory czytania ze strumienia (>>) i pisania do strumienia (<<).
- 27.03.2008 (dziedziczenie):
- Istota dziedziczenia: projektowanie hierarchii klas, dostosowanie gotowej klasy do własnych potrzeb.
- Definiowanie dziedziczenia: klasa bazowa i pochodna.
- Zakresy widzialności składowych i dostęp do składników klasy bazowej w klasie pochodnej.
- Dziedziczenie publiczne i niepubliczne; wybiórcze udostępnianie za pomocą deklaracji using.
- Nie dziedziczy się: składowych prywatnych, konstruktorów, destruktora, przypisania kopiującego.
- Inicjalizacja i destrukcja obiektów w warunkach dziedziczenia.
- Wskaźnikiem do klasy bazowej można pokazywać na obiekty klas pochodnych.
- Dziedziczenie wielobazowe.
- Dziedziczenie wirtualne.
- 3.04.2008 (polimorfizm i rzutowanie):
- Hierarchia klas i niejawne rzutowanie w górę wskaźników i referencji do obiektów.
- Definiowanie metod wirtualnych.
- Działanie polimorfizmu w przypadku dziedziczenia.
- Wczesne i późne wiązanie.
- Destruktory w klasach polimorficznych.
- Klasy abstarkcyjne i metody czysto wirtualne.
- Jawne rzutowanie w górę wskaźników i referencji do obiektów w hierarchii klas za pomoca operatora dynamic_cast na etapie wykonania.
- Jawne rzutowanie na etapie kompilacji za pomoca operatorów static_cast i reinterpret_cast.
- Składowy operator rzutowania w klasie.
- Konstruktor konwertujący bez atrybutu explicit.
- RTTI - identyfikacja dowolnych typów na etapie wykonania.
- 10.04.2008 (wyjątki):
- Zgłaszanie wyjątków (instrukcja throw) i ich wyłapywanie (instrukcja try-catch).
- Dopasowywanie wyjątków w blokach catch.
- Mechanizm lotu wyjątku (zwijanie stosu).
- Specyfikacja wyjątków w funkcjach i w metodach polimorficznych.
- Wyjątki zgłaszane w kontruktorach.
- W destruktorach nie wolno zgłaszać wyjątków.
- Zdobywanie zasobów poprzez inicjalizację.
- Klasa auto_ptr.
- Wyjątki standardowe i ich hierarchia w STL.
- Definiowanie własnych wyjątków.
- 17.04.2008 (szablony funkcji):
- 24.04.2008 (szablony klas):
- 8.05.2008 (strumienie):
- Standardowe strumienie wejścia/wyjścia cin, cout, clog i cerr.
- Hierarchia klas strumieni.
- Operatory << i >> do formatowanego wstawiania i wyjmowania ze strumienia.
- Flagi stanu formatowania w strumieniach.
- Nieformatowane operacje wejścia/wyjścia.
- Manipulatory standardowe; definiowanie własnych manipulatorów.
- 29.05.2007 (strumienie):
- Obsługa błędów w strumieniach.
- Strumienie związane z plikami ifstream i ofstream.
- Strumienie związane z łańcuchami znakowymi istringstream i ostringstream.
- Łączenie strumieni.
- Współpraca ze starą biblioteką cstdio.
- 5.06.2007 (kontenery i iteratory):
- Kontenery STL implementują semantykę wartości.
- Brak bezpieczeństwa w kontenerach.
- Cechy elementów pamiętanych w kontenerach.
- Wspólne operacje kontenerów. Leksykograficzne porównywanie kontenerów.
- Kontenery sekwencyjne (vactor, deque, list) i asoscjacyjne (set, multiset, map, multimap).
- Kontenery specjalne: stack, queue priority_queue.
- Iteratory STL implementują semantykę wskaźnika na element w tablicy.
- Operatory w iteratorach.
Notatki
Wprowadzenie do C++
Pierwsze programy
Najprostszy program w języku C++ wygląda następująco:
Każdy program w języku C++ musi mieć zdefiniowana funkcję main()
.
Wykonanie programu rozpoczyna się od wywołania tej właśnie funkcji.
Funkcja main()
zwraca wartość typu int
, która jest
przekazywana do systemu operacyjnego.
Jeśli jawnie nie zwróci się żadnej wartości w tej funkcji, to domyślnie
funkcja main()
zwróci wartość 0
.
Wartość zerowa oznacza poprawne zakończenie się programu a wartość niezerowa
sygnalizyje jakiś błąd.
Następny program wypisze na standardowy wyjściu komunikat powitalny:
Pliki nagłówkowe zawierające deklaracje funkcji i zmiennych globalnych oraz
definicje klas z biblioteki standardowej nie mają rozszerzenia
.h
.
Dyrektywa #include<iostream>
dołącza plik nagłówkowy,
zawierający definicję podstawowych mechanizmów strumieniowych i deklarację
obiektów związanych ze standardowym wejściem i wyjściem.
Dyrektywa użycia using namespace std;
odnosząca się do
standardowej przestrzeni nazw std
jest włączona po to, by nazw
zdefiniowanych w bibliotece standardowej nie trzeba było kwalifikować za
pomocą operatora zakresu 9::
).
Jawne wskazywanie przestrzeni nazw, w których zdefiniowane są obiekty
zmniejsza bowiem czytelność kodu, na przykład std::cin
zamiast
samego cin
.
W bibliotece standardowej przeciążono operatory przesunięć bitowych
(<<
i >>
) w odniesieniu do operacji
na strumieniach.
Operatory te sugerują kierunek przepływu danych.
Można ich używać kaskadowo, czyli w jednym wyrażeniu można wilokrotnie
zapisać dane do strumienia wyjściowego operatotem <<
albo
wielokrotnie odczytać dane ze strumienia wejściowego operatotem
>>
.
Wynikiem działania operatorów przesunięć bitowych na strumieniach jest
pierwszy argument, czyli referencja do strumienia.
Operatory przesunięć bitowych są lewostronnie łączne, więc przykładowe
wyrażenie cout<<"wartość x="<<x<<endl
należy
zinterpretować ((cout<<"wartość x=")<<x)<<endl
.
Kolejny program najpierw wczyta ze standardowego wejścia liczbę całkowitą
int
a potem wypisze na standardowy wyjściu informację o znaku
tej liczby:
Strumienie związane ze standardowym wejściem i wyjściem, czyli
cin
, cout
, clog
i cerr
,
są strumieniami, które automatycznie dokonują konwersji danych z postaci
binarnej na tekstową i odwrotnie.
W ostatnim przykładzie pokazano, jak można zareagować na błędne dane
w strumieniu wejściowym.
Wykorzystano do tego celu przeciążony dla strumieni operator rzutowania
(bool)
w połączeniu z operatorem nagacji logicznej
(!
).
Po nieudanym zapisie albo odczycie strumień przechodzi do stanu z błędem,
co można wykryć testując go jak w przykładowym programie if
(!cin)
.
Kompilowanie programów
Programy napisane w C++ i różne konstrukcje językowe prezentowane na tej stronie są kompilowane kompilatorem g++ w wersji 4.1.2 pod kontrolą systemu operacyjnego Linux Ubuntu 7.04.
Załóżmy, że napisaliśmy program w C++ i zapisaliśmy go w pliku o nazwie
program.cpp
.
Aby go skompilować, należy wydać następujące polecenie:
Jeśli w programie nie było żadnych błędów, to w wyniu otrzymamy program
wykonywalny o nazwie a.out
.
Jeśli chcemy nadać inną nazwę programowi wykonywalnemu, to trzeba użyć opcji
kompilatora -o
:
Po takim wywołaniu kompilator utworzy plik z programem wykonywalnym o nazwie
prog.exe
.
Kompilowanie programów z dodatkowymi opcjami dla preprocesora
Załóżmy, że napisaliśmy program w C++ i zapisaliśmy go w pliku o nazwie
program.cpp
.
Można podglądnąć jaki kod wygeneruje ostatecznie preprocesor, wydając
następujące polecenie:
Po takim wywołaniu kompilator wypisze na standardowe wyjście przetworzony
przez preprocesor języka C++ program prog.cpp
i na tym zakończy
swoje działanie (nie wygeneruje kodu wynikowego).
Kompilując program można zdefiniować jakieś makro, które kompilator będzie
znał od początku. Należy wówczas posłużyć się wywołaniem z opcją
-D
:
W przykładzie tym zdefiniowano nazwę NDEBUG
, wykorzystywaną
w asercjach do generowania kodu testującego poprawność zadanych warunków
logicznych w programie; jeśli nazwa NDEBUG
zostanie zdefiniowana,
to preprocesor nie wygeneruje żadnego kodu testującego warunki (wywołanie
makra assert(warunek)
nie będzie generować żadnego istotnego kodu).
Opcji -D
można także użyć do zdefiniowania makra z argumentami,
na przykład:
W kompilowanym programie będzie można posługiwać się wywołaniem makra
kwadrat(wyrażenie)
.
Jeśli potrzebujemy usunąć zdefiniowaną wcześniej makrodefinicję, to należy
się posłużyć parametrem -U
, co ma zastosowanie przy kompilowaniu
programu składającedo się z kilku plików źródłowych: