Szablon <string>

Szablon definiuje klasę std::string reprezentującą tablicę znakową, która może dynamicznie zmieniać swoje rozmiary.

Poza funkcjami do zarządzania pamięcią, klasa zawiera szereg metod umożliwiających dostęp do znaków lub działania na całych tekstach (konkatenacja, usuwanie, wstawianie, zastępowanie, itd.)

Zarządzanie pamięcią

capacity()

Zwraca rozmiar bufora.

reserve (size_type n =0)

Zmienia rozmiary bufora. Gwarantuje, że po jej wykonaniu funkcja capacity() będzie zwracała co najmniej n.

size() lub length()

Zwraca długość tekstu (liczbę znaków umieszczonych w buforze).

resize(size_type n, E c = E())

Gwarantuje, że funkcja size()będzie zwracała co najmniej n.  W razie potrzeby powiększa tablicę i wypełnia znakiem c.

 

Przykład

#include <string>

#include <iostream>

#include <fstream>

using namespace std;

 

Przykład –cd

int main(int argc, char* argv[])

{

   string s;

   ifstream is("readme.txt");

   s.reserve(40);

   for(int i=0;;i++){

      int c=is.get();

      if(c<0)break;

      s+=(char)c;

      // lub: s.append(1,c);

      if(i%100==0){

          cout<<s.capacity()<<" ";

          cout<<"("<<s.size()<<") ";

      }

   }

   cout<<endl<<s;

   return 0;

}

 

Rezultat:

63 (1) 127 (101) 223 (201) 319 (301) 415 (401) 511 (501) 607 (601) 703 (701) 831 (801) 927 (901) 1023 (1001) 1119 (1101) 1215 (1201)

tekst pliku…

Metoda c_str()

Metoda zwraca stały wskaźnik do tekstu. Może być uzyta tam, gdzie wymagane jest użycie danych typu const char*.

void main(){

   s="Tekst";

   cout<<strcmp(s.c_str(),"Tekst")<<endl;

}

 

Dostęp do znaków

Użytkownik klasy string może zrealizować dostęp do znaków z wykorzystaniem operatora [], funkcji at() lub iteratorów.

 

Przykład

int main(int argc, char* argv[])

{

    string s="abcdefghij";

    s[5]='F';

    string::iterator it=s.begin();

    *it='A';

   

    s.at(1)='B';

    for(string::const_iterator ci=s.begin();

        ci!=s.end();ci++)cout<<*ci;

    cout<<endl;

 

    for(string::reverse_iterator     ri=s.rbegin();

    ri!=s.rend();ri++)cout<<*ri;

    cout<<endl;

 

   

    string s2;

    s2.resize(10);

   

    for(ri=s.rbegin(),it=s2.begin();ri!=s.rend();

        ri++,it++)*it=*ri;

    cout<<s2<<endl;

    return 0;

}

 

Rezultat:

ABcdeFghij

jihgFedcBA

jihgFedcBA

 

 

Uwaga: iterator begin() i funkcja c_str() zazwyczaj zwracają wskaźnik do tego samego obszaru pamięci. Jednakże nie należy używać ich zamiennie. Wywołanie iteratora nie gwarantuje obecności znaku 0 na końcu.

Konkatenacja tekstów

Do konkatenacji (łączenia) tekstów służy metoda append() występująca w kilku przeciążonych wersjach lub operator + i +=.

Przykład

int main(int argc, char* argv[])

{

   string s("Ala ma");

   s.append(" kota");

   s+=" i psa";

   cout<<endl<<s;

   return 0;

}

Rezultat: Ala ma kota i psa

 

Ekstrakcja tekstów

Do wyodrębniania fragmentów tekstu służy metoda substr(). Jej parametrami są indeks początkowy i długość tekstu.

 

Przykład

int main(int argc, char* argv[])

{

   string s="abcdefgijklmn";

   cout<<s.substr(3,3)<<endl;

   return 0;

}

 

Rezultat: def

 

Usuwanie fragmentów tekstu

W klasie string zdefiniowano kilka przeciążonych wersji metody erase() pozwalającej na usuwanie fragmentów tekstu (lub całego tekstu).

 

Przykład

int main(int argc, char* argv[])

