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:

powrót na początek strony


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)

powrót na początek strony


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.

powrót na początek strony


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ń
  1. 4/6.03.2008: liczby naturalne w zapisie słownym (ps/pdf)
  2. 11/13.03.2008: schowek na liczbę (ps/pdf)
  3. 18/20.03.2008: daty w kalendarzu gregoriańskim (ps/pdf)
  4. 1/3.04.2008: tablica bitów (ps/pdf)
  5. 8/10.04.2008: rozkład liczb naturalnych na czynniki pierwsze (ps/pdf)
  6. 15/17.04.2008: konwersja łańcuchów znakowych na liczby (ps/pdf)
  7. 22/24.04.2008: kalkulator ONP (ps/pdf)
  8. 29.04/8.05.2008: uniqueleton (ps/pdf)
  9. 13/15.05.2008: długie liczby całkowite (ps/pdf)
  10. 20/29.05.2008: sortowanie za pomocą obiektów funkcyjnych (ps/pdf)
  11. 27/29.05.2008: stos i kolejka (ps/pdf)
  12. 3/5.06.2008: bezpieczne pliki i manipulatory (ps/pdf)
  13. 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
  • grupa wtorkowa (T.Cichocki): (html)
  • grupa czwartkowa (P.Rzechonek): (html)

powrót na początek strony


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.

powrót na początek strony


Notatki

Wprowadzenie do C++
Pierwsze programy

Najprostszy program w języku C++ wygląda następująco:

// najkrótszy program int main () {}

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:

// program powitalny # include <iostream> using namespace std; int main () { cout << "Witaj na kursie C++!" << endl; }

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:

// program czytający dane i wypisujący wyniki # include <iostream> using namespace std; int main () { clog << "Podaj liczbę całkowitą: "; int x; cin >> x; if (!cin) { cerr << "Błędne dane!" << endl; return 1; } if (x<0) cout << '-' << endl; else if (x>0) cout << '+' << endl; else cout << '0' << endl; return 0; }

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:

user@comp:~/dir/$ g++ program.cpp

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:

user@comp:~/dir/$ g++ -o prog.exe program.cpp

Po takim wywołaniu kompilator utworzy plik z programem wykonywalnym o nazwie prog.exe.

powrót na początek strony

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:

user@comp:~/dir/$ g++ -E program.cpp | less

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:

user@comp:~/dir/$ g++ -D NDEBUG program.cpp

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:

user@comp:~/dir/$ g++ -D 'kwadrat(n)=((n)*(n))' program.cpp

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:

user@comp:~/dir/$ g++ -D NDEBUG prog1.cpp -U NDEBUG prog2.cpp prog3.cpp