13 zut STL containers,algorithms, utilities, smart pointers

I - Kontenery
       vector
       array
       lista
       deque
       map

II - Iteratory
      input
      output
      forward
      bidirectional
      random
      std::advance
      std::distance
      back inserter (front)

III - Algorithms
      for_each  (niemodyfikujace)
      transform
      count/ if
      min/max
      find
      equal/mismatch
      copy   (modyfikjacy)
      transform v2
      fill_n
      generate_n
      replace
      remove
      unique
      reverse
      rotate
      next_permutation
      random_shuff;e
      sort (sortujace)
      accumulate
    
IV - I/O STREAM
     stream
     file i/o

V - Utility
      std::pair
      std::tuple
      smart pointers



I
Kontenery

dostęp sekwencyjny:
array - zwykła tablica z C, statyczna, dodane metody
vector - najczęsciej używany, zwany dynamiczną tablicą z metodami
lista - elementy powiązane(linked) latwo wstawić w srodek, trzeba iterowac iterator zeby znalezc
deque - wektor wektorów, spis vektorów, tylko podvektory są w ciągłej pamięci

 szybkie przeszukiwanie:
map - zbiór par(x1-y1,x2-y2)-podajemy klucz, otrzymujemy dane
set - drzewo (kolekcja wartości) wolno wklada-szybko zwraca (odwrotnie niż vektor)

multiset - kolekcja kluczy
multimap - kolekcja par klucz-wartosc


Adaptors

stack - adapts a container to provide stack (LIFO ) (stos książek)
queue - adapts a container to provide queue (FIFO) (kolejka)
priority_queue - heap

kontenery end

Iteratory
Kontenery używają iteratorów żeby uzyskać dostęp do elementów

std::vector<int>::iterator it;         //obsluguje wszystkie vektory int;
it=mojvektor.begin();
albo
auto it=mojvektor.begin();

*it - zwraca wartość na jaką wskazuje iterator

int a=std::distance(moj.begin(), it );     - pokaże który z kolei jest dany iterator

Iteracja po kontenerze;

for (auto element : container) 
{
    iteruje kontener elementem
}

powyższe jednak tworzy kopie, lepiej iterować po referencji

for (auto& element : container )


Rodzaje iteratorów:
InputIterator
OutputIterator
ForwardIterator
BidirectionalIterator
RandomAccesIterator

Ważne, jak z powodu zmiany rozmiaru vektor zostanie przeniesiony, iterator traci wskazanie.



                                                              dostęp sekwencyjny:

Vektor

Tworzenie:

std::vector<int> v1 (10, 15);                   //wielkosc 10 w kazdym wstawi 15
std::vector<int> v2 (10);                       //wielkosc - opcjonalne 
std::vector<int> v3 (v1.begin(), v1.end()-1);

std::vector<string> v4 ( 20, "przykladowy tekst" );
std::vector<float> f1 { 1.0, 2.3, 3.3, 4.4}; //konkretne wartosci


Gdzie alokuje:

vector<Type> vect;
samą zmienna na stosie, ale elementy vektora na stercie


vector<Type> *vect = new vector<Type>;
wszystko na stercie





Tworzenie w klasie:

template<typename T,int N>
class Ringbuffer
{

std::vector<T> myvec;                   //vektor deklaracja
typename std::vector<T>::iterator it;   //iterator deklaracja

public:


Ringbuffer()                            //konstruktor
{
    myvec.assign(N,0);                  //vektor inicjalizacja 
    it=myvec.begin();                   // iterator inicjalizacja
}


template <typename T1,int N1>           //konstruktor kopiujacy musi miec
Ringbuffer (const Ringbuffer<T1,N1>& another){}  //wlasny template

};

w obszarze zmiennych deklaracja, w konstruktorze inicjalizacja

przy okazji jeśli klasa templatowa to konstruktor kopiujący musi mieć wlasny template jak powyzej.



typ przypisany do vektora musi być: kopiowalny, przenoszalny, automatycznie konstruowalny, usuwalny



Inicjalizacja vectora w  C++ (5 different ways)

-Initializing by one by one pushing values

    vector<int> vect;
   
    vect.push_back(10);
    vect.push_back(20);

-Specifying size and initializing all values :

    vector<int> vect(n, 10);

-Initializing like arrays :

    vector<int> vect{ 10, 20, 30 };

-Initializing from array :

    int arr[] = { 10, 20, 30 };
    int n = sizeof(arr) / sizeof(arr[0]);

    vector<int> vect(arr, arr + n);

-Initializing from another vector :
 
    vector<int> vect1{ 10, 20, 30 };
    vector<int> vect2(vect1.begin(), vect1.end());



Dodawanie elementów:

można dodawać na początek i na konieć vektora (co go poszerza i zmienia iteratory)

mojv.push_back(10);      lub push_front


ale moża zastosować insert lub emplace , w miejsce iteratora wstawi wartość, ale nie zastąpi tylko przesunie resztę, jeśli it był początkiem, to wstawi na początek a dotychczasowy pierwszy będzie drugi (jeden i drugi)

mojv.insert(it,10);
mojv.emplace(it,10);

jeśli vektor był pusty nie zadziała, jeśli był stworzony tylko jako
vector<int> mojv ;