{

   string s="abcdefgijklmn";

   int n1= s.find('d');

   int n2 = 3; // ile znakow

   s.erase(n1,n2);

   cout<<s<<endl;

   s="abcdefgijklmn";

   s.erase(s.begin()+2,s.begin()+4);

   cout<<s<<endl;

   return 0;

}

 

Rezultat:

abcgijklmn

abefgijklmn

 

Wstawianie fragmentów tekstu

Do wstawiania fragmentów tekstu służy (przeciążona  na kilka sposobów) funkcja insert().  Funkcja wstawia w miejsce określone przez indeks lub iterator teskt, który może być tablicą znaków, fragmentem innego obiektu klasy string lub sekwencją identycznych znaków.

Przykład

int main(int argc, char* argv[])

{

   string s("Ala ma kota");

   s.insert(strlen("Ala ma "), "psa i ");

   cout<<s<<endl;

   s="abcdefgijklmn";

   s.insert(1,2,'x');

   cout<<s<<endl;

   return 0;

}

 

Rezultat:

Ala ma psa i kota

axxbcdefgijklmn

 

Znajdywanie i zastępowanie fragmentów tekstu

Funkcja find() pozwala na wyszukanie w tekście łańcucha znaków lub wystąpienia znaku. Funkcja replace() pozwala na zastąpienie wskazanego fragmentu tekstu innym.

Przykład

int main(int argc, char* argv[])

{

    string s("Witaj $user, jestes zalogowany");

    string userTag="$user";

    int start=s.find(userTag);

    s.replace(start,userTag.size(),"Piotr Szwed");

    cout<<s<<endl;

    return 0;

}

 

Rezultat:

Witaj Piotr Szwed, jestes zalogowany

Adaptacja klasy

Klasa string nie jest szablonem, ale klasą powstałą w wyniku instancjacji szablonu basic_string typem char. Jest ona zdefiniowana w postaci deklaracji typedef:

typedef basic_string<char> string;

Intencją autorów biblioteki było stworzenie ogólnego szablonu, który pozwalałby na obsługę łańcuchów znakowych także dla innych platform i języków (Unicode, jezyków azjatyckich).

 

 

Szablon basic_string zdefiniowany jest jako:

 

template<class charT,
  class traits = char_traits<charT>,
  class allocator = allocator<charT> >
  class basic_string;

 

Klasa traits „własności” definiuje kilkanaście funkcji statycznych określających podstawowe cechy znaków:

 

static void assign(E& x, const E& y);

static E *assign(E *x, size_t n, const E& y);

static bool eq(const E& x, const E& y);

static bool lt(const E& x, const E& y);

static int compare(const E *x, const E *y, size_t n);

static size_t length(const E *x);

static E *copy(E *x, const E *y, size_t n);

static E *move(E *x, const E *y, size_t n);

static const E *find(const E *x, size_t n, const E& y);

static E to_char_type(const int_type& ch);

static int_type to_int_type(const E& c);

static bool eq_int_type(const int_type& ch1, const int_type& ch2);

static int_type eof();

static int_type not_eof(const int_type& ch);

Przykład

Klasa typu string, której operator == ignoruje wielkość liter (na podst. Thinking In C++)

 

class ichar_traits :

public  std::char_traits<char>

{

public:

  static int compare(const char* str1,

    const char* str2, size_t n) {

      return strnicmp(str1,str2,n);

  }

};

 

typedef basic_string<char, ichar_traits,

  allocator<char> > istring;

 

int main() {

  istring first = "tHis";

  istring second = "ThIS";

  cout << (first==second) << endl;

}

 

Alokator

Drugim parametrem szablonu jest klasa allocator, czyli klasa odpowiedzialna za zarządzanie pamięcią. Metody:

·       allocate() i deallocate() przydzielają i zwalniają pamięć dla tablicy obiektów

·       construct() i destroy() tworzą (klonują) obiekt i zwalniają pamięć obiektu

Każdy obiekt klasy powstałej w wyniku instancjacji szablonu basic_string ma pole typu allocator.  Rozmiar sizeof (allocator<char>) wynosi 1b.

Przykłady zastosowań kontenerów

Klasa SparseMatrix

