Biblioteka wyrażeń regularnych została dołączona do standaradu C++11 i jest dostępna w pliku nagłówkowym:
#include <regex>
Utworzenie nowego wyrażenia regularnego jest jeszcze prostsze z surowymi łańcuchami znaków (w których nie interpretuje się ukośnika jako znaku specjalnego)
regex pattern {R"((\w+)\s+(\d{1,3}))"};
Powyższe wyrażenie regularne dopasuje się do wyrazu składającego się z co najmniej jednej litery \w, po którym następuje odstęp złożony z co najmniej jednego białego znaku \s i wreszcie ciągu składającego się z od 1 do 3 cyfr \d. Dodatkowo zostały zastosowane grupy przechwytujące (kolejne grupy to kolejne nawiasy okrągłe), dzięki którym można się odnieść do fragmentów dopasowania.
regex pattern {R"((\w+)\s+(\d{1,3}))"}; string line {"Adam 789"}; smatch matches; if (regex_match(line, matches, pattern)) { cout<<"udało się dopasować do linii: "<<line<<endl; cout<<"zerowa grupa przechwytująca to całe dopasowanie: "<<matches[0]<<endl; cout<<"pierwsza grupa przechwytująca to napis: "<<matches[1]<<endl; cout<<"druga grupa przechwytująca to liczba: "<<matches[2]<<endl; }
Oprócz regex_match, dostępne są też funkcje regex_search i regex_replace. Zapoznać się z dokumentacją wyrażenia regularne
W C++11 została wprowadzony dodatkowy kontener implementujący statyczną tablicę w pliku nagłówkowym
#include <array>
Deklaracja nowej tablicy:
//inicjalizacja tablicy intów o rozmiarze 3 za pomocą zadanych wartości: array<int, 3> tab {1,2,3}; //alternatywnie jednorodna inicjalizacja tablicy boolów o romiarze 10: array<bool, 10> flags; flags.fill(false);
Po tablicy można iterować bez problemów:
for (auto v : tab) { cout << "value: " << v << endl }
Zbiór, czyli kolekcja niepowtarzalnych elementów. Dostępna jest po dołączeniu:
#include <set>
Przykład użycia zbioru:
set<int> s {4,5,6,7}; if (s.find(5) != s.end()) { cout<<"5 jest elementem zbioru"<<endl; } if (s.find(17) != s.end()) { cout<<"17 jest elementem zbioru"<<endl; } else { cout<<"17 nie jest w zbiorze"; }
W przypadku użycia zbioru, sprawdzenie, czy element znajduje się w zbiorze ma złożoność logarytmiczną w przeciwieństwie do liniowej złożoności wyszukiwania w tablicy. Dokumentacja do zbioru.
Za pomocą słowa kluczowego using w C++11 można wprowadzić kilka różnych definicji.
using ::std::unique_ptr;
#include <string> using Url = std::string; bool IsValid(const Url &url);
using namespace std;
Zapoznać się z tekstem Smart Pointers. Inteligentne wskaźniki automatycznie zarządzają dostępem do powierzonej im pamięci i gdy pamięć nie jest już używana zostaje automatycznie zwolniona. Opisane poniżej funkcjonalności weszły w standardzie C++14.
Najprostszy z inteligentnych wskaźników. Jedyny właściciel pamięci, gdy zmienna traci zakres pamięć jest automatycznie zwolniona. Definicja znajduje się w pliku nagłówkowym
#include <memory>
Definiowanie wskaźnika i przydzielenie mu nowego obiektu Type:
unique_ptr<Type> p = make_unique<Type>();
Można też zapisać to krócej z wykorzystaniem auto:
auto p = make_unique<Type>();
Wskaźnika można używać jak zwykłego wskaźnika:
unique_ptr<Type> p = make_unique<Type>(); cout<<p->value;
Unikalny wskaźnik jest jedynym właścicielem pamięci, dlatego jeśli chcemy przypisać obiekt unique_ptr do innego musimy jawnie przenieść element:
unique_ptr<int> origin = make_unique<int>(3); //Błąd kompilacji: unique_ptr<int> new_owner = origin; //Przeniesienie odpowiedzialności: unique_ptr<int> real_new_owner = move(origin); if (oring == nullptr) { cout<<"W tym momencie utworzony obiekt o wartości 3 już nie należy do origin"<<endl; }
Wtedy funkcja, która jako parametr przyjmuje unique_ptr rząda ona tak na prawdę przekazania zarządzania nad obiektem:
unique_ptr<int> GiveMeItForASec(unique_ptr<int> p) { ++(*p); return p; } void foo() { auto ptr = make_unique<int>(99); //BLAD KOMPILACJI: auto new_ptr = GiveMeItForASec(ptr); //OK auto new_ptr = GiveMeItForASec(move(ptr)); //TERAZ ptr nie zawiera nic }
Wciąż jednak można pozostawić właściciela w spokoju i przekazać unique_ptr przez stałą referencję lub wskaźnik (druga opcja brzmi dziwnie, ale ujednolica konwencję, patrz niżej).
Innym typem wskaźnika jest współdzielony wskaźnik, którego można współdzielić z innymi obiektami tego typu i pamięć zostanie zwolniona w momencie, gdy ostatni wskaźnik przestanie istnieć. Jest to możliwe, dzięki zliczaniu ilości odwołań do wskazanego fragmentu pamięci, które się odbywa automatycznie.
shared_ptr<int> p = make_shared<int>(78); //poniższa linia jest poprawna shared_ptr<int> another = p; //W tym momencie oba wskaźniki pokazują na ten sam obszar pamięci, a wewnętrzny licznik odwołań wynosi 2
Minusem wykorzystania współdzielonych wskaźników inteligentnych jest większe zużycie pamięci (gdzieś musi być przechowany licznik odwołań), dlatego z tych wskaźników zaleca się korzystać w ostateczności jeśli zależy nam na minimalizowaniu zużycia pamięci.
Z drugiej strony jeśli nie zależy nam na pamięci, a z jakiegoś dziwnego powodu wciąż chcemy pisać aplikację w C++ , wykorzystanie shared_ptr w większości przypadków powinno rozwiązać problemy z wyciekami pamięci.
Ten wskaźnik jest dodany w celu rozwiązania problemu cyklicznych odwołań współdzielonych wskaźników wzajemnie na siebie (wtedy licznik odwołań nigdy nie spadnie do zera i pamięć nigdy nie zostaje zwolniona). Dokładniejszy opis w tekście podanym w literaturze.
Przestrzenie nazw porządkują definicje symboli i pozwalają wykluczyć konflikty pojęć z różnych kontekstów przypadkowo tak samo nazywające się. Symbole to nazwy metod, nazwy zmiennych globalnych, nazwy struktur i klas, nazwy typów, itp…
Definicja nowej przestrzeni nazw może wyglądać następująco:
#ifndef MODULE_H #define MODULE_H #include <string> namespace mymodule { std::string ToString(int value); } #endif // MODULE_H
#include <string> #include <sstream> #include "Module.h" namespace mymodule { using ::std::string; using ::std::stringstream; string ToString(int value) { stringstream ss; ss<<value; return ss.str(); } }
#include "Module.h" int main() { auto str = mymodule::ToString(100); }
Typ pary i krotki stanowi w C++11 odpowiednik anonimowej struktury, którą można użyć w dowolnym miejscu kodu, bez potrzeby jej definiowania. Np.
std::pair<std::string, int> p {"abc",17}; std::tuple<std::string,double,std::string> t3 {"Mam",17.3,"lat"};
W celu poprawy czytelności kodu, można wykorzystać using np.
using Carry = bool; using Code = char; std::pair<Carry,Code> NextCode(Code c);
Argumenty do funkcji można przekazywać w C++ na wiele różnych sposobów, natomiast należy brać pod uwagę czytelność zarówno kodu klienta (wywołujący metodę), jak i kod implementujący metodę. Stąd na laboratoriach będziemy korzystać z następującej konwencji:
std::string ToString(const Type &t);
std::string ToString(bool b);
std::pair<bool, char> NextChar(char c);
void Modify(Type *t);
Konwencja jest znacznie czytelniejsza od zastosowania niestałej referencji z punktu widzenia klienta:
Type t; Modify(&t);
Kod przekazuje jawnie adres co sygnalizuje możliwość zmodyfikowania obiektu przez funkcję.
std::unique_ptr<TinyUrlCodec> Init(); void NextHash(std::arrray<char, 6> *state); std::string Encode(const std::string &url, std::unique_ptr<TinyUrlCodec> *codec); std::string Decode(const std::unique_ptr<TinyUrlCodec> &codec, const std::string &hash);
#include <utility> #include <string> #include <array> #include <memory>
unsigned int ToMinutes(std::string time_HH_MM); unsigned int MinimalTimeDifference(std::vector<std::string> times);
#include <vector> #include <sstream> #include <regex> #include <cmath>
std::unique_ptr<Counter> Init(); void Inc(std::string key, std::unique_ptr<Counter>* counter); int Counts(const std::unique_ptr<Counter> &counter, std::string key); void SetCountsTo(std::string key, int value, std::unique_ptr<Counter> *counter);
#include <string> #include <memory> #include <map>
std::unique_ptr <SmartTree> CreateLeaf(int value); std::unique_ptr <SmartTree> InsertLeftChild(std::unique_ptr<SmartTree> tree, std::unique_ptr<SmartTree> left_subtree); std::unique_ptr <SmartTree> InsertRightChild(std::unique_ptr<SmartTree> tree, std::unique_ptr<SmartTree> right_subtree); void PrintTreeInOrder(const std::unique_ptr<SmartTree> &unique_ptr, std::ostream *out); std::string DumpTree(const std::unique_ptr<SmartTree> &tree); std::unique_ptr <SmartTree> RestoreTree(const std::string &tree);
#include <ostream> #include <string> #include <memory>