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:
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.
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
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:
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:
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.
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.
W C++ dostępne są następujące instrukcje sterujace:
double potega(double);
double potega(double liczba){ return liczba*liczba; }
// NIEPOPRAWNIE void foo(int a, int b){...} double foo(int c, int d){...}
Tablice jednowymiarowe w C++ można deklarować na cztery sposoby:
int tab[100];
int tab[5] = {1,2,3,4,5};
int tab[10] = {1,2,3,4,5};
int tab[] = {1,2,3,4,5};
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]){ ... }
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; }
W konsoli
g++ program.cpp -o program
Warto zauważyć:
int factorial(int);
Zdefiniowaną w katalogu factorial. Wykonaj dwie wersje funkcji:
std::string reverse(std::string str)
#include <string>
const char *characters = str.c_str(); //uzyskanie z obiektu string wskaźnika na poszczególne znaki size_t size = str.size(); //uzyskanie z obiektu string ilości znaków //utworzenie nowego obiektu string na podstawie innego char*, char[], itp.. return std::string(reversed_characters)
bool is_palindrome(std::string str);
#include <string>
void MultiplicationTable(int tab[][10]);
uint64_t DoubleBasePalindromes(int max_vaule_exculsive);
#include <cstdint>