Klasa SparseMatrix jest rodzajem słownika (funkcji) odwzorowuje int ´ int ® double. Do przechowywania elementów odwzorownia wykorzystywany jest szablon vector.

class SparseMatrix

{

   struct element{

      int row,col;

      double value;

   };

   vector<struct element> elements;

public:

   double get(int r, int c)const{

      for(int i=0;i<elements.size();i++){

          if(elements[i].row==r &&

                 elements[i].col==c)

          return elements[i].value;

      }

      return 0;

   }

   void set(int r, int c, double v){

      for(int i=0;i<elements.size();i++){

          if(elements[i].row==r &&

                 elements[i].col==c){

             elements[i].value=v;

             return;

          }

      }

      struct element e;

      e.row=r;e.col=c;   e.value=v;

      elements.push_back(e);

   }

   int getRowsCount()const{

      int rc=0;

      for(int i=0;i<elements.size();i++)

          if(elements[i].row>rc)

             rc=elements[i].row;

      return rc;

   }

   int getColsCount()const{

      int cc=0;

      vector<struct element>::
          const_iterator it;

      for(it=elements.begin();

          it!=elements.end();it++)

          if(it->col>cc)cc=it->col;

      return cc;

   }

};

 

void main() {

  SparseMatrix m;

  for(int k=0;k<10;k++)m.set(k,k,1);

 

  for(int i=0;i<m.getRowsCount();i++){

   for(int j=0;j<m.getColsCount();j++)

      cout<<m.get(i,j)<<" ";

   cout<<endl;

  }

}

Rezultat:

1 0 0 0 0 0 0 0 0

0 1 0 0 0 0 0 0 0

0 0 1 0 0 0 0 0 0

0 0 0 1 0 0 0 0 0

0 0 0 0 1 0 0 0 0

0 0 0 0 0 1 0 0 0

0 0 0 0 0 0 1 0 0

0 0 0 0 0 0 0 1 0

0 0 0 0 0 0 0 0 1

 

Lista – szablon <list>

Szablon definiuje listę dwukierunkową. Możliwe jest dodawanie elementów na początku i końcu listy, wstawianie, usuwanie, łączenie ze sobą list. Nie jest możliwy swobodny dostęp do elementów za pośrednictwem operatora []. Lista jest efektywna, jeżeli liczba elementów i ich miejsce wstawienia nie są z góry określone. Możliwe jest automatyczne sortowanie listy.

#include <list>

void main()

{

    list<int> li;

    for(int i=0;i<10;i++)

        li.push_back(-rand()%100);

    for(i=0;i<10;i++)

        li.push_front(rand()%100);

    for(list<int>::const_iterator it=li.begin();

        it!=li.end();it++)    cout<<*it<<" ";

    cout<<endl;

    li.sort();

    for(it=li.begin();it!=li.end();it++)

        cout<<*it<<" ";

    cout<<endl;

}

 

Rezultat:

36 27 42 95 91 61 27 81 45 5 -41 -67 -34 0 -69 -24 -78 -58 -62 -64

-78 -69 -67 -64 -62 -58 -41 -34 -24 0 5 27 27 36 42 45 61 81 91 95

 


Aby możliwe było automatyczne sortowanie elementów listy za pomocą metody sort(), klasa, której obiekty są umieszczone na liście musi zapewniać operatory do porównywania elementów.

class A

{

public:

    bool operator==(const A&)const{return false;}

    bool operator<(const A&)const{return false;}

};

 

void main()

{

    list<A> ali;

    for(int i=0;i<10;i++)ali.push_back(A());

    ali.sort();

}

 

Stos – szablon <stack>

Stos jest kontenerem implementowanym wewnętrznie za pomocą bardziej ogólnego szablonu deque (dwukierunkowa lista wektorów).

Stos zapewnia interfejs pozwalający na dodawanie i usuwanie elementów na końcu oraz odczyt ostatniej wartości.

Przykład – kalkulator odwrotnej notacji polskiej

Odwrotna notacja polska (Reverse Polish Notation) jest pozbawioną nawiasów formą zapisu ułatwiającą maszynową interpretację wyrażeń. Zasadą jej interpretacji jest umieszczenie na stosie argumentów, a następnie operatora. Podczas ewaluacji operator pobiera ze stosu wymaganą liczbę argumentów, oblicza wartość i umieszcza rezultat na stosie.

