La librairie standard C++
Transcription
La librairie standard C++
Programmer avec la librairie standard C++ Introduction à boost Ressources y The C++ Standard Library, a tutorial and reference, Nicolai M. Josuttis, Addison Weslley. y Beyond the c++ Standard Library, an introduction to boost, Björn Karlsson, Addison Wesley. y http://cs.stmarys.ca/~porter/csc/ref/cpp_standlib.html y http://gcc.gnu.org/onlinedocs/libstdc++/manual/spine.html y http://msdn.microsoft.com/en-us/library/cscc687y(VS.8 0).aspx y http://www.boost.org/ Composantes de la librarie y Utilitaires : Paires, limites, functions auxiliaires, opérateurs de comparaison, auto_ptr, fichiers C. y Exceptions y Streams (flots) y STL (Standard Template Library) y Contenneurs y Itérateurs y Algorithms y Strings Toutes les classes et fonctions de la bibliothèque standard appartiennent au namespace std:: Les fichiers en-tête de la bibliothèque standard ne portent pas d’extension . #include <iostream> Fichier en-tête standard Namespace int main() { std::cout << "Hello World\n"; } Paires y Cette famille de classes permet de représenter des paires de valeurs. y On déclare une paire en utilisant pair<T1,T2> où T1 et T2 sont des types. y Le constructeur par défaut crée une paire où les deux valeurs sont crées avec le constructeur par défaut des types respectifs (T1 et T2). y Les membres first et second permettent d’accéder respectivement à la première et à la deuxième valeur. Paires y La famille de fonctions make_pair(x,y) permet de créer à la volée une paire, le type du résultat dépend des paramètres. y Si les types T1 et T2 définissent l’opérateur de comparaison < alors pair<T1,T2> le définit aussi. #include <iostream> #include <utility> Fichier en-tête contenant La définition de pair int main() { std::pair<int,double> p1(1,2.0), p2=std::make_pair(3,4e0); std::cout << "p1=(" << p1.first << "," <<p1.second << ")\n" << "p2=(" << p2.first << "," <<p2.second << ")\n"; } Limites y Les classes numeric_limits<T> permettent de connaître les caractéristiques de chaque type. y Une liste assez complète de membres est disponible dont : max(), min(), digits, digits10, is_signed, is_integer, etc. y Tous les membres sont statiques y Il faut inclure le fichier en-tête limits. std::cout << "max(short) " << std::numeric_limits<short int>::max() << std::endl << "round to zero " << std::numeric_limits<double>::round_style << std::endl; Fonctions auxiliaires y Deux familles de fonctions auxiliaires sont disponibles pour faciliter des tâches courantes. y max, retourne le maximum de ses arguments, elle s’applique à tout type qui peut être comparé, l’opérateur doit être < défini. y Swap, échange les valeurs de deux variables (passés par référence), s’applique à tout type pouvant être copié. #include <iostream> #include <algorithm> int main() { Fichier en-tête contenant La définition de max et swap double a=1., b=-7.; std::cout << "max(" << a << "," << b << ")=" << std::max(a,b) << std::endl; std::swap(a,b); std::cout << "a=" << a << "\nb=" << b << std::endl; } Opérateurs de comparaison y Le namespace std::rel_ops contient une famille d’opérateurs génériques qui permettent de définir tous les opérateurs de comparaison pour toute classe qui définit < et ==. y Pour utiliser rel_ops il suffit d’importer le namespace avec l’instruction using. #include <utility> class X { … public: bool operator== (const X& x) const; bool operator<(const X& x) const; … }; Les comparaisons supplémentaires deviennent accessibles Les opérateurs != et > non définis dans la classe X sont automatiquement crées void f() { using namespace std::rel_ops; X x1, x2; … if( x1 != x2) … if (x1 > x2) … } Fichier en-tête C y Tous les fichiers en-tête de la librairie standard C sont accessibles en C++ avec un nom préfixé par « c » et sans le suffixe « .h » y Les foncions C ne sont pas dans le namespace std. y Exemples: cmath, cstddef, cstdlib. #include <iostream> #include <cmath> int main() { std::cout << "pi=" << M_PI << "\nsin(1)=" << sin(1.0) << std::endl; } Auto_ptr y L’utilisation de pointeurs en C++ peut être source d’erreurs. y La famille de classes auto_ptr permet de gérer automatiquement le cycle de vie d’un pointeur. y On évite: y Les fuites de mémoire (pointeurs non désaloués) y Les problèmes liés aux exceptions. y Un auto_ptr est initialisé avec un pointeur obtenu par new, le destructeur de auto_ptr se charge de libérer la mémoire, la gestion de mémoire devient automatique. y Un pointeur géré par auto_ptr ne peut pas être partagé. y Si on copie un auto_ptr la variable originale n’a plus le contrôle de la mémoire. y Un auto_ptr ne peut pas être utilisé comme membre d’un conteneur (vector, list, etc.). y Si on a besoin d’un pointeur intelligeant pouvant partager la gestion de la mémoire entre différentes variables on doit préférer un shared_ptr (actuellement membre de boost qui est désormais dans la bibliothèque standard). y Le constructeur d’un auto_ptr reçoit comme argument un pointeur. y La classe aut_ptr définit les opérateurs * et -> permettant d’utiliser la même sémantique que pour un pointeur. #include <iostream> #include <memory> int main() { std::auto_ptr<int> p(new int(55)); En-tête déclarant auto_ptr Création de auto_ptr à partir de mémoire obtenue std::cout << "p=" << p.get() << "\n*p=" << *p << std::endl; par new *p=10; On "\n*p=" utilise un<<auto_ptr comme un std::cout << "p=" << p.get() << *p << get() std::endl; permet de récupérer le pointeur } pointeur vers la mémoire gérée par le auto_ptr Exceptions de la librairie standard y Les composantes de la librairie standard peuvent jeter des exceptions. y La librairie standard définit des classes qui représentent ses exceptions. y Toute exception jetée par une composante de la librairie standard dérive de la classe std::exception. y La classe exception contient un membre what() qui renvoi un char* avec un message d’erreur. bad_alloc Domain_error bad_cast Invalid_argument bad_typeid Length_error logic_error Out_of_range exception ios_base::faillure Range_error Runtime_error Overflow_error Bad_exception Underflow_error #include <iostream> #include <exception> int main() { int n; std::cout << "Taille="; std::cin >> n; try { int *p=new int[n]; } catch (std::exception &e) { std::cout << "exception " << e.what() << std::endl; } return 0; } STL y La Standard Template Library est une partie de la librairie standard C++ comprenant trois parties : y Conteneurs. Utilisés pour gérer des collections d’objets d’un type donné. y Itérateurs. Utilisés pour parcourir les éléments des conteneurs, ils font abstraction du stockage et peuvent être utilisés pour représenter des parties de conteneurs. y Algorithmes. Utilisés pour traiter des éléments de conteneurs. Les algorithmes ne manipulent pas directement les conteneurs, ils doivent utiliser des itérateurs. Conteneur Ité rat eu r Algorithme ur e t ra Ité Conteneur Itérateur Conteneur Conteneurs y Les conteneurs permettent de stocker de façon optimale plusieurs objets de même type. y Chaque conteneur est optimisé pour un usage spécifique. y On peut classer les conteneurs en deux familles: y Conteneurs séquenciels: vector, deque, list. Les éléments sont stockés dans un ordre décidé par l’utilisateur (par exemple l’ordre d’insertion) y Conteneur associatifs: set, multiset, map et multimap. Les éléments sont stockés triés selon un ordre associé au type (en général <). Opération communes à tous les conteneurs, si TC est un conteneurs : y TC c, crée un conteneur c vide y TC c1(c2), crée un conteneur c1 de même type que c2 et copie les éléments. y TC c(beg,end), crée un conteneur á partir de deux itérateurs et copie les valeurs de [beg,end). y c.size(), retourne le nombre d’éléments dans le conteneur. y c.empty(), retourne vrai si le conteneur est vide. y c.max_size(), retourne le nombre maximal d’éléments pouvant être contenus dans le conteneur. y c1==c2, c1!=c2, c1<c2, c1>c2, c1<=c2, c1>=c2, compare deux conteneurs (ordre lexicographique). y c1=c2, copie tous les éléments de c2 dans c1. y c1.swap(c2), swap(c1,c2), échange les éléments de c1 et c2. y c.begin(), retourne un itérateur vers le premier élément de c. y c.end(), retourne un itérateur vers la position après le dernier élément. y c.rbegin(), retourne un itérateur inverse vers le dernier élément. y c.rend(), retourne un itérateur inverse vers la position avant le premier élément. y c.insert(pos,elem), insère une copie de elem. y c.erase(beg,end), efface tous les éléments dans l’intervalle [beg,end). y c.clear(), efface tous les éléments du conteneur. Vector y La famille de classes vector est une représentation d’un tableau dynamique et des opérations permettant de manipuler les données y stockés. y L’accès à un élément se fait en O(1). y L’insertion à la fin se fait en O(1), sauf exception. y L’insertion ailleurs que à la fin se fait en O(n) y Un vecteur se comporte d’une façon proche d’un tableau, avec une syntaxe plus riche. y On crée un vector de taille n en appelant le constructeur à un argument entier non signé n. y On peut aussi créer un vector contenant n copies de la valeur elem en appelant le constructeur à deux arguments n et elem. y On accède à l’élément i en utilisant l’opérateur [i], dans ce cas on ne fait aucune vérification de i. y La fonction at(i) accède à l’élément i et vérifie que i est valable, une exception est jetée si i<0 où i>=n. y La fonction push_back(elem) permet d’ajouter un élément à la fin d’un vector qui se trouve agrandi. y La fonction resize(n) permet de changer la taille d’un vector. Size et capacity y La performance de l’ajout d’éléments en fin d’un vector est due au fait que la classe garde une réserve d’espace en fin de mémoire. y Le nombre d’éléments contenus dans un vector est donné par size(). y Le nombre total d’éléments que le vector peut contenir sans avoir à redimensioner la mémoire est donné par capacity(). y Le changement de capacity() se fait automatiquement, on peut donc avoir une insertion qui prend beaucoup de temps. #include <iostream> #include <vector> int main() { std::vector<int> vi(100),vj; for (int i=0; i<100; ++i) { vi[i]=100-i; vj.push_back(i); } std::cout << "size(vi)=" << vi.size() << " capacity(vi)=" << vi.capacity() << "\nsize(vj)=" << vj.size() << " capacity(vj)=" << vj.capacity() << "\nvi[0]=" << vi[0] << " vi[99]=" << vi[99] << "\nvj[0]=" << vj[0] << " vj[99]=" << vj[99] << std::endl; … vi.resize(128); for (int i=100; i<128; ++i) vi[i]=100-i; std::cout << "size(vi)=" << vi.size() << " capacity(vi)=" << vi.capacity() << "\nvi[128]=" << vi[128] << std::endl; vi.reserve(1000); std::cout << "capacity(vi)=" << vi.capacity() << std::endl; return 0; } y Les fonctions insert(…) permettent d’insérer des éléments à une position donnée. y La fonction pop_back() détruit le dernier élément et diminue la taille du vector y Les fonctions erase déruisent une parties des éléments du vector. Deque y Un deque est très similaire à un vector sauf qu’on y peut facilement insérer des éléments au début et à la fin. y L’interface de deque est très similaire à celle de vector, avec les différences suivantes : y Il n’y a pas de fonctions capacity() et reserve(). y Les fonctions push_front() et pop_front permettent de facilement insérer et détruire des éléments au début du conteneur. #include <iostream> #include <deque> int main() { std::deque<int> v(100); for (int i=0; i<100; ++i) v[i]=100-i; for (int i=0; i<50; ++i) { v.push_back(i); v.push_front(i*i); } std::cout << "size(v)=" << v.size() << "\nv[0]=" << v[0] << " v[" << v.size()-1 << "]=" << v[v.size()-1] << std::endl; return 0; } List y Une liste est un conteneur qui organise les éléments dans une liste doublement chainée. y L’insertion dans une liste se fait en O(1), l’accès à un élément en O(n). y Il n’y a pas d’opérateur d’accès direct aux éléments d’une liste, tout accès doit être séquentiel. Pas de fonction at() où d’opérateur []. y Il n’y a pas de taille pré allouée pour une liste, donc pas de capacity() où resize(). y Les listes ont des membres spécifiques pour transférer des éléments qui sont plus rapides que les algorithmes. #include <iostream> #include <list> #include <vector> void printList(const std::list<int>& l1, const std::list<int> &l2) { std::cout << "List 1: "; for (std::list<int>::const_iterator pi=l1.begin(); pi!=l1.end(); ++pi) std::cout << *pi << ","; std::cout << std::endl; std::cout << "List 2: "; for (std::list<int>::const_iterator pi=l2.begin(); pi!=l2.end(); ++pi) std::cout << *pi << ","; std::cout << std::endl; } int main() { std::list<int> list1, list2; for (int i=0; i<10; ++i) { list1.push_back(i); list2.push_front(i); } printList(list1,list2); list2.splice(find(list2.begin(),list2.end(),3),list1); printList(list1,list2); list1=list2; list2.sort(); printList(list1,list2); list2.unique(); printList(list1,list2); return 0; } Set y Un set<T> contient des éléments de type T triées automatiquement. T doit définir un ordre, par défaut on utilise l’opérateur < mais une autre fonction peut être utilisée. y La recherche d’un élément a une complexité O(log(n)). y Set possède quelques opérations spécifiques à la recherche dans le conteneur : y count(elem) retourne le nombre d’éléments avec valeur égale à elem. y find(elem) retourne le premier élément avec valeur elem. y lower_bound(elem), upper_bound(elem), retournene la position du premier élément >= elem, resp. <= elem. y Dans un set il y a pas de duplication. y Les membres suivants permettent l’insertion d’éléments : y c.insert(…), plusieurs surcharges d’une même fonction permettant d’insérer à différents endroits. y c.erase(…) permet de effacer un ou plusieurs éléments. y c.clear() efface tout le contenu de la liste. #include <iostream> #include <set> #include <string> int main() { std::set<std::string> couleurs; couleurs.insert("bleu"); couleurs.insert("rouge"); couleurs.insert("vert"); couleurs.insert("jaune"); couleurs.insert("bleu"); for (std::set<std::string>::const_iterator p=couleurs.begin(); p != couleurs.end(); ++p) std::cout << *p << ","; std::cout << std::endl; } Multiset y Un multiset est un set acceptant des duplications d’éléments. y Multiset utilise le même fichier en tête que set. y Alors que count retourne 0 où 1 pour un set elle peut retourner une valeur > 1 pour un multiset. #include <iostream> #include <set> #include <string> int main() { std::multiset<std::string> couleurs; couleurs.insert("bleu"); couleurs.insert("rouge"); couleurs.insert("vert"); couleurs.insert("jaune"); couleurs.insert("bleu"); for (std::multiset<std::string>::const_iterator p=couleurs.begin(); p != couleurs.end(); ++p) std::cout << *p << ","; std::cout << std::endl; std::cout << "count(jaune)=" << couleurs.count("jaune") << "\ncount(bleu)=" << couleurs.count("bleu") << std::endl; } Map y Un map<K,T> est un conteneur permettant de gérer des paires de clés de type K et valeurs de type T. Il peut être vu comme un dictionnaire. y La classe K doit avoir un ordre de tri, par défaut on utilise l’opérateur < mais on peut spécifier une autre fonction. y Dans un map les clés sont uniques. y La recherche d’une clé dans un map est de complexité O(log(n)). y Un map peut être utilisé comme un tableau associatif, on utilise l’opérateur [] avec un indice de type K. Les opérations de recherche count(key), find(key), lower_bound(key), upper_bound(key) et equal_range(key) sont disponibles pour les map, elles sont similaires aux opérations de même nom pour les set et s’appliquent aux clès. Ces opérations retournent des std::pair<K,T>. y Les opérations insert(…) et erase(…) sont aussi présentes, dans insert l’élement à insérer est représenté par une std::pair<K,T>. y #include <iostream> #include <map> int main() { std::map<std::string,double> nombres; nombres.insert(std::make_pair("pi",3.14159)); nombres.insert(std::make_pair("e",2.71828)); nombres["catalan"] = 0.9159655; for (std::map<std::string,double>::const_iterator p=nombres.begin(); p != nombres.end(); ++p) std:: cout << "nombres[" << p->first << "]=" << p->second << std::endl; } Multimap y Un multimap est un map acceptant des duplications de clès. y L’opérateur [] n’est pas défini pour un multimap. y La famille de classes multimap est définie dans le même fichier en-tête que map. #include <iostream> #include <map> int main() { std::multimap<std::string,std::string> dico; dico.insert(std::make_pair("pi", "Le rapport entre le perimètre et le diamètre d'un cercle.")); dico.insert(std::make_pair("pi", "La seizième lettre de l'alphabet grec.")); dico.insert(std::make_pair("e","La base des logarithmes naturels.")); for (std::multimap<std::string,std::string>::const_iterator p=dico.begin(); p != dico.end(); ++p) std:: cout << p->first << " : " << p->second << std::endl; } Itérateurs y Un itérateur permet de faire abstraction d’un parcours sur un conteneur. y Les itérateurs miment la syntaxe des pointeurs qu’on peut observer sur l’exemple suivant : int N=100; double *t=new double[N]; for (double *p=t; p != t+N; ++p) *p=(*p)+1; y Les itérateurs représentent des intervalles semi-ouverts à droite. y On peut classer les itérateurs en plusieurs catégories : Itérateur Possibilités Types qui l’utilisent Entrée (input) Lecture de l’élément suivant istream Sortie (output) Écriture de l’élément suivant ostream, inserter Forward Lecture et écriture de l’élément suivant list, set, multiset, map, multimap Bi directionel Lecture et écriture de l’élément suivant et du précédent vector Accès direct Lecture et écriture d’un élément quelconque vector, deque, string, array y L’interface de chaque itérateur dépend de son type, plus un type a de possibilités plus son interface est riche. y Pour les itérateurs d’entrée : Expression Action *iter Accès en lecture à l’élément du conteneur iter->membre Accès en lecture au membre de l’élément du conteneur ++iter Avance vers la position suivante, retourne la nouvelle position. iter++ Avance vers la position suivante, retourne l’ancienne position. iter1 == iter2 Comparaison (égalité) iter1 != iter2 Comparaison (différent) TYPE(iter) Constructeur par copie. y Les itérateurs de sortie ont les opérations suivantes : Expression Action *iter = valeur Écrit dans l’élément auquel l’itérateur fait référence ++iter Avance vers la position suivante, retourne la nouvelle position. iter++ Avance vers la position suivante, retourne l’ancienne position. TYPE(iter) Constructeur par copie. y Les itérateurs forward sont une combinaison d’itérateurs d’entrée et de sortie, ils ont les opérations suivantes : Expression Action *iter Accède à l’élément auquel l’itérateur fait référence iter->membre Accède au membre de l’élément auquel l’itérateur fait référence. ++iter Avance vers la position suivante, retourne la nouvelle position. iter++ Avance vers la position suivante, retourne l’ancienne position. iter1==iter2 Comparaison (égalité) iter1!=iter2 Comparaison (différence) Expression Action TYPE() Constructeur par défaut. TYPE(iter) Constructeur par copie. iter1=iter2 Assignation y Les itérateurs bi-directionels sont des itérateurs forward avec les opérations supplémentaires : Expression Action --iter Recule vers la position précédente, retourne la nouvelle position. iter-- Recule vers la position précédente, retourne l’ancienne position. y Les itérateurs d’accès aléatoire sont des itérateurs bidirectionels avec les opérations supplémentaires : Expression Action iter[n] Accès à l’élément avec index n. iter+=n Avance de n éléments (recule si n négatif). iter-=n Recule de n éléments (avance si n négatif). iter+n Retourne un itérateur vers le prochain n-ième élément. iter-n Retourne un itérateur vers le précédent n-ième élément. iter1-iter2 Distance entre iter1 et iter2. iter1 < iter2 Vérifie si iter1 est avant iter2. Expression Action iter1 > iter2 Vérifie si iter1 est après iter2. iter1 <= iter2 Vérifie si iter1 n’est pas avant iter2. iter1>=iter2 Vérifie si iter1 n’est pas après iter2. y Chaque conteneur définit plusieurs types d’itérateus comme sous types, les deux plus utilisés sont : y y y C<T>::iterator C<T>::const_iterator Tout conteneur STL définit deux membres retournant des itérateurs permettent de le parcourir dans l’ordre de stockage : begin() qui référence le premier élément et end() qui fait référence à une position après le dernier élément. c.begin() c.end() Fonctions auxiliaires d’itérateurs y Trois fonctions auxiliaires sont définies pour tout itérateur, la complexité dépend du type d’itérateur : y advance : permet d’avancer un itérateur de n positions. O(1) pour les itérateurs d’accès aléatoire, O(n) sinon. y distance : permet de calculer la distance entre deux itérateurs atteignables. O(1) pour les itérateurs d’accès aléatoire, O(n) sinon. y iter_swap : échange les valeurs pointés par deux itérateurs. Fonctionne même si les itérateurs ne sont pas du même type. Itérateurs inversés y Un itérateur inversé permet de parcourir un conteneur en sens inverse de l’ordre de stockage. y Chaque conteneur définit plusieurs types d’itérateus comme sous types, les deux plus utilisés sont : y y C<T>::reverse_iterator C<T>::const_reverse_iterator y Tout conteneur STL définit deus itérateur inversés rbegin() et rend() qui pointent respectivement vers le dernier élément et vers une position avant le premier élément. c.rend() c.rbegin() #include <iostream> #include <vector> #include <cmath> int main() { std::vector<double> v; for (double x=1; x<10; ++x) v.push_back(sqrt(x)); for (std::vector<double>::iterator p=v.begin(); p < v.end(); ++p) { std::cout << *p << ", "; if ( fabs(*p-2.0) < 0.0001) *p=-10.00; } std::cout << std::endl; for (std::vector<double>::reverse_iterator p=v.rbegin(); p < v.rend(); ++p) std::cout << *p << ", "; std::cout << std::endl; return 0; } Itérateurs d’insertion y Un itérateur d’insertion est un adaptateur qui transforme une assignation en insertion. y Un itérateur d’insertion est un opérateur de sortie pour lequel les opérations suivantes ont un comportement spécial : y operator* n’a aucun effet et retourne *this, ceci a pour conséquence que *pos est équivalent à pos. y operator= a pour effet d’insérer dans le conteneur, en général les appels sont transformés vers push_back(), push_front() où insert(). Classe Fonction appelée Syntaxe back_insert_iterator push_back(valeur) back_inserter(cont) front_insert_iterator push_front(valeur) front_inserter(cont) insert_iterator insert(pos,valeur) inserter(cont,pos) #include <iostream> #include <list> #include <algorithm> void PRINT(const std::list<int> &v) { for (std::list<int>::const_iterator p=v.begin(); p!=v.end(); ++p) std::cout << *p << ", "; std::cout << std::endl; } int main() { std::list<int> v; std::back_insert_iterator<std::list<int> > iter(v); *iter=1; iter++; *iter=2; PRINT(v); front_inserter(v) = 44; std::list<int>::iterator p=v.begin(); p++; p++; inserter(v,p) =55; PRINT(v); return 0; } Foncteurs y Certains algorithmes acceptent des fonctions comme arguments de fonctions ou de templates. y Une classe qui surcharge l’opérateur () peut être utilisée à la place d’une fonction (comme argument d’une fonction représentant un algorithme). y Un certain nombre de fonctions simples sont prédéfinies dans la bibliothèque standard. y La bibliothèque standard fournit un certain nombre de templates et utilitaires qui permettent de produire des fonctions complexes à partir de fonctions simples. void PRINT(double i) { std::cout << i << std::endl; } class MyFunc { public: explicit MyFunc(double a) : a_(a) { } void operator()(double &x) const { x= a_ * sin(x); } void setA(double a) { a_=a; } private: double a_; }; int main() { std::list<double> l; for (int i=0; i<10; ++i) l.push_back(i/10.0); std::cout << "List1\n"; std::for_each(l.begin(),l.end(),PRINT); MyFunc asinx(1.0); std::for_each(l.begin(),l.end(),asinx); std::cout << "List1\n"; std::for_each(l.begin(),l.end(),PRINT); asinx.setA(2.0); std::for_each(l.begin(),l.end(),asinx); std::for_each(l.begin(),l.end(),PRINT); return 0; } Fonctions prédefinies : Expression Action negate<T>() -param plus<T>() param1+param2 minus<T>() param1-param2 multiplies<T>() param1*param2 divides<T>() param1/param2 modulus<T>() param1%param2 equal_to<T>() param1==param2 not_equal_to<T>() param1!=param2 less<T>() param1<param2 greater<T>() param1>param2 less_equal<T>() param1<=param2 Expression Action greater_equal<T>() param1>=param2 logical_not<T>() !param logical_and<T>() param1 && param2 logical_or<T>() param1 || param2 Adaptateurs : Expression Action bind1st(op,valeur) op(valeur,param) bind2nd(op,valeur) op(param,valeur) not1(op) !op(param) not2(op) !op(param1,param2) y L’utilisation des adptateurs demande que l’object fonction passé en argument définisse deux type : T::argument_type et T::result_type. y Si on utilise son propre type on peut le dériver des classes std::unary_function où std::binary_function. y Pour utiliser une fonction membre comme argument d’un adaptateur on doit utiliser std::mem_fun_ref où std::mem_fun si l’argument est un pointeur vers un objet. y Pour utiliser une fonction globale comme argument d’un adaptateur on doit utiliser l’adaptateur std::ptr_fun. #include <iostream> #include <list> #include <algorithm> #include <functional> #include <cmath> #include "boost/bind.hpp" int main() { std::list<double> l; void PRINT(double i) { std::cout <<i << std::endl; } std::cout << "List\n"; std::for_each(l.begin(),l.end(),PRINT); std::transform( l.begin(), l.end(), l.begin(), std::bind2nd(std::multiplies<double>(),2) ); std::cout << "List\n"; std::for_each(l.begin(),l.end(),PRINT); for (int i=0; i<10; ++i) l.push_back(i/10.0); return 0; } y Les adaptateurs de la bibliothèque standard ont deux limitations importantes : y Bind marche uniquement avec des fonctions de deux variables. y On ne peut pas composer des fonctions y La bibliothèque boost fournit une généralisation des adaptateurs standard qui règle ces problèmes. y Une seule fonction boost::bind qui traite directement jusqu’à 9 paramètres y Elle utilise des macros appelées placeholders (_1, _2, …, _9) pour marquer les places des paramètres. std::bind2nd(std::multiplies<double>(),2) Equivalent à : boost::bind(std::multiplies<double>(),_1,2) #include <iostream> #include <list> #include <algorithm> #include <functional> #include <cmath> #include "boost/bind.hpp" void PRINT(double i) { std::cout <<i << std::endl; } int main() { std::list<double> l; for (int i=0; i<10; ++i) l.push_back(i/10.0); std::cout << "List\n"; std::for_each(l.begin(),l.end(),PRINT); std::transform( l.begin(), l.end(), l.begin(), boost::bind(std::multiplies<double>(),boost::bind(sin,_1),2) ); std::cout << "List\n"; std::for_each(l.begin(),l.end(),PRINT); return 0; } Algorithmes y Les algorithmes agissent sur des conteneurs à travers des itérateurs, ont peut les classer en familles : y Non modificatifs y Modificatifs y Effacement y Changement d’ordre y Tri y Sur des données triés y Numériques y Quelques algorithmes ont des versions avec suffixes : y _if, quand deux formes de l’algorithme ont le même nombre de paramètres une acceptant une valeur et l’; autre une fonction. y _copy, quand un algorithme a une forme qui copie manipule les éléments sur place et une autre qui les copie, par exemple, reverse() inverse l’ordre des éléments et reverse_copy() copie les éléments dans un autre intervalle en inversant l’ordre. Algorithmes non modificatifs Nom Action for_each() Applique un opération à chaque élément count() Retourne le nombre d’éléments count_if() Retourne le nombre d’éléments vérifiant une condition min_element() Retourne le plus petit élément max_element() Retourne le plus grand élément find() Cherche un élément find_if() Cherche un élément vérifiant une condition search_n() Recherche les n premiers éléments avec une propriété search() Recherche la première occurrence d’un sous intervalle find_end() Recherche la dernière occurrence d’un sous intervalle find_first_of() Recherche le premier élément parmi une liste adjacent_find() Recherche des éléments adjacents qui sont égaux Nom Action equal() Vérifie que deux intervalles sont égaux mismatch() Retourne les premier s éléments différents de deux séquences lexicographical_compare() Retourne si un intervalle est lexicographiquement inférieur à un autre. Algorithmes modificatifs Nom Action for_each() Applique un opération à chaque élément copy() Copie un intervalle à partir du début copy_backward() Copie un intervalle à partir de la fin transform() Modifie et copie, où combine les éléments de 2 intervalles merge() Fusionne deux intervalles swap_ranges() Échange les éléments de deux intervalles fill() Remplace chaque élément avec une valeur fill_n() Remplace n éléments avec une valeur generate() Remplace chaque élément avec le résultat d’une opération generate_n() Remplace n éléments avec le résultat d’une opération Nom Action replace() Remplace les éléments avec une certaine valeur par une autre valeur replace_if() Remplace les éléments qui vérifient un critère par d’autres éléments replace_copy() Remplace en copiant les éléments avec une certaine valeur par une autre valeur replace_copy_if() Remplace en copiant les éléments qui vérifient un critère par d’autres éléments Algorithmes d’effacement Nom Action remove() Efface les éléments avec une valeur donnée remove_if() Efface les éléments qui vérifient un critère remove_copy() Copie les éléments qui n’ont pas une valeur donnée remove_copy_if() Copie les éléments qui ne vérifient pas un critère unique() Efface les éléments dupliqués adjacents unique_copy() Copie les éléments en effaçant les dupliqués Algorithmes de changement d’ordre Nom Action reverse() Inverse l’ordres des éléments reverse_copy() Copie les éléments en inverseant leur ordre rotate() Applique une rotation sur l’ordre des éléments rotate_copy() Copie les éléments en appliquant une rotation sur l’ordre next_permutation() Permutte l’ordre des éléments prev_permutation() Permutte l’ordre des éléments random_shuffle() Échange les éléments selon un ordre aléatoire partition() Change l’ordres des éléments de telle forme que ceux qui vérifient un critère se trouvent au début stable_partition() Comme partition() mais garantit la préservation de l’ordre des éléments qui vérifient et qui ne vérifient pas le critère. #include <iostream> #include <list> #include <vector> #include <algorithm> int main() { std::list<int> l; for (int i=1; i<=4; ++i) l.push_back(i); PRINT(l); std::vector<int> v; std::back_insert_iterator<std::vector<int> > iter(v); std::reverse_copy(l.begin(),l.end(),iter); PRINT(v); return 0; } Algorithmes de tri Nom Action sort() Trie les éléments stable_sort() Trie en préservant l’ordre des éléments égaux partial_sort() Trie jusqu’à ce que les n premiers éléments soient bons partial_sort_copy() Copie les éléments en les triant nth_element() Tri partiel en garantissant que les éléments avnt une position sont plus petits partition() Change l’ordre en garantissant que les premiers éléments satisfont un critère stable_partition() Comme partition() mais garantit la position des éléments égaux make_heap() Transforme un intervalle en tas push_heap() Ajoute un élément à un tas pop_heap() Efface un élément d’un tas sort_heap() Trie un tas Algorithmes pour données triés Nom Action binary_search() Cherche une valeur includes() Cherche un intervalle dans un autre intervalle lower_bound() Cherche le premier élément <= à une valeur upper_bound() Cherche le premier élément >= à une valeur equal_range() Cherche l’intervalles des éléments = à une valeur merge() Fusionne eux intervalles set_union() Retourne l’union de deux intervalles set_intersection() Retourne l’intersection de deux intervalles set_difference Retourne la différence de deux intervalles set_simetric_difference() Retourne la différence symétrique de deux intervalles inplace_merge() Fusionne deux intervalles consécutifs Algorithmes numériques Nom Action accumulate() Combine les éléments (somme, produit, etc). inner_product() Combine les éléments de deux intervalles adjacent_difference() Combine chaque élément avec son prédécesseur partial_sum() Combine chaque élément avec tous ses prédécesseurs Flots y Les flots sont utilisés pour les entrées sorties. y Avec les flots ont peut lire et écrire dans différents dispositifs : fichiers, strings, etc. y Les classes qui manipulent des fichiers sont : y std::istream, pour lecture y std::ostream, pour écriture y std::isstream, pour lecture écriture y Les classes qui manipulent des strings : y istringstream, pour lecture d’une chaine de caractères y ostringstream, pour écriture dans une chaine de caractères y L’écriture dans un stream se fait à l’aide de l’opérateur << y La lecture d’une stream se fait à l’aide de l’opérateur >> y Les opérateurs << et >> retournent une référence vers un stream. y On définit les opérateurs comme fonctions globales. Strings y La classe std::string représente les chaines de catractères.