======Dynamiczne zarządzanie pamięcią i operacje na plikach======
//Writing in C or C%%++%% is like running a chain saw with all the safety guards removed//
Do ukończenia tego laboratorium wymagana jest znajomość wskaźników i referencji z C. Jeżeli ktoś nie czuje się w tym temacie na siłach, w laboratorium [[.:wskazniki|Wskaźniki i referencje]] przygotowane zostało krótkie przypomnienie najważniejszych informacji.
===== Refaktoryzacja kodu =====
Refaktoryzacja polega na ulepszeniu kodu bez zmiany jego zachowania. Np. zmiany nazwy funkcji w celu wyjaśnienia jej działania (zamist **bool pal(std::string s)** lepszą nazwą będzie **bool IsPalindrome(std::string single_word_phrase)**)
Refaktoryzacje mogą też przyspieszyć pisanie kodu, początkowo kod można napisać w postaci jednej dłużej metody a następnie powykrawać z niego podprocedury w celu poprawy jego czytelności i modułowości
void ScheduleAppointment(const std::string &customer, const std::string &room, const std::string &when) {
std::regex valid_customer {R"(\w+ \w+)"};
if ( !std::regex_match(customer,valid_customer) ) {
return;
}
std::regex valid_room {R"([A-Z]-\d{3})"};
if ( !std::regex_match(room,valid_room)) {
return;
}
std::regex valid_when {R"(\d{2}:\d{2})"};
if ( !std::regex_match(when,valid_when)) {
return;
}
Room *found_room = nullptr;
for (auto &r : rooms) {
if (r.Name() == room) {
found_room = &r;
}
}
...
}
po refaktoryzacji kod mógłby wyglądać następująco:
bool IsValid(const std::string &str, const std::regex &validator) {
return std::regex_match(str, validator);
}
...
bool IsValidCustomer(const std::string &customer) {
std::regex valid_customer {R"(\w+ \w+)"};
return IsValid(customer,valid_customer);
}
Room FindRoom(const std::string &room) {
for (auto &r : rooms) {
if (r.Name() == room) {
return r;
}
}
return {};
}
void ScheduleAppointment(const std::string &customer, const std::string &room, const std::string &when) {
if ( !IsValidCustomer(customer) ) {
return;
}
if ( !IsValidRoom(room)) {
return;
}
if ( !IsValidWhen(when)) {
return;
}
Room found_room = FindRoom(room);
...
}
Zapoznać się z możliwościami automatycznej refaktoryzacji pod [[https://blog.jetbrains.com/clion/2014/12/refactorings-in-clion-be-safe-and-quick/|linkiem]] do 8 min 30 s.
===== Dynamiczne zarządzanie pamięcią =====
Często zdarza się, że nie jesteśmy w stanie z góry przewidzieć ile pamięci będzie potrzebował nasz program do poprawnego działania. Na przykład w prostym kalkulatorze macierzowym nie można z góry ustalić wymiarów tablicy przechowującej macierz ani z góry ustalić ile takich tablic-macierzy będzie występować w równaniu!
Jedynym rozwiązaniem jest tworzenie nowych macierzy o ustalonych wymiarach podczas działania programu.
====Wskaźniki i alokacja pamięci====
Wraz z dynamiczną alokacją pamięci idą w parze wskaźniki. Pamięć zawsze alokowana jest pod jakimś adresem. A zmienne przechowujące adres i pozwalające się dostać do danych w niej przechowywanych, to właśnie wskaźniki.
===Operator new ===
Operator **new** jest ekwiwalentem znanej z C funkcji //malloc// ale dużo potężniejszym. Wynikiem zwracanym przez operator **new** jest wskaźnik do zaalokowanej pamięci
Przykład dynamicznego tworzenia zmiennych:#include
using namespace std;
int main(void){
char* ptr = new char; //Stworzy zmienną typu char
int* tab = new int[10]; //Stworzy tablicę typu int o rozmiarze 10
// NIEPOPRAWNIE!!!
int** tab2d = new int[10][10];
}
===Operator delete ===
Operator **delete** działa podobnie jak funkcja //free// znana z C. Pamięć w momencie kiedy nie jest już wykorzystywana przez program powinna być zwolniona.
Przykład dynamicznego tworzenia i usuwania zmiennych:#include
using namespace std;
int main(void){
char* ptr = new char; //Stworzy zmienną typu char
int* tab = new int[10]; //Stworzy tablicę typu int o rozmiarze 10
// Tutaj znajdują się operacje na zmiennych
delete ptr;
delete [] tab;
// NIEPOPRAWNIE!!
// Jeśli tab2d jest poprawnie zaalokowaną tablica dwuwymiarową
// nie można zwolnić pamięci w następujący sposób
delete [][] tab2d;
}
Niestety należy pamiętać o tym by zwolnić pamięć przydzieloną inaczej uzyskujemy wyciek pamięci i program w trakcie działania zaczyna zużywać coraz więcej pamięci operacyjnej mimo, że już jej i tak nigdy nie użyje! Sprawa też nie jest taka prosta, bo nie zawsze wiadomo, czy pewne elementy nie są już na pewno przez nikogo używane.
=== Brak wartości ===
W C%%++%%11 nie używamy już makra **NULL** zostało dodane specjalne słowo kluczowe reprezentujące pusty wskaźnik **nullptr**
int *v = nullptr;
delete v;
=====Ale jest nadzieja - zautomatyzowane zarządzanie pamięcią część pierwsza=====
Jedną z możliwości automatycznego zarządzania pamięcią jest oddelegowanie tego zadania do kontenerów zdefiniowanych w bibliotece standardowej,
które automatycznie zwolnią pamięć jeśli przestanie ona być używana.
==== Vector ====
Jednym z takich kontenerów jest tablica rozszerzalna [[http://en.cppreference.com/w/cpp/container/vector|vector]], zapoznać się z dokumentacją
uruchomić przykłady podane w dokumentacji przez **run code** i przyglądnąć się dokumentacji funkcji:
* emplace_back
* pop_back
* clear
Przenalizować poniższy kod:
#include
#include
using std::cout;
using std::endl;
using std::vector;
void PrintVector(const vector &v) {
bool first = true;
for(auto n : v) {
if (!first) {
cout<< ", ";
} else {
first = false;
}
cout << n;
}
cout< v = {7, 5, 16, 8};
cout << "initilized with 4 numbers"<
[[http://coliru.stacked-crooked.com/a/056089e192222291|Run code]]
==== Map ====
[[http://en.cppreference.com/w/cpp/container/map|Tablica asocjacyjna]], która pozwala zaadresować dowolny element innym typem, niekoniecznie tylko liczbą naturalną.
#include
#include