Na przykład: wyrażenie (12.5 + (-5)) *2 /3 zapisywane jest w ONP jako „12.5 -5 + 2 * 3 /

#include <stack>

#include <strstream>

#include <stdlib.h>

using namespace std;

 

class RPNCalc

{

    stack<double> dstack;

    void evaluate(char op)

    {

        if(dstack.size()<2){

             cout<<"Too little args"<<endl;

             return;

        }

        double arg2=dstack.top();dstack.pop();

        double arg1=dstack.top();dstack.pop();

        double result;

        switch(op){

             case '+':result=arg1+arg2;break;

             case '-':result=arg1-arg2;break;

             case '*':result=arg1*arg2;break;

             case '/':result=arg1/arg2;break;

        }

        dstack.push(result);

    }

public:

    void push(const string&s){

        if(s=="+" ||s=="-" || s=="*"||s=="/"){

             evaluate(s.c_str()[0]);

             return;

        }

        double arg=atof(s.c_str());

        dstack.push(arg);

    }

    void getResult(){

        if(dstack.size()<1)return;

        double result=dstack.top();dstack.pop();

        cout<<result<<endl;

    }

 

    void evaluate(const char*s){

        istrstream is(s);

        while(is){

             string a;

             is>>a;

             if(a.empty())break;

             push(a);

        }

        getResult();

    }

};

 

void main() {

    RPNCalc calc;

    calc.evaluate("12.5 -5 + 2 * 3 /"); //5

    for(;;){

        char command[256];;

        cin.getline(command,256);

        if(!strcmp(command,"exit"))return;

        calc.evaluate(command);

    }

}

 

Słownik – szablon <map>

Słownik jest zapisem funkcji odwzorowującej klucze w wartości. Jest więc zbiorem par (klucz, wartość). W słowniku może wystąpić tylko pojedyncza instancja klucza. Dana wartość może być przypisana wielu kluczom. Szablon map jest optymalizowany, aby zapewnić dużą prędkość wyszukiwania elementów słownika, dlatego jest implementowany jako drzewo.

Klasa może być użyta jako klucz, jeżeli zapewnia operatory do porównywania elementów == oraz < lub jest typem wbudowanym zapewniającym te operatory domyślnie.

Przykład – słownik poleceń.

#include <map>

 

void main()

{

     map<string,int> dict;

    dict.insert(map<string,int>::value_type("open",0));

    dict.insert(map<string,int>::value_type("o",0));

    dict["close"]=1;

    dict["c"]=1;

    dict["exit"]=-1;

    map<string,int>::const_iterator i;

    for(i=dict.begin();i!=dict.end();i++){

        cout<<i->first<<" -> "<<i->second<<endl;

    }

 

    for(;;){

        char command[256];;

        cin.getline(command,256);

        i = dict.find(command);

        if(i==dict.end()){

             cout<<command<<" ???";continue;

        }

        cout<<i->second<<endl;

        if(i->second==-1)return;

    }

}

 

Rezultat:

c -> 1

close -> 1

exit -> -1

o -> 0

open -> 0

... Liczby odpowiadające poleceniom

Jak widać, iterator słownika wypisuje elementy w porządku alfabetycznym.

 

Zbiór – szablon <set>

Zbiór jest kontenerem, który przechowuje unikalne wartości elementów. Szablon definiuje trzy podstawowe metody:

·       insert() – dodaje element

·       empty() – testuje czy zbiór jest pusty

·       find() – znajduje element w zbiorze

Zbiór może być traktowany jak zdegenerowana postać słownika: zapisuje odwzorowanie klucz®wartość, ale to, w co odwzorowany jest klucz jest nieistotne: liczy się obecność klucza w zbiorze.

Zbiór jest implementowany jako drzewo, dlatego elementy zbioru muszą spełniać takie same wymagania dotyczące interfejsu, jak klucze dla słownika: definiować operatory == oraz <.

Przykład

#include <set>

void main()

{

    set<int> cont;

    cout<<(cont.empty()?"empty":"!empty")<<endl;

    for(int i=10;i>=0;i--)cont.insert(i);

    for(i=5;i<15;i++)cont.insert(i);

    cout<<(cont.empty()?"empty":"!empty")<<endl;

    set<int>::const_iterator it = cont.find(10);

    if(it != cont.end())cout<<"has:"<<*it<<endl;

    for(it=cont.begin();it!=cont.end();it++)

        cout<<*it<<" ";

    cout<<endl;

}

 

Rezultat

empty

!empty

has:10

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

 

Iteratory

Bilblioteka STL klasyfikuje iteratory ze względu na możliwość:

·       odczytu (input iterator) InIt

·       zapisu (output iterator) OutIt

·       kierunek ruchu (w przód FwdIt, w przód i tył BidIt, random acces RanIt)

Nową cechą zmodyfikowanej biblioteki iostream jest zdefiniowanie iteratorów umożliwiających odczyt lub zapis do strumieni (traktowanych jak kontenery).

#include <list>

#include <iostream>

void main()

{

    list<int> li;

    istream_iterator<int> ist(cin);

    for(;ist!=istream_iterator<int>();ist++){

        li.push_back(*ist);    

    }

    // istream_iterator<int>() – koniec strumienia

    for(int i=0;i<10;i++)li.push_back(rand()%10);

    for(i=0;i<10;i++)li.push_front(rand()%10);

    ostream_iterator<int> out(cout, " ");

    for(i=0;i<10;i++){

        *out=i;

        out++;

    }

    cout<<endl;

    cout<<endl;

    copy(li.begin(),li.end(),out);

    cout<<endl;

    li.sort();

    copy(li.begin(),li.end(),out);

    cout<<endl;

}

 

Algorytmy

W nagłówku <algorithm> zdefiniowano szereg szablonów funkcji implementujących standardowe algorytmy działające na kontenerach różnych typów (w tym strumieniach).

Parametrami tych funkcji są zazwyczaj iteratory (wejściowe i wyjściowe) oraz dla niektórych algorytmów obiekty funkcyjne.

Obiekty funkcyjne

Obiekty funkcyjne pozwalają na adaptację działania algorytmu przez przekazanie do niego funkcji. Zamiast wskaźnika do funkcji (w stylu C) przekazywany jest obiekt klasy, która definiuje operator () . Operator () jako jedyny może przyjąć dowolną liczbę argumentów – listę formalnych parametrów funkcji.

Przykład

template <class T, class FO>

void callFunctionObject(T&t,FO&foo)

{

    foo(t);

}

 

class Incrementer

{  

    public:

    void operator()(int&t)

    {

        t++;

    }

};

 

void main()

{

    int x=10;

    callFunctionObject(x,Incrementer());

    cout<<x<<endl;

}

Rezultat: 11

Algorytmy STL wykorzystują w zasadzie cztery podstawowe rodzaje funkcji (obiektów funkcyjnych):

·       Funkcja jednoargumentowa (unarna)

·       Funkcja dwuargumentowa (unarna)

·       Predykat (jednoargumentowa funkcja zwracająca typ bool)

·       Predykat dwuargumentowy (binarny)

 

 

Algorytm copy

Algorytm pozwala na przekopiowanie zawartości kontenera (wszystkie elementy lub pewien obszar) do innego kontenera w miejsce wskazane przez iterator wyjściowy.

#include <list>

#include <algorithm>

void main()

{

    list<int> li;

    for(int i=0;i<10;i++)

    {li.push_back(i);li.push_front(i);}

    list<int> li2;

    li2.resize(li.size());

    copy(li.begin(),li.end(),li2.begin());

 

    copy(li2.begin(),li2.end(),

        ostream_iterator<int>(cout," "));

    cout<<endl;

}

Rezultat:

9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9

Algorytm count

Algorytm oblicza liczbę wystąpień elementu w kontenerze.

#include <list>

#include <algorithm>

void main()

{

    list<int> li;

    for(int i=0;i<10;i++)

    {li.push_back(i);li.push_front(i);}

    cout<<count(li.begin(),li.end(),0)<<endl;

}

Rezultat: 2

Algorytm count_if

Algorytm oblicza liczbę elementów w kontenerze spełniających predykat przekazany jako obiekt funkcyjny.

#include <list>

#include <algorithm>

 

class LessThen

{

    int v;

public:

    LessThen(int _v){v=_v;}

