Wprowadzenie do programowania w C++
IDE
Na zajęciach kod programów będzie pisany przy użyciu CLion IDE. IDE oprócz dostarczania podstawowego kolorowania składni i możliwości uruchamiania kodu,
dostarcza szereg przydatnych narzędzi, które znacznie potrafią przyspieszyć pisanie kodu. Są to między innymi:
podpowiadanie składni (CTRL+SPACE)
zautomatyzowanie nawigacji po projekcie (CTRL+B, CTRL+SHIFT+BACKSPACE, CTRL+ALT+HOME, CTRL+N, itd…)
-
automatyczna generacja części kodu (ALT+INSERT, CTRL+SHIFT+D, …)
narzędzie do automatycznego formatowania kodu (CTRL+ALT+L)
integracja z debuggerem
integracja z systemem kontroli wersji git,
integracja z narzędziem do automatycznego budowania kodu CMake
integracja z frameworkiem do testów jednostkowych Google Test
narzędzia do refaktoryzacji kodu, czyli poprawiania czytelności i jakości kodu bez modyfikacji jego zachowania
i wiele innych
W celu zmaksymalizowania produktywności należy zainstalować plugin Key promoter i wydrukować sobie Clion Reference Card.
Również należy zainstalować sobie wersję CLiona na domowym sprzęcie należy skorzystać z darmowej licencji studenckiej.
CMake
Na zajęciach będziemy korzystać z systemu budującego CMake. W celu ułatwienia pracy
należy zapoznać się z elementami znajdującymi się w dokumencie CMake,
w szczególności,
Zagadnienia te będą obowiązywać na kolokwium
Git i automatyczne sprawdzanie zadań
Na zajęciach będziemy wykorzystywali system kontroli wersji git.
Pod linkiem znajduje się adres repozytorium, gdzie zdefiniowane są testy do zadań
do laboratoriów. Każde laboratorium ma swój osobny katalog np. lab1, lab2, itd… w każdym laboratorium znajdują się podfoldery
dla każdego zadania, przykładowy factorial do pierwszego zadania z silnią już znajduje się repozytorium. Należy sklonować repozytorium
i zaimportować projekt do CLiona.
Należy zapoznać się z plikami CMakeLists.txt w folderze lab1 i lab1/factorial. Polecenie add_subdirectory_if_exists(reversestring) nie jest
wbudowane w CMake, ale jest zdefiniowane w tym projekcie jako wariant add_subdirectory, ale nie powodujący błędu jeśli podkatalog
nie istnieje. Żeby rozwiązywać kolejne zadania należy nawiązać do konwencji nazwniczej użytej w projekcie tzn. kolejne foldery muszą się
nazywać tak samo jak to jest przedstawione w kolejnych deklaracjach add_subdirectory_if_exists. W przeciwnym wypadku podprojekt nie
będzie widoczny
W celu sklonowania repozytorium do lokalnego folderu wystarczy wpisać w linii komed polecenie (jako opcjonalny można podać ostatni argument
z nazwą folderu do którego ma zostać skolnowany projekt
git clone https://gitlab.com/agh-jimp2/exercises.git
Należy sobie odświeżyć wiedzę na temat gita, w szczególności:
git diff,
git commit,
git status,
git add,
git push,
git pull,
git log,
git checkout
Dostępny projekt jest wyposażony w automatyczne testy jednostkowe weryfikujące poprawność napisanego kodu. Same testy zostaną omówione pod koniec laboratoriów,
jednak teraz wystarczy wiedzieć, że są to mini programiki, których celem jest uruchomienie małego fragmentu kodu i sprawdzenie, czy zachowuje się on zgodnie z naszymi oczekiwaniami
i zaraportowanie użytkownikowi sytuacji gdy jest przeciwnie.
Projekt został tak pomyślany, że każde zadania ma swoje osobne testy. Ogólna struktura kodu wygląda tak, że zdefiniowana jest biblioteka z kodem rozwiązania, która jest linkowana zarówno
do programu zdefiniowanego przez Ciebie, jak i mini programiku z testami. Stąd po naciśnięciu CTRL+F9 (Build) zostanie zbudowany projekt. Następnie można nacisnąć kombinację CTRL+ALT+F10
żeby zobaczyć listę dostępnych celów (targets) zdefiniowanych w całym projekcie. Wśród nich powinny znajdować się następujące cele:
factorial
libfactorial
lab1_factorial_tests
lab1_all_tests
i wiele innych
Cele zdefiniowane ze słowem tests powinny znajdować się w niższej sekcji i posiadać ikonę z czerwonym elementem. Są to zadania, które zostały rozpoznane przez IDE jako testy.
Wybranie zadania factorial uruchomi program z funkcją main zdefiniowaną w lab1/factorial/main.cpp. Natomiast uruchomienie zadania lab1_factorial_tests uruchomi testy jednostkowe
dotyczące zadania z silnią. Zadanie lab1_all_tests uruchamia wszystkie testy zdefiniowane dla laboratorium numer 1. W tym momencie jednak to zadanie nie powinno się dać skompilować
ponieważ brakuje kodu realizującego kolejne zadania. Uruchomienie libfactorial nie jest możliwe, dlatego, że jest to bilblioteka z kodem i nie ma punktu wejścia z funkcją main tak jak cały program.
Uruchomienie zadania lab1_factorial_tests powinno zakończyć się jednak częściowym powodzeniem, tzn. część testów powinno przejść, a część nie.
Uzyskujemy informację, że z 16 testów wykonanych 12 zawiodło. Testy zostały zorganizowane w dwie grupy factorial_test i FactorialDataDriveTests, każdej z tych grup można się przyglądnąć osobno.
Wynik testu informuje nas, że na przykład uruchomienie factorial(5) powinno dać nam w rezultacie 120, ale otrzymany wynik to 0
W innym wypadku napisane jest, że oczekiwano p.second równego 1, ale uzyskany wynik factorial(p.first) był również równy 0
Jeśli przeniesiemy się do definicji funkcji factorial (CTRL+SHIFT+ALT+N i wpisać początek nazwy factorial), a następnie naciśniemy (CTRL+B) by przeskoczyć do definicji funkcji wszystko będzie
jasne defnicja funkcji factorial jest następująca:
int factorial(int value) {
return 0;
}
Nic dziwnego, że zawsze zwraca 0.
Typy
Lista typów wbudowanych w standard C++
Nazwa | Opis |
char | Znak, albo mała liczba całkowita |
short int
(short) | Mała liczba całkowita |
int | Liczba całkowita |
long int
(long) | Długa liczba całkowita |
bool | Wartość true albo false |
float | Liczba zmiennoprzecinkowa |
double | Liczba zmiennoprzecinkowa podwójnej precyzji |
long double | Duża liczba zmiennoprzecinkowa podwójnej precyzji |
void | Pusty typ danych |
Typy char, short, int, long int posiadają warianty signed i unsigned.
Standard nie gwarantuje rozmiaru typu i rozmiary podstawowych typów zmieniają
się w zależności dla jakiej architekturze jest kompilowany kod.
Jeśli stawiamy pewne wymagania, np. chcemy przynajmniej 8 bajtową liczbę typu integer lepiej
skorzystać z typów zdefniowane w cstdint.
Oprócz typów prymitywnych w C++ można zdefiniować znacznie bogatszy system typów niż w C
w oparciu o klasy. Temat ten będzie zgłębiony na dalszych laboratoriach jednak w ćwiczeniach przyda się
znajomość klasy std::string, przyglądnąć się jak
w dokumentacji przedstawione są metody znajdujące się w klasie string (własciwie to basic_string), np.
c_str()
length()/size()
substr()
find_first_of()
replace()
Instrukcje sterujące
W C++ dostępne są następujące instrukcje sterujace:
instrukcja warunkowa if … else …
instrukcja warunkowa switch … case
pętla while
pętla do … while
pętla for
break - przerywa aktualną pętlę
continue - kontynuuje pętlę przechodząc do następnej iteracji
return
Funkcje
Główną funkcją każdego programu jest funkcja main().
Pisanie funkcji można rozdzielić na dwie fazy:
opcjonalna deklarację (tworzenie tzw. prototypu funkcji):
double potega(double);
definicję:
double potega(double liczba){
return liczba*liczba;
}
Domyślnym typem zwracanym (jeśli nie określono) jest int.
Brak określenia typu zwracanego przez funkcję w jej definicji jest błędem składniowym jeśli prototyp określa typ zwracany inny niż int
Zapomnienie o zwróceniu wartości z funkcji, która zadeklarowana została jako zwracająca wartość, jest błędem składniowym.
Funkcje rozróżniane są po nazwie i liście parametrów, a nie po zwracanym typie. Dlatego nie można zdefiniować dwóch funkcji o takich samych nazwach i liście parametrów a innych zwracanych typach:
// NIEPOPRAWNIE
void foo(int a, int b){...}
double foo(int c, int d){...}
Tablice
Tablice jednowymiarowe
Tablice jednowymiarowe w C++ można deklarować na cztery sposoby:
Deklarując jedynie rozmiar tablicy, bez podawania jej elementów:
int tab[100];
Deklarując rozmiar i podając elementy tablicy:
int tab[5] = {1,2,3,4,5};
Deklarując rozmiar, ale nie podając wartości wszystkich elementów:
int tab[10] = {1,2,3,4,5};
Podając listę elementów a nie podając rozmiaru tablicy:
int tab[] = {1,2,3,4,5};
Tablice wielowymiarowe
Tablice wielowymiarowe deklaruje się analogicznie do jednowymiarowych, jednakże podczas deklaracji tablicy wielowymiarowej konieczne jest podanie rozmiarów wymiarów, poza pierwszym:
//Poprawnie
int tab[][4] = {{1,2,3,4},{5,6,7,8}};
//NIEPOPRAWNIE
int tab[][] = {{1,2,3,4},{5,6,7,8}};
Definiując funkcję, której parametrem jest tablica wielowymiarowa, również konieczne jest podanie wszystkich rozmiarów wymiarów, poza pierwszym:
void foo(int tab[][10]){
...
}
Prosty program
Przykład
Program ma za zadanie wczytanie imienia i wyświetlenie powitania na ekranie.
// W pliku o nazwie program.cpp
#include <iostream> // Dołączamy bibliotekę odpowiedzialna za
// operacje wejścia i wyjścia
using namespace std;
int main(int argv, char* arg[]){
char imie[20];
cout << "Podaj swoje imie: ";
cin >> imie;
cout << "Witaj " << imie << endl;
return 0;
}
Jak kompilujemy
W konsoli
g++ program.cpp -o program
Omówienie
Warto zauważyć:
Nie jest wymagane podanie rozszerzenia biblioteki iostream.
Biblioteka iostream jest ekwiwalentem znanej z C biblioteki stdio. C++ wprowadza strumienie za pomocą których odbywają się wszystkie operacje wejścia/wyjścia.
Przekierowanie danych do/ze strumienia możliwe jest dzięki operatorom « oraz ». Obiektem strumienia odpowiedzialnym za wyświetlanie na ekranie jest cout natomiast cin jest to strumień wejściowy.
using namespace std określa, że korzystamy z przestrzeni nazw std z biblioteki iostream.
Ćwiczenia
-
[1 plus] Napisz program obliczający silnię. Silnia powinna być obliczana przez funkcję
int factorial(int);
Zdefiniowaną w katalogu factorial. Wykonaj dwie wersje funkcji:
[2 plusy] Wyświetlanie napisu wspak. Napisz funkcję która jako parametr będzie przyjmowała tablicę znaków i wyświetlała ją wspak. Funkcja powinna być rekurencyjna! Podpowiedź: przerwij rekurencję po napotkaniu zerowego znaku końcowego \0.
[2 punkty] Napisz funkcję palindrom, sprawdzającą czy podany jako parametr napis jest palindromem. Funkcja powinna zwracać true gdy napis jest palindromem, a false gdy nie jest.
Napisz proste menu posiadające dwie opcje: Wyjście i Sprawdź palindrom. Po wybraniu Sprawdź palindrom program powinien poprosić o wpisanie słowa a następnie sprawdzić i wyświetlić na ekranie czy podane słowo jest palindromem. Po wybraniu Wyjście program powinien kończyć działanie.
[2 plusy] Napisz funkcję która będzie przyjmować tablicę dwuwymiarową o rozmiarach 10 na 10 i będzie uzupełniać ją iloczynami wierszy i kolumn, tworząc tabliczkę mnożenia. Napisz osobną funkcję do wyświetlania tej tablicy.
Moduł: multiplicationtable
Pliki z implementacją: MultiplicationTable.h/cpp
Sygnatura metody:
void MultiplicationTable(int tab[][10]);
uruchomienie testów: ALT+SHIFT+F10 → lab1_multiplication_table_tests
-
Moduł: doublebasepalindromes
Pliki z implementacją: DoubleBasePalindromes.h/cpp
Sygnatura metody:
uint64_t DoubleBasePalindromes(int max_vaule_exculsive);
Pliki nagłówkowe:
#include <cstdint>
uruchomienie testów: ALT+SHIFT+F10 → lab1_double_base_palindrome_tests