to musimy albo resize (i wypełni zerami lub przypadkowymi wartosciami

mojv.resize(10);              tym można też zmniejszyć

albo

myvec.assign(10,0);       (rozmiar, czym ma wypełnić ) też może zmniejszyć

CO WAŻNE OPERACJE MOGĄ UNIEWAŻNIĆ ITERATORY

Czym się różnią?
Emplace takes the arguments necessary to construct an object in place, whereas insert takes (a reference to) an object.
insert copies objects into the vector.
emplace construct them inside of the vector.

Można również zrobić dereferencję iteroatora i zamienić wpisaną tam wartość 
it=mojv.begin();
*(it+1)=100;


GENERALNIE JEŚLI DO ITERATORA DODAMY WARTOŚĆ WIĘKSZA NIŻ ROZMIAR ITERATORA I SPROBUJEMY ZAPISAC *(it)  TO NADPISZEMY PAMIĘĆ

DLATEGO LEPIEJ UZYWAĆ DOSTĘPU PRZEZ  AT

mojvector.at(liczba);


#include<iostream>
#include<vector>

using namespace std;
vector<int> mojv {1,2,3,4,5,6,7,8};

int main ()
{

vector<int>::iterator it;
it=mojv.begin();

cout<<endl<<"vektor po stworzeniu 1-8"<<endl;
for (auto element : mojv)
    cout<<element<<endl;

cout<<endl<<"proba std::advance iteratora poza zakres"<<endl;
std::advance(it,20);
if(it>mojv.end())
    cout<<"za duzy"<<endl;
else
    cout<<"poprawny"<<endl;
cout<<"wyświetlam"<<endl;
cout<<*(it)<<endl;
int aa= std::distance(mojv.begin(),it);
cout<<"dystans= "<<aa<<" rozmiar vectora byl= "<<mojv.size()<<endl;


//insert copies objects into the vector.
//emplace construct them inside of the vector.

it=mojv.begin();
//std::advance(it,7);
it=mojv.emplace(it+2,98); //emplace niszczy wskazanie iteratora ALE zwraca iterator na nowo wstawiony
cout<<endl<<"iterator+2 po emplace 98= "<<*(it)<<" ,a vector:"<<endl;
for (auto element : mojv)
    cout<<element<<endl;
int bb= std::distance(mojv.begin(),it);
cout<<"dystans= "<<bb<<" rozmiar vectora byl= "<<mojv.size()<<endl;


it=mojv.begin();
mojv.insert(it+2,99);  //insert nie zmienia wskazanie iteratora
cout<<endl<<"iterator+2 po insert 99= "<<*(it)<<" ,a vector:"<<endl;
for (auto element : mojv)
    cout<<element<<endl;


//it=mojv.begin();
//*(it+1)=100;
mojv.at(9)=100;
cout<<endl<<"po vector.at(9)=100"<<endl;
for (auto element : mojv)
    cout<<element<<endl;

//TYLKO vector.at(liczba)   nie pozwala na dostęp POZA ZAKRESEM

return 0;
}




Przykład 3 konstruktorów i ich wywoływania:

#include <vector>
#include <iostream>

using namespace std;

class A
{
    int number;
    int number2;
public:
    A(int _number):number(_number),number2(0)
    {
        cout<<"Normal constructor\n";
    }
    A()
    {
        cout<<"Default constructor\n";
    }
    
    A(const A& source)
    {
        number = source.number;
        number2 = source.number2;
        cout<<"Copy constructor\n";
    }

    A & operator=(const A& source)
    {
        number = source.number;
        number2 = source.number2;
        cout<<"Assignment operator\n";
        return *this;
    }
};

int main()
{
    vector <A> v1(1); //(1)
    v1.push_back(1); //(2)
    v1[0]=10;        //(3)
    return 0;
}


Output:
Default constructor ****(1)
Normal constructor ****(2)
Copy constructor     ****(2)
Copy constructor     ****(2)
Normal constructor ****(3)
Assignment operator  ****(3)


Metody







Array

Tworzenie

array<typ,wielkosc> nazwa ;


Metody:

Iterators


Capacity


Element access


Modifiers



Non-member functions

lexicographically compares the values in the array
(function template)
accesses an element of an array
(function template)
specializes the std::swap algorithm
(function template)



Lista

Tworzenie

list<typ>  nazwa;

niemożliwy dostęp przez  []

iterator trzeba iterować, żeby dostać się do kolejnych elementów.


te same metody co Vektor


przykład porownania prędkości vektor - lista  i metody push back - find - lista
LINK git


Deque

Jeśli wsadzamy vektory jako elementy, to tylko one będą zajmowały ciągłą pamięć, całe deque będzie się składać z sumy ciągłych elementów



                                                                szybkie przeszukiwanie:

Map inaczej słownik

zbiór par, klucz wartość, wstawianie wolne ale szybkie szukanie (drzewo się samo balansuje)
tylko 1 klucz na mapę i musi być porównywalny, dane jak w vektorze
złożoność obliczeniowa szukania lepsza niżliniowa (log N)

Tworzenie

map<typklucza,typdanych> nazwa={{1,"slowo"} , {2,"slowo2"},...

trzecim elementem w < > jest komparator do porównywania większe mniejsze, można go zmienić jak potrzeba, ale jak inny do nie pasuje do innych map bez takiego.


przy wyświetlaniu rangeforem:


for(auto element : MyMap )
    cout<< element.second;

.second - wyświetli tylko dane bez klucza

Klasy składowe

porównuje obiekty typu value_type
(klasa)

Metody

Konstruuje map
(publiczna metoda)
Niszczy map
(publiczna metoda)
przypisuje wartości do kontenera
(publiczna metoda)
zwraca skojarzony alokator
(publiczna metoda)
Dostęp do elementów
(C++11)
dostęp do wskazanego elementu, ze sprawdzeniem zakresów
(publiczna metoda)
dostęp do wskazanego elementu
(publiczna metoda)
Iteratory
zwraca iterator na początek kontenera
(publiczna metoda)
zwraca iterator za koniec kontenera
(publiczna metoda)
zwraca odwrócony iterator na początek
(publiczna metoda)
zwraca odwrócony iterator za koniec kontenera
(publiczna metoda)
Pojemność
sprawdza, czy kontener jest pusty
(publiczna metoda)
zwraca liczbę elementów
(publiczna metoda)
zwraca maksymalną możliwą liczbę elementów
(publiczna metoda)
Modyfikatory
czyści zawartość
(publiczna metoda)
wstawia elementy
(publiczna metoda)
(C++11)
konstruuje element "w miejscu"
(publiczna metoda)
(C++11)
tworzy elementy "w miejscu", korzystając z podpowiedzi
(publiczna metoda)
usuwa elementy
(publiczna metoda)
zamienia zawartość
(publiczna metoda)
Przeszukiwanie
zwraca liczbę elementów o podanym kluczu
(publiczna metoda)
znajduje element ze wskazanym kluczem
(publiczna metoda)
zwraca przedział elementów o podanym kluczu
(publiczna metoda)
zwraca iterator na pierwszy element nie mniejszy niż dany klucz
(publiczna metoda)
zwraca iterator na pierwszy element większy niż dany klucz
(publiczna metoda)
Obserwatory
zwraca funkcję porównującą klucze
(publiczna metoda)
zwraca funkcję porównującą klucze obiektów typu value_type
(publiczna metoda)


Przykład:
Stwórz słownik który zapisuje słowa a jako klucz daje hashe do nich. Wyszukiwanie następuje po odtworzeniu hasha i szukaniu po nim (bo słowa są tylko danymi a szukamy kluczem:


#include<iostream>
#include<map>

using namespace std;


struct Dictionary
{
    
    std::map<int,string> MyMap;
    
    void insert(const std::string& word)
    {
    int temphash=Hash(word);
    MyMap.emplace(temphash,word);

    }

    bool find(const std::string& word) const
    {
    
    int temphash=Hash(word);

    if( MyMap.find(temphash)!=MyMap.end() )
        return true;
    else
        return false;
      
    }

    bool empty() const;

    int Hash(const std::string& key) const
    {
    int _value = std::hash<std::string>{}(key);
                
        return _value;
    }
};



int main ()
{

Dictionary mojslownik;

mojslownik.insert("dupa");


for(auto element : mojslownik.MyMap )
    cout<< element.second;

cout<<endl;


cout<<mojslownik.find("dupa")<<endl;

    
return 0;
}


Set

Kolekcja kluczy, sortowana po kluczach, wolno dodaje, szybko szuka

#include<iostream>
#include <map>
#include <set>
#include <iterator>
#include <algorithm>
#include <string>

using namespace std;

int main()
{
    set<int> s1;
    //set<int>::iterator it=s1.begin();   //niepoprawne umiejscowienie iteratora

    s1.insert(2);
//set<int>::iterator it=s1.begin();      //jeśli tu, to insert(1) sprawi, że iterator
    s1.insert(5);                        //dalej bedzie pokazywal na 2, ale widzi dalsze pozycje!!!

    s1.insert(3);

    s1.insert(6);

    s1.insert(4);

    s1.insert(8);

    s1.insert(7);

    s1.insert(1);

set<int>::iterator it=s1.begin();    //najlepiej na koncu

    cout << "Elements in set:\n";
    for (auto el : s1)
        cout << el << " "; // Sorted

it++;   //sposob zwiekszania iteratora (nie dziala it+5) ale dziala advance
it++;
it++;
it++;
it++;
it++;


    cout<<endl;
    cout<<"iterator:"<<*(it)<<endl;

    std::advance(it,-5);

    cout<<"iterator po advance:"<<*(it)<<endl;

 return 0;
}

wyswielta:
Elements in set:
1 2 3 4 5 6 7 8
iterator:7
iterator po advance:2



co ciekawe operacje robione po iteratorze, np sl.erase(2) , czyli usunięcie elementu '2', sprawi, ze iterator będzie aktualny i bedzie potrafil wyswietlic

1 3 4 5 6 7 8
iterator:8
iterator po advance:3

II

Iteratory 




Input iterator: can read from the pointed-to element (DO ODCZYTU ELEMENTOW)
*
->
++iter/iter++
==, !=
TYPE(iter)
ma zdefiniowaną POST i PRE INKREMENTACJĘ


Output Iterator is an Iterator that can write to the pointed-to element (DO ZAPISU)
*
++iter/iter++
TYPE(iter)
std::ostream_iterator
std::ostreambuf_iterator
std::insert_iterator
std::back_insert_iterator
std::front_insert_iterator


Forward Iterator, like input&output but it can be used in multipass algorithms.


Bidirectional like Forward but:  --iter/--iter


Random access iterator(only vector, String, Array)
[]
+=n, -=n
iter = iter + n, iter = iter -n
iter-iter, iter > iter, <, <=, >=


polecenia:
std::advance - przesuwa iterator listy np o 50, szybko

albo np: 
 std:advance (std::end(kontener),-5)     i mamy iterator 5 przed koncem

std::distance - distance between two iterators
int a=std::distance(moj.begin(), it );     - pokaże który z kolei jest dany iterator



Zad:
Znajduje element w vektorze i drukuje index pod jakim się znajduje:

#include<iostream>
#include<vector>

using namespace std;

vector<int> mojv {1,2,3,4,5,6,7,8};

int main ()
{

vector<int>::iterator it;
it=mojv.begin();
int aa;

for (unsigned int i=0;i<=mojv.size();i++)
{
    if(*(it++)==6)
       aa= std::distance(mojv.begin(),it);
}
cout<<aa<<endl;

return 0;
}




(back|front)_insert iterators

front inserter inserts a value at front of the container (list, deque)

back inserter inserts a value at the end o the container (vector, deque, lists, strings)
przypisany do iteratora ustawia nas na końcu.

"sprytny" iterator który po przypisaniu sam rozszerza kontener.
przykład poniżej w algorytmie transform, gdzie przypisuje z jednego do drugiego kontenera i ten drugi (nowy jest pusty, zwykły iterator problem, back inserter sprawi, ze jak przypisze to rozszerzy,



wstęp do algorytmów:

Predicates
-Free function
-template function
-Function object
-lambda

do predykatów stosujemy praktycznie tylko lambdę

LAMBDA -funkcja zdefiniowana w miejscu jej wywołania, zaczyna się capture list czyli []

[&] - referencje do wszystkich objektow
[=] - kopie do wszystkich obiektow
[ ] - lapie kontekst (closure costam)
[] - niczego nie lapie

jeśli w Capture List [ ] mamy &s, przekazuje przez referencję zmienną s która jest poza linią w której definiujemy lambdę
ale równie dobrze mogło by być samo [ &] ale wtedy daje dostęp do wszystkich obiektów

lambdas!
VERY easy to implement
Performs almost always better!
Should be used by default!

przykłady lambdy w algorytmach, gdzie ostatni argument - predykat - zawsze załatwiany lambdą.

EXPLICIT  Przy oznaczeniu konstruktora jako explicit, nie będzie przeprowadzana automatyczna konwersja przy pomocy tego konstruktora czyli Explicit nie pozwala na wywolanie takie:

A a = 5; (dla jednego argumentu)

można jednak tak:


A a(5);


                                                                        III
                                         Algorithms

Typy algorithms:
nonmodifying
modifying
removing
mutating
sorting
numeric

                 niemodyfikujące, niektóre mogą modyfikować, ale nie zaleca się

FOR_EACH (niemodyfikowalny) iteruje i zmienia predykatem  (zwraca unaryfunction)

for_each(InputIt first, InputIt last, UnaryFunction f)


inputIt first   i last to iteratory początku i konca  zakresu ktory ma  for_eachować
UnaryFunction to predykat (funkcja która wykona dzialanie "dla każdego"

#include<iostream>                                                                                                                                                                                  
#include<vector>
#include<algorithm>

using namespace std;
 
vector<int> mojv{1,2,3,4,5};
auto it=mojv.begin();


void print( const std::vector<int>& v) {
 std::cout<<"[";
 std::for_each(v.begin(),v.end()-1, [](int vv) {std::cout<<vv; std::cout<<",";    });
 std::cout<<(*v.end()-1);
 std::cout<<"]";
}
  
  
int main ()
{
print(mojv);

return 0;
}


w przykladzie powyżej zakres  end jest zmiejszony o 1   v.end()-1 ponieważ nie chcemy wyświetlić ostatniego elementu  (za nim ma nie być przecinka).

co robi nasz predykat  (tu LAMBDA)
przyjmuje z for_eacha  inty vv  i po kolei coutuje je   z dodanym przecinkiem

następnie za for_eachem, wyswietlamy ostatnią wartość i zamknięcie nawiasu ]


Funkcja uper która zamienia na duże litery, co ważne funkcja zwraca nowy string (można zrobić wersję która nic nie zwraca ale za pomocą referencji zmienia oryginalny)
tutaj tworzymy wewnątrz funkcji nowy string ss, ale żeby przekazać ss do predykatu (tu lambdy) musimy w Capture List [ ]  podać zmienna ss z referencją
w ciele Lambdy { } dodaje do ss kolejne elementy iterowane for_eachem przez string s (tu mojs)

include<iostream>
#include<vector>
#include<algorithm>
#include<string>

using namespace std;

std::string mojs="moj napis";

string upper(const std::string &s)
{
    std::string ss;
    std::for_each(begin(s),end(s), [&ss](char c) { ss+=::toupper(c); });
return ss;
}


int main ()
{
 
std::cout<<mojs<<std::endl;

std::cout<<upper(mojs)<<std::endl;

return 0;
}

wersja która modyfikuje bezposrednio dostarczany string:

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>

using namespace std;

std::string mojs="moj napis";

void upper(std::string &s)
{

    std::for_each(begin(s),end(s), [&s](char c) { s+=::toupper(c);  });

}


int main ()
{

std::cout<<mojs<<std::endl;

upper(mojs);

std::cout<<mojs<<std::endl;

return 0;
}

jak widać w Capture List [ ] mamy &s, ale równie dobrze mogło by być samo [ &]

Jeśli chcemy przekazać do lambdy wszystkie zmienne przez referencję ale jedną przez kopię to
[&,i]







TRANSFORM  zmienia jeden w drugi   (zwraca OutputIt)


transform(InputIt first1, InputIt last1, OutputIt d_first, UnaryOperation unary_op)


InpitIT first   last  - zakres po ktorym  iteruje (iteratory   np  begin  - end
można pisać  v.begin()  v.end()  co zwraca iterator początku i konca, ale i  begin(v) albo end(v)    )

OutputIt to iterator nowego kontenera (zawsze innego)  innego (albo tego samego) TYPU

przykład transformacja z jednego stringa do drugiego (czyli inne stringi ten sam typ)
przykład ze zmiana na duże litery, ale tym razem za pomocą transform

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>

using namespace std;

std::string mojs="moj napis";

string upper(const std::string &s)
{
std::string nowy;
auto it=begin(nowy);
    std::transform(begin(s),end(s), std::back_inserter(nowy),[](char c) { return ::toupper(c);  });
return nowy;
}


int main ()
{

std::cout<<mojs<<std::endl;

std::cout<<upper(mojs)<<std::endl;

return 0;
}

Co ważne w tym przykładzie predykat jak widać zwraca (ma return) w for_each nie miał returna
(albo w 1 przykłądzie tylko cout, albo w 2 przykłądzie zmieniał przez referencję to co dostał)
tu musi zwrócić żeby przekazać do OutputIT, można dodać informację  o zwracanym typie, albo zostawić samo return ze zwracanym typem (wystarczy).
ponieważ zwraca do outputiteratora, a stworzony string (nowy) jest pusty stosujemy  back_inserter który dodaje na koniec i powiększa (gdybyśmy chcieli stosować sam iterator, kontener musiał by mieć już określoną wielkość)
przykład:

string upper(const std::string &s)
{
std::string nowy(10,' ');
auto it=begin(nowy);
    std::transform(begin(s),end(s), it,[](char c) { return toupper(c);  });
return nowy;
}







COUNT/ COUNT_IF   mogą modyfikować, ale z zasady nie powinny. Pierwszy zlicza jak określona wartość jest spełniona, drugi ma predykat który daje warunki

count(InputIt first, InputIt last, const T& value)

count_if(InputIt first, InputIt last, UnaryPredicate p)

przykład z predykatem który zwraca TRUE jak parzyste, wynik to zliczona ilość true (parzystych)

#include<iostream>
#include<vector>
#include<algorithm>


using namespace std;

vector<int> mojva={1,2,3,4,5,6,7,8,9,10,11};


int odd(const std::vector<int> &v)
{

return count_if(begin(v),end(v), [](int ii){ return ii%2 == 0;  } );

}



int main ()
{

cout<<odd(mojva)<<endl;

return 0;
}


MIN/MAX 
wylicza najmniejsza lub największą wartość
może mieć wersję z predykatem (funkcją z warunkami) ale wtedy nie nazywa się ...._IF tylko
dodaje się tą funkcję

min_element(ForwardIt first, ForwardIt last, Compare comp)

przykład bez predykatu:
#include<iostream>
#include<vector>
#include<algorithm>


using namespace std;

vector<float> mojva={-1,-2,-3,4,3,2};


float minimum(std::vector<float> v)
{
auto it=std::min_element(begin(v),end(v) ); 
 
return *it;
}


int main ()
{

    cout<<minimum(mojva)<<endl;


return 0;
}

wersja z predykatem , elementy w nawiasie ( )  pierwszy ostatni najmniejszy, tu st,  drugi aktualnie porównywany, tu ch.
Trzeba dać return żeby wiedział na jaki warunek ma znajdować minimum
tu wstawione szukanie wartości bezwzględnej czyli najmniejsze w abs jest -1 a nie -3.

#include<iostream>
#include<vector>
#include<algorithm>


using namespace std;

vector<float> mojva={-1,-2,-3,4,3,2};


float minimum(std::vector<float> v)
{
auto it=std::min_element(begin(v),end(v),[](float st,float ch) { return abs(st)<abs(ch);     }  ); 
 
return *it;
}


int main ()
{

    cout<<minimum(mojva)<<endl;


return 0;
}
.



FIND
szuka, zwraca podany iterator InputIT last jeśli NIE znalazł
(i zwraca jak znajdzie, iterator na następny za znalezionym!!!!)

find(InputIt first, InputIt last, const T& value)



Implement a function that

works on std::vector<int>
finds all occurrences of N
return a vector of indexes of N's

Rozwiązanie:

#include<iostream>
#include<vector>
#include<algorithm>


using namespace std;

vector<int> mojva={1,2,3,4,3,2,10,3,55,3,128};


std::vector<int> znajdz(std::vector<int> &v,int ii)
{
unsigned int licz=0;
std::vector<int> tempv;

while(licz<(v.end()-v.begin()))
{
auto it=std::find_if(v.begin()+licz,end(v),[ii](int i ){  return i==ii;  } ); 
licz=it-v.begin();
if(licz<(v.end()-v.begin()))   // check if not las 
    tempv.push_back(licz);
licz+=1;
}

return tempv;
}


int main ()
{

    for(auto element : znajdz(mojva,3))
        cout<<element<<endl;

return 0;
}
.


equal/mismatch

mismatch(InputIt1 first1, InputIt1 last1, InputIt2 first2)

equal - zwraca bool
mismatch - zwraca strukturę z 2 wartościami std::pair  (ma 2 pola, dostęp do pierwszego p.first do drugiego p.second

przykład equal:

#include<iostream>
#include<vector>
#include<list>
#include<algorithm>

using namespace std;

vector<int> mojva={1,2,3,4,5,6,7,8,9,10,11};
list<int> mojlist={1,2,3,4,5,6,7,8,9,10,11};

bool rowne(std::vector<int> &v,std::list<int> &l )
{
return std::equal(v.begin(),end(v),l.begin() ); 
}

int main ()
{
    cout<<rowne(mojva,mojlist);
return 0;
}

mismatch - zwraca strukturę z 2 wartościami std::pair  (ma 2 pola, dostęp do pierwszego p.first do drugiego p.second

pierwsze pole zawiera iterator pierwszego kontenera do którego jest taki sam jak drugi
drugie pole zwraca iterator drugiego kontenera do którego jest takie same.

Poniżej przykład na jednym kontenerze, ale sprawdzający czy wyraz jest palindromem, dlatego trzeci argument to nie iterator na drugi kontener ale iterator na pierwszy tylko cofający się (z literką r)


#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

vector<int> mojva={1,2,3,4,4,3,2,1};

bool pali(std::vector<int> &v )
{
const auto p= std::mismatch(v.begin(),end(v),v.rbegin() ); 
return p.first == v.end() &&  p.second == v.rbegin();
}


int main ()
{
    cout<<pali(mojva)<<endl;;
return 0;
}


modyfikujące 

COPY/COPY_BACKWARP/COPY_IP

copy(InputIt first, InputIt last, OutputIt d_first)


zwraca iterator na ostatni+1 w docelowym

przykład, który kopiuje tylko parzyste:

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

vector<int> mojva={1,2,3,4,4,3,2,1};

void copiuj(std::vector<int> &v )
{
    std::vector<int> temv;

std::copy_if(v.begin(),end(v),std::back_inserter(temv), [](int i) {return i%2 == 0;    }  ); 

for (auto element : temv)
    cout<<element<<endl;;
}


int main ()
{
copiuj(mojva);
return 0;
}

predykat działa tak, że zwraca bool, jeśli zwraca true (tu jest parzysta) wtedy kopiuje.

w copy muszą być przypisywalne.


TRANSFORM

transform(InputIt first1, InputIt last1, OutputIt d_first, UnaryOperation unary_op)



przykład transform, z intów na string. iterator docelowego to back_inserter, żeby go powiekszał po dodaniu.

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

vector<int> mojva={1,2,3,4,4,3,2,1};

void transformuj(std::vector<int> &v )
{
    std::vector<string> taemv;

std::transform(v.begin(),end(v),std::back_inserter(taemv), [](int i) {return std::to_string(i);    }  ); 

for (auto element : taemv)
    cout<<element<<endl;;
}


int main ()
{
transformuj(mojva);

return 0;
}


predykat przyjmuje typ pierwszego konerera a zwraca typ drugiego

TRANSFORM ver 2
to wersja która zmienia 2 kontenery w 3 typ, przyjmuje 5 argumentów.

transform(InputIt1 first1, InputIt1 last1, InputIt2 first2, OutputIt d_first, BinaryOperation binary_op)



przykład:

#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>   //potega  pow(x,y)

using namespace std;

vector<int> mojva={1,2,3,4,4,3,2,1};

vector<int> mojva2={1,2,3,4,5,6,7,8};

void transformuj(std::vector<int> &v,std::vector<int> &v2 )
{
    std::vector<float> tempv;

std::transform(v.begin(),end(v),begin(v2),std::back_inserter(tempv), [](int i,int ii) {
return pow(i,ii);    }  ); 

for (auto element : tempv)
    cout<<element<<endl;;
}

int main ()
{
transformuj(mojva,mojva2);

return 0;
}




FILL_N

wypełnia wartością, (iterator, ile elementów, wartość)

fill_n (OutputIterator first, Size n, const T& val)


GENERATE_N
wypełnia kontener wartością którą może określić predkat.

generate_n( OutputIt first, Size count, Generator g )



REPLACE 
zamienia wartości w kontenerze
replace(ForwardIt first, ForwardIt last, const T& old_value, const T& new_value)



REPLACE_IF
zamienia te które spełniają warunek predykatu
replace_if(ForwardIt first, ForwardIt last, UnaryPredicate p, const T& new_value)

jeśli predykat zwróci true zamieni na wartość ostatniego argumentu

Implement a function that

works on std::vector<std::string>
replaces all lowercase string with "bummer"

#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>

using namespace std;

vector<string> mojva={"ala","Ala","ALa","ala","ALA"};

void replace1(std::vector<string> &v )
{
std::replace_if(v.begin(),end(v), [](string ss) {
        
        for( auto element : ss)
            {
            if ((char)element<122 && (char)element >97)
                return  true;   
            }
        return false;
        
        },std::string ("bummer")  ); 
}

int main ()
{
replace1(mojva);

for (auto element : mojva)
    cout<<element<<endl;;

return 0;
}

w rangefor iteruje dostarczonego stringa ss i jesli trafi na choć jedną dużą wysyła true i zamienia ten string na "bummer"
jesli nie znajdzie ani jednej dużej wysyła false i nie zmienia.


REMOVE
nie usuwa, ale przestawia usunięte na koniec
dlatego trzeba zastosować dodatkowo erase (to nie algorytm ale funkcja składowa kontenerów)


remove(ForwardIt first, ForwardIt last, const T& value)

przykład z erase:

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

vector<int> mojva={1,2,3,4,3,2,10,3,55,3,128};

void remove(std::vector<int> &v,int ii )
{
auto it=std::remove(v.begin(),end(v),ii ); 
v.erase(it, v.end());
}

int main ()
{
remove(mojva,3);
for(auto element : mojva)
    cout<<element<<endl;
return 0;
}

remove zwraca iterator na niejsce gdzie wstawił usunięte elementy.
Dlatego wykorzystujemy ten zwrocony iterator i za pomocą erase kasujemy od niego do końca
pozostaje sam kontener z usuniętymi elementami.


REMOVE_IF

przykład:

#include<iostream>
#include<vector>
#include<algorithm>
#include<cctype>

using namespace std;

vector<string> mojva={"ala1","Ala","ALa","ala2","ALA"};

void remove(std::vector<string> &v )
{
auto it=std::remove_if(v.begin(),end(v),[](std::string ss){
        for( auto element : ss)
        {
            if (isdigit(element))
               return true;
        } 
        return false;  
        }
        ); 

v.erase(it, v.end());

}

int main ()
{
remove(mojva);
for(auto element : mojva)
    cout<<element<<endl;
return 0;
}



UNIQUE

usuwa zdublowane

unique(ForwardIt first, ForwardIt last)

działa tylko na posortowanym kontenerze.




mutujące zmieniają, ale tylko przez przestawianie, nie usuwanie


REVERSE

reverse(BidirIt first, BidirIt last)
odwraca w tym samym kontenerze, iteratory bidirectional, więc na liście jednokierunkowej nie zadziała. nic nie zwraca.

REVERSE_COPY

reverse_copy(BidirIt first, BidirIt last, OutputIt d_first)

odwraca i kopiuje do nowego


ROTATE

wykorzystywany w Circular buffer z zaliczenia

// simple rotation to the left
    std::rotate(v.begin(), v.begin() + 1, v.end());
 
    std::cout << "simple rotate left  : ";
    for (int n: v)
        std::cout << n << ' ';
    std::cout << '\n';
 
    // simple rotation to the right
    std::rotate(v.rbegin(), v.rbegin() + 1, v.rend());
 
    std::cout << "simple rotate right : ";
    for (int n: v)
        std::cout << n << ' ';
    std::cout << '\n';





NEXT_PERMUTATION

generuje kolejne mozliwe permutacje


RANDOM_SHUFFLE

potasuje kontener

SHUFFLE

potasuje używając funkcji sortującej


sortujące

SORT

posortuje

std::sort sortuje

std::partial_sort - posortuje, ale te same pozostawi w takiej kolejnosci jak były (tyle, że koło siebie) jesli będzie sortowanie np po długości stringu, to zwykły sort ustawi koło siebie ale moze poprzestawiać oryginalną kolejność, partial_sort gwarantuje zachowanie.

Implement a function that
works on std::vector<std::string>
sort  based on string length

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

vector<string> mojva={"alaaaaaaaa","Alaaa","ALaaaaa","ala","ALAaa"};

void sortuj(std::vector<string> &v )
{
std::sort(v.begin(),end(v), [](string ss,string ss2) {    return ss.length()<ss2.length();         }  ); 
}

int main ()
{
sortuj(mojva);

for (auto element : mojva)
    cout<<element<<endl;;

return 0;
}



ACCUMULATE (wymaga include numeric)
suma wartości kontenera (np predykatem można sumować długości stringów w kontenerze)


std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
 
    int sum = std::accumulate(v.begin(), v.end(), 0);
 
    int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());

Przykład
Implement a program that

reads N values from command line
reads N floating point values
checks if values are given in ascending order
if ascending multiply

#include<iostream>
#include<vector>
#include<list>
#include<algorithm>
#include<numeric>

using namespace std;

void licz( )
{
int licz;
float licz2;
vector<float> mojva;

cout<<"podaj ilosc liczb do przemnozenia:"<<endl;
    cin>>licz;
cout<<"podawaj kolejne liczby float"<<endl;

std::cin.exceptions(std::ios::failbit | std::ios::badbit);

for(int i=0;i<licz;i++)
{
    cin>>licz2;
    mojva.push_back(licz2); 
}

if (std::is_sorted( mojva.begin(),mojva.end() ) )
    {
    cout<<  std::accumulate  (mojva.begin(),end(mojva),1.0f,std::multiplies<float>() ) << endl;
    }
}

int main ()
{
licz();
return 0;
}



IV
I/O STREAM

wystarczy raz zmienić wyświetlany typ i taki pozostaje:

zad 11.16
works on std::vector<std::uint8_t>
displays all values as 0x7f____ 0xff____

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>

using namespace std;

void copiuj( )
{
vector<uint8_t> mojv={10,20,30,40};
    
cout<<std::hex<<endl;

for (auto element :mojv )
    cout<<(int)element<<endl;;
}

int main ()
{
copiuj();
return 0;
}

będzie cały czas wyświetlał w hexadecymalnym.

chyba, że zapamiętamy poprzedni typ OLD i go po operacji przywrócimy:

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>

using namespace std;

void copiuj( )
{
vector<uint8_t> mojv={10,20,30,40};
 
std::ios::fmtflags old = std::cout.flags();

cout<<std::hex<<endl;

for (auto element :mojv )
    cout<<(int)element<<endl;;

std::cout.setf(old);
cout<<"powrot do OLD"<<endl;


for (auto element :mojv )
    cout<<(int)element<<endl;;


ENDL
nie nadużywać, oprócz końca lini ma flush, przez co flushuje strumien i pozbawia procesor możliwości pracy na cache (wydajność) (proces z Shedulera przenoszony jest do kolejki)



Dopóki poprawnie czyta:
while(std::cin) {}



metody strumienia:
goodbit - ios_base::good()  - sprawdza czy dobry
failbit - ios_base::fail()   - wzraca TRUE jeśli uszkodzony
badbit - ios_base::fail()   - zwraca true jeśli źle wpisany

strumień nie rzuca wyjątków, chyba, że sami zmienimy
throws exceptions on failbit and badbit:

w Przykładzie z ACCUMULATE mamy linię:
std::cin.exceptions(std::ios::failbit | std::ios::badbit);





FILE I/O

std::ifstream     czyta
std::ofstream    zapisuje

std::fstream      zapis i odczyt


Implement cat program:

#include<iostream>
#include<vector>
#include<fstream>

using namespace std;

int main (int argc,char**argv)
{

if(argc<2)
{
    cout<<"arg < 2" <<endl;
}
else
{
    std::string arg=argv[1];
    cout<<arg<<endl;
    std::ifstream file { arg};     
    file.unsetf(std::ios_base::skipws);
    
    if(file.is_open())
    {
        char c;
        while(file >> c)
        {
            cout<<c;
        }
    }
    else
    cout<<"failed to open"<<endl;
}
cout<<endl;
return 0;
}


std::ios_base::skipws

When the skipws format flag is set, as many whitespace characters as necessary are read and discarded from the stream until a non-whitespace character is found before.
Przekaże białe znaki, dzięki czemu znak końca linii zostanie uwzględniony.




Strumienie jak kontenery, dzięki czemu można stosować algorytmy na strumieniach.

Jak widać w powyższym przykładzie czytać plik strumieniem też można.




                                                                       V
                                         UTILITY



STD::PAIR    - para, prosty typ, przydatny gdy chcemy zwrócić 2 argumenty z funkcji

przykład inicjalizacji:
#include <utility>
#include <string>
#include <complex>
#include <tuple>
#include <iostream>
 
int main()
{
    std::pair<int, float> p1;
    std::cout << "Value-initialized: "
              << p1.first << ", " << p1.second << '\n';
 
    std::pair<int, double> p2(42, 0.123);
    std::cout << "Initialized with two values: "
              << p2.first << ", " << p2.second << '\n';
 
    std::pair<char, int> p4(p2);
    std::cout << "Implicitly converted: "
              << p4.first << ", " << p4.second << '\n';
 
}



Przykład użycia jeśli mamy funkcję która przyjmuje string i doubla i mapę o takich samych arg.

void foo(const std::string& ss,  const std::complex<double>& d);

std::map<std::string, std::complex<double>> m;

możemy dzięki parze zastosować ją w rangeforze jako TYP
for( typ  a  :  kontener)    // zamiast typu może być auto

for(std::pair<std::string, std::complex<double>> p: m)
{
    foo(p.first, p.second);
}



Przykład zwracania przez funkcję 2 argumentów w parze:

std::pair<std::string, std::complex<double>>  
    foo(const std::string& s, const std::complex<double>& d)
{
    return std::pair<std::string, std::complex<double>> ( s, d);
}


Przykład zastosowania make_pair który automatycznie rozpozna typ:

std::pair<std::string, std::complex<double>>  
    foo(const std::string& s, const std::complex<double>& d)
{
    return std::make_pair(s, d);
}




std::tuple        krotka

zmienna liczba parametrów szablonowych

Przykład zastosowania:

#include <tuple>
#include <iostream>
#include <string>
#include <stdexcept>
using student_t = std::tuple<double, char, std::string>;
 
student_t get_student(int id)
{
    if (id == 0) return std::make_tuple(3.8, 'A', "Lisa Simpson");
    if (id == 1) return std::make_tuple(2.9, 'C', "Milhouse Van Houten");
    if (id == 2) return std::make_tuple(1.7, 'D', "Ralph Wiggum");
    throw std::invalid_argument("id");
}
 
int main()
{
    auto student0 = get_student(0);
    std::cout << "ID: 0, "
              << "GPA: " << std::get<0>(student0) << ", "
              << "grade: " << std::get<1>(student0) << ", "
              << "name: " << std::get<2>(student0) << '\n';
}




smart pointers
http://slides.com/uczelnia_bt_kw/stl-3-14#/3


Klasa shared_ptr
język C++ udostępnia co prawda wskaźniki i referencje, ale same w sobie są one niewystarczające, ponieważ niejednokrotnie zachodzi potrzeba zagwarantowania usunięcia obiektu wskazywanego wraz z usunięciem ostatniego „odwołania", co ma zapewniać wykonanie operacji porządkujących stan programu (zwolnienie pamięci, zwolnienie zasobów skojarzonych z obiektem).

Potrzebna jest więc semantyka „sprzątania po obiekcie, kiedy ten nie jest już nigdzie używany". Semantykę tę, w wydaniu ze współdzieleniem własności obiektu, realizuje klasa shared_ptr. Daje ona możliwość używania wielu wskaźników shared_ptr odnoszących się do tego samego obiektu wskazywanego i biorących go w „posiadanie". Ostatni istniejący wskaźnik współdzielony, będący właścicielem obiektu wskazywanego, jest odpowiedzialny za zwolnienie obiektu i jego zasobów.
Domyślnie zwolnienie obiektu polega na wywołaniu na jego rzecz operatora delete (przy założeniu, że obiekt został utworzony wywołaniem new).
Możliwe jest jednak (a czasem konieczne) definiowanie własnych operacji porządkowania stanu obiektu w postaci własnych wytycznych zwalniania obiektu wskazywanego. Jeśli na przykład obiekt wskazywany jest tablicą przydzielaną przez new [], zwolnienie obiektu powinno odbywać się przez wywołanie del ete []. Inne wytyczne zwalniania będą dotyczyły obiektów przechowujących dodatkowe zasoby, takie jak pliki, uchwyty systemowe, blokady i tym podobne.
Podsumowując, zadaniem wskaźników typu shared_ptr jest automatyzacja usuwania zasobów skojarzonych z obiektem wskazywanym w momencie, kiedy obiekt ten nie jest już więcej używany (ale nie wcześniej).

Stosowanie wskaźników współdzielonych shared_ptr
Klasę shared_ptr stosuje się bardzo podobnie jak zwyczajny wskaźnik: wskaźniki współdzielone można przypisywać, kopiować i porównywać, a także stosować z operatorami wyłuskania * i ->, udostępniającymi obiekt wskazywany i jego składowe.
Weźmy następujący przykład:

#include <iostream>
#include <string>
#include <vector>
#include <memory>

using namespace std;

int main()
{

// dwa współdzielone wskaźniki reprezentujące dwa imiona
shared_ptr<string> pNico(new string ("nico") );
shared_ptr<string> pJutta(new string ("jutta") );

// popraw zapis imion zoielkę literę
(*pNico )[0] = 'N';
pJutta -> replace (0,1,"J");


// umieść wskaźniki w kontenerze (wielokrotnie)
vector<shared_ptr<string>> whoMadeCoffee;

whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pNico);
whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pNico);


// wypisz wszystkie elementy
for (auto ptr : whoMadeCoffee) {
     cout << *ptr << " ";
}
cout << endl ;


// jeszcze raz zmień pisowmię jednego z imion
*pNico = "Nicolai";


// ponownie wypisz wszystkie elementy
for (auto ptr : whoMadeCoffee) {
    cout << *ptr << " ";
}
cout << endl ;


// wypisz wewnętrzne dane wskaźnika
cout << "use_count: " << whoMadeCoffee[0].use_count () << endl;
}

Po dołączeniu do programu pliku nagłówkowego <memory> zawierającego definicję klasy shared_ptr deklarujemy i iniqalizujemy dwa wskaźniki współdzielone typu shared_ptr, ujmujące wskaźniki do dwóch łańcuchów znakowych:

shared_ptr<string> pNico(new string ("nico") ) ;
shared_ptr<string> pJutta(new string("jutta") ) ;


Ponieważ konstruktor wskaźnika typu shared_ptr z pojedynczym argumentem wywołania jest deklarowany ze słowem explicit, nie można tu zastosować składni inicjalizacji przez przypisanie, ponieważ jest ona traktowana jako niejawna konwersja typu.

Można za to użyć nowej dla C++11 składni inicjalizacji listą inicjalizującą:

shared_ptr<string> pNico = new string ("nico") ;    / /B Ł Ą D
shared_ptr<string> pNico{new string("nico") } ;   / /O K

Można też skorzystać z funkcji pomocniczej make_shared ():

shared_ptr<string> pNico = make_shared<string>("nico");
shared_ptr<string> pJutta = make_shared<string>("jutta") ;

Ten sposób tworzenia i inicjalizacji wskaźnika shared_ptr jest szybszy i prostszy, ponieważ wykorzystuje jedną alokację w miejsce dwóch: jednej dla obiektu wskazywanego i jednej dla danych wewnętrznych wskaźnika współdzielonego (patrzpunkt 5.2.4).

Alternatywnie wskaźnik współdzielony można zadeklarować osobno i dopiero
później przypisać do niego nowy wskaźnik. Nie można jednak wykonać tego za pomocą
operatora przypisania, a tylko za pośrednictwem funkcji składowej reset () wskaźnika:

shared_ptr<string> pNico4;
pNico4 = new string ( "nico");           // BŁĄD: nie ma przypisania zwykłego wskaźnika
pNi co4. reset(new string ( "nico"));  //O K



Następne dwa wiersze programu pokazują, że używanie wskaźników shared_ptr
nie różni się od stosowania zwyczajnych wskaźników:
(*pNico) [0] = ' N';
pJutta->replace(0, l ,"J") ;


komunikaty wypisywane przez sam program:
Jutta Jutta Nico Jutta Nico
Jutta Jutta Nicolai Jutta Nicolai
use count: 4


Klasa unique_ptr  (od c++11)

jest odmianą inteligentnego wskaźnika wyłącznego, pomocną w unikaniu
wycieku zasobów w sytuacjach potencjalnego występowania wyjątków. Ogólnie
rzecz biorąc, klasa unique_ptr implementuje koncepcję wyłącznego posiadania
obiektu wskazywanego, co oznacza, że obiekt wskazywany jest w danym momencie
w posiadaniu dokładnie jednego wskaźnika wyłącznego. Kiedy wskaźnik wyłączny
jest usuwany albo staje się wskaźnikiem pustym bądź zaczyna wskazywać do innego
obiektu, dotychczasowy obiekt wskazywany również jest usuwany. Klasa zastąpiła
wprowadzoną w c++98 auto_ptr.

standardowo należy obsłużyć wyjątki, żeby w przypadku ich wystąpienia nie nastąpiło wyjście z funkcji bez usunięcia dynamicznie stworzonych obiektów.

void f ( )
{
ClassA* ptr = new ClassA; // utwórz jawnie obiekt zasobu
try {
...            // wykonaj jakieś operacje
}
catch ( ... ) { // dla wszelkich wyjątków
   delete ptr; // - posprzątaj
   throw; // - przerzuć wyjętek dalej
}
delete ptr; // sprzątanie przy zwykłym wyjściu z funkcji
}


Sytuacja byłaby jeszcze gorsza, gdyby funkqa miała zarządzać
w ten sposób nie jednym, ale kilkoma obiektami, albo posiadała więcej niż jedną
klauzulę catch. Takie radzenie sobie z zasobami należy uznać za niepożądane, bo
mimo wszystko niebezpieczne (łatwo o błąd, pominięcie wyjątku, przeoczenie
zasobu).
W tym kontekście bardzo pomocne okazują się inteligentne wskaźniki, potrafiące
zwolnić obiekty wskazywane, kiedy same są usuwane. Co więcej, jeśli takie
wskaźniki są obiektami lokalnymi funkcji, zostaną usunięte automatycznie przy
wyjściu z funkcji, niezależnie od tego, czy jest to wyjście normalne, czy wymuszone
wyjątkiem. Implementacją takiego inteligentnego wskaźnika jest klasa
unique_ptr.

Poprzedni przykład z użyciem wskaźnika wyłącznego wyglądałby tak:

// plik nagłówkowy klasy unique_ptr
#include <memory>

void f ()
{
// utwórz i zainicjalizuj loskaźnik wyłączny do zasobu

std::unique<ClassA> ptr(new ClassA);

...       // wykonaj jakieś operacje
}

nie potrzeba już jawnego wywołania delete ani klauzul przechwytywania
wyjątków.


Stosowanie wskaźników wyłącznych unique_ptr

Klasa unique_ptr udostępnia interfejs wzorowany na interfejsie zwyczajnych wskaźników,
to znaczy definiuje operator wyłuskania * zwracający obiekt wskazywany
oraz operator dostępu do składowej obiektu wskazywanego -> (jeśli obiekt wskazywany
jest klasą albo strukturą):

// utwórz i zainicjalizuj łańcuch znaków (przez wskaźnik wyłączny)

std::unique_ptr<std::strig> up(new std::string ( "nico") ) ;

(*up) [0] = 'N' ; // zastęp pierwszy znak łańcucha
up->append ("lai"); // dołącz kilka znaków do łańcucha
std::cout <<*up<< std::endl; // wypisz cały łańcuch


Wskaźniki wyłączne nie implementują jednak arytmetyki wskaźnikowej (nie udostępniają
więc np. operacji ++); należy to zaliczyć do zalet, ponieważ arytmetyka
wskaźników bywa uznawana za źródło problemów programistycznych.

Zauważ, że klasa unique_ptr<> nie pozwala zainicalizowac obiektu wskaźnika
wyłącznego za pomocą składni inicjalizacji przez przypisanie; wskaźnik wyłączny
trzeba zainiqalizować bezpośrednim wywołaniem konstruktora z adresem obiektu
wskazywanego:

std::unique_ptr<int> up = new int; //B Ł Ą D
std::unique_ptr<int> up(new int); // OK


Wskaźnik wyłączny unique_ptr może być wskaźnikiem pustym13. Z takim wskaźnikiem
mamy do czynienia po wywołaniu konstruktora domyślnego uni que_ptr:

std::unique_ptr<std::string>  up;

Do wskaźnika wyłącznego można przypisać wskaźnik pusty nul1ptr albo wyzerować
go wywołaniem funkcji składowej reset ():

up = nullptr;
up.reset();

Ponadto na rzecz wskaźnika wyłącznego można wywołać release (), co powoduje
zwrócenie adresu obiektu wskazywanego z równoczesnym oddaniem odpowiedzialności
za zwolnienie tego obiektu:

std::unique_ptr<std::string> up(new std::string("nico") ) ;
std::string* sp = up.release( ) ; / / up nie jest już właścicielem obiektu

Fakt posiadania obiektu wskazywanego można określić za pośrednictwem operatora
bool ():

if (up) {                                    / / jeśli up nie jest wskaźnikiem pustym
std::cout << *up << std::endl ;
}


Transfer obiektu wskazywanego

Wskaźnik wyłączny unique_ptr implementuje semantykę wyłącznego posiadania
obiektu wskazywanego, ale to programista musi zapewnić, że nie istnieją inne
wskaźniki wyłączne inicjalizowane tym samym obiektem wskazywanym:

std::string *sp = new std::string ( "witaj ");
std::unique_ptr<std::string> upl ( sp );
std::unique_ptr<std::string> up2( sp ); //BŁĄD : upl i up2 posiadają ten sam obiekt

Niestety, taki błąd jest błędem czasu wykonania, a nie kompilacji.

Prowadzi to do pytania o działanie konstruktora kopiującego i kopiującego
operatora przypisania klasy unique_ptr. Odpowiedź jest natomiast prosta: wskaźnik
wyłączny nie może być kopiowany ani przypisywany do innego wskaźnika
wyłącznego w znaczeniu klasycznej semantyki kopiowania. Dozwolony jest za to
transfer odpowiedzialności za obiekt wskazywany w ramach semantyki przenoszenia,
zdefiniowanej w C++11 . W takim przypadku konstruktor
przenoszący i przenoszący operator przypisania dokonują transferu obiektu
wskazywanego między wskaźnikami wyłącznymi.

// zainicjalizuj wskaźnik wyłączny nowo utworzonym obiektem
std::unique_ptr<ClassA> upl(new ClassA);

// skopiuj wskaźnik wyłączny do innego wskaźnika wyłącznego
std::unique_ptr<ClassA> up2 (upl );  // BŁĄD: niedozwolone kopiowanie

// przekaż obiekt wskazywany do innego wskaźnika wyłącznego
std::unique_ptr<ClassA> up3(std::move(upl)); //O K



up3 jest posiadaczem obiektu utworzonego przez new, a upl staje się wskaźnikiem pustym.
Obiekt utworzony wyrażeniem new Cl assA zostanie usunięty dokładnie raz,
w momencie usuwania wskaźnika wyłącznego up3.

Podobnie zachowuje się operator przypisania:

// zainicjalizuj wskaźnik wyłączny nowo utworzonym obiektem
std::unique_ptr<ClassA> upl(new Cl as sA) ;

std::unique_ptr<ClassA> up2; // utwórz drugi wskaźnik wyłączny

up2 = upl ; // BŁĄD: niedozwolone przypisanie kopiujące

up2 = std::move (upl ); // przeniesienie wskaźnika wyłącznego z upl do up2

Przypisanie przenoszące powoduje transfer obiektu wskazywanego z upl do up2;
w efekcie up2 jest nowym właścicielem obiektu wskazywanego uprzednio przez upl,
który staje się wskaźnikiem pustym.

Gdyby wskaźnik up2 był przed transferem w posiadaniu innego obiektu, obiekt
ten zostałby zwolniony wywołaniem delete.


Przypisanie nowej wartości do wskaźnika unique_ptr wymaga, aby nowa
wartość była również typu uni que_ptr. Nie można użyć w przypisaniu zwyczajnego
wskaźnika:

std::unique_ptr<ClassA> ptr; // utwórz (pusty) wskaźnik wyłączny
ptr=new ClassA; //BŁĄD
ptr=std::unique_ptr<ClassA>(new ClassA); //OK, usuń obecnie wskazywany obiekt
// i weź nowy

Do wskaźnika wyłącznego można za to przypisać wskaźnik pusty nuli ptr, co
ma efekt identyczny z wywołaniem funkcji składowej reset ():



Weak_ptr (125 stl book)

Wskaźnik pomocniczy dla shared_ptr, pozwalający na współdzielenie dostępu do obiektu wskazywanego, bez współwłasności i współodpowiedzialności za obiekt.
Można go traktować jak wskaźnik na shared_ptr, co pozwala bezpiecznie przekazywać go do dowolnej klasy bez mnożenia shared_ptr. (i ryzyka mnożenia licznika shared pointera)
Mając weak_ptr, jesteśmy niezależni od shared_pointera. Jeśli shared pointer jest pełny dostaniemy adres obiektu, jeśli został usunięty, dostaniemy null.
Jesli chcemy pracować na obiekcie na który wskazuje weak_ptr zawsze powinnismy za pomocą locka stworzyć shared_ptr i po zakończeniu pracy usunąć go ,chyba, że stworzony lokalnie, sam zostanie usunięty po } .

Funkcja use_count() zwraca liczbę faktycznych współwłaścicieli obiektu (shared_ptr)
Weak_ptr może być pusty, jeśli nie został zainicjalizowany pełnym shared_pointerem, (domyslnie konstruktor tworzy pusty) albo jeśli po zainicjalizowaniu doszło do usunięcia ostatniego shared_pointera.
zm.expired() na pustym zwróci true
zm.lock() zwraca nowy wskaźnik shared_ptr do wskazywanego obiektu podbijając licznik.
Stworzenie w ten sposób wskaźnika shared_ptr jest jedynym bezpiecznym sposobem korzystania z obiektu na który wskazuje weak_ptr.
zm.reset() resetuje wskaźnik (zostaje wyzerowany)

// empty definition
    std::shared_ptr<int> sptr;

    // takes ownership of pointer
    sptr.reset(new int);
    *sptr = 10;

    // get pointer to data without taking ownership
    std::weak_ptr<int> weak1 = sptr;



Metody smart pointerów

.get()               - zwraca przechowywany wskaźnik
.reset()            - resetuje wskaźnik – zwalnia prawo wlasności,  można od razu przypisac nowy
.release()        -   zwraca zwykły wskaźnik a obecny smart przestaje być właścicielem – zwalnia prawo własności
.use_count()  – zwraca wartość licznika obiektów shared
.unique()        – na shared jeśli jedyny właściciel zwróci true
.expired()       - na pustym  weak_ptr zwróci true
.lock()             -   na weak_ptr zwraca nowy wskaźnik shared_ptr do wskazywanego obiektu podbijając licznik (nie operować na weak)

Tworzenie:
shared_ptr<string> pNico(new string ("nico") ) ;    //               =new string(„nico”);   nie zadziała
shared_ptr<string> pNico = make_shared<string>("nico");
pNico. reset(new string ( "nico2")); 
tworzenie weak prosto z shared:
std::weak_ptr<int> weak1 = sptr;   (potem żeby działać na nim zwracamy lock shared, weak tylko do przekazywania)

Transfer:
// skopiuj wskaźnik wyłączny up1 do innego wskaźnika wyłącznego
std::unique_ptr<ClassA> up2 (upl );  // BŁĄD: niedozwolone kopiowanie
// przekaż obiekt wskazywany do innego wskaźnika wyłącznego
std::unique_ptr<ClassA> up3(std::move(upl)); //O K
Podobnie zachowuje się operator przypisania, też trzeba użyć move.







Przykłady unikania wycieków.
132.  (stl book)



smart pointers jako argumenty funkcji:
Link1
Link2




git z zadaniami:
https://github.com/JanosikOpryszek/kod/tree/master/ZUT/stl

slajdy:
http://slides.com/uczelnia_bt_kw/

taczala git:
https://github.com/Super-Teachers

wilk git:
https://github.com/kwilkx/advc

syntex:
https://tohtml.com/cpp/

https://tohtml.com 
Bred3