    bool operator()(int k){return k<v;}

};

 

void main()

{

    list<int> li;

    for(int i=0;i<10;i++)

    {li.push_back(i);li.push_front(i);}

    cout<<
    count_if(li.begin(),li.end(),LessThen(3))
    <<endl;

}

Rezultat: 6

Algorytm for_each

Algorytm wykonuje dla każdego obiektu należącego do wskazanej sekwencji w kontenerze jednoargumentową funkcję przekazaną w postaci obiektu funkcyjnego.

 

#include <list>

#include <algorithm>

 

class Dump

{

public:

    void operator()(int k){cout<<k<<" ";}

};

 

 

void main()

{

    list<int> li;

    for(int i=0;i<10;i++)

    {li.push_back(i);li.push_front(i);}

    for_each(li.begin(),li.end(),Dump());

    cout<<endl;

}

Rezultat:

9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9


Algorytm includes

Algorytm działa na uporządkowanych kolekcjach elementów. Pozwala na sprawdzenie, czy wszystkie elementy danego kontenera są zawarte w drugim kontenerze. Algorytm może być używany do porównywania zbiorów.

Przykład

#include <set>

#include <algorithm>

 

void main()

{

    set<int> s1;

    for(int i=0;i<20;i++){s1.insert(i);}

    set<int> s2;

    for(i=0;i<10;i++){s2.insert(i);}

   

    cout<<"s1 includes s2 "<<

    includes(s1.begin(),s1.end(),s2.begin(),s2.end())

    <<endl;

    cout<<"s2 includes s1 "<<

    includes(s2.begin(),s2.end(),s1.begin(),s1.end())

    <<endl;;

}

Rezultat:

s1 includes s2 1

s2 includes s1 0

 

 


Algorytm min_element

Algorytm pozwala na wyznaczenie minimalnego elementu w kontenerze. Przykłady pokazują dwie wersje wywołania algorytmu: opartą na naturalnym porządku elementów i z dostarczonym obiektem funkcyjnym (dwuargumentowym predykatem) do porównywania elementów.

Przykład 1

#include <list>

#include <algorithm>

 

void main()

{

    list<int> li;

    for(int i=0;i<10;i++)

    {li.push_back(i);li.push_front(i);}

    list<int>::const_iterator m=     min_element(li.begin(),li.end());

   

    cout<<*m<<endl;

}

Rezultat: 0

Przykład 2

#include <list>

#include <algorithm>

 

class ReverseLess

{

public:

    bool operator()(int e1,int e2){return e1>e2;}

};

 

void main()

{

    list<int> li;

    for(int i=0;i<10;i++)

    {li.push_back(i);li.push_front(i);}

    list<int>::const_iterator m=     min_element(li.begin(),li.end(),ReverseLess());

   

    cout<<*m<<endl;

}

Rezultat: 0

Algorytm replace

Algorytm pozwala na zastąpienie w kontenerze każdego wystąpienia elementu o danej wartości nową wartością.

 

Przykład

#include <list>

#include <algorithm>

 

void main()

{

    list<int> li;

    for(int i=0;i<10;i++)

    {li.push_back(i);li.push_front(i);}

    replace(li.begin(),li.end(),5,-5);

    copy(li.begin(),li.end(),
        ostream_iterator<int>(cout," "));

    cout<<endl;

}

Rezultat:

9 8 7 6 -5 4 3 2 1 0 0 1 2 3 4 -5 6 7 8 9


Algorytm replace_if

Algorytm zastępuje nową wartością wszystkie elementy spełniające określony predykat przekazany jako obiekt funkcyjny.

Przykład

#include <list>

#include <algorithm>

 

class LessThen

{

    int v;

public:

    LessThen(int _v){v=_v;}

    bool operator()(int k){return k<v;}

};

 

void main()

{

    list<int> li;

    for(int i=0;i<10;i++)

        {li.push_back(i);li.push_front(i);}

    replace_if(li.begin(),li.end(),LessThen(5),0);

    copy(li.begin(),li.end(),

        ostream_iterator<int>(cout," "));

    cout<<endl;

}

 

Rezultat:

9 8 7 6 5 0 0 0 0 0 0 0 0 0 0 5 6 7 8 9