Programme de baccalauréat en informatique Programmation Orientée Objets IFT Thierry EUDE Module 5 : La surcharge des opérateurs Département dinformatique et de génie logiciel
2 Département dinformatique et de génie logiciel Définition Consiste à redéfinir des opérateurs usuels comme +, -, *, /, =, <, [], etc... sur de nouveaux types : les classes. Particulièrement intéressante avec -les nombres complexes, les fractions, les vecteurs, les matrices, les chaînes de caractères... Dans certaines situations, rend la lecture du code plus facile : d = d1.plus(d2) --> d = d1 + d2
3 Département dinformatique et de génie logiciel Listes des opérateurs permis Liste complète des opérateurs qui peuvent être surchargés :
4 Département dinformatique et de génie logiciel Ce qui n'est pas permis On ne peut pas surcharger les opérateurs :..* :: ?: On ne peut pas créer de nouveaux opérateurs : |x| i.e. valeur absolue y := x i.e. assignation à la Pascal y = x**2 i.e. x à la puissance 2.
5 Département dinformatique et de génie logiciel Définir un opérateur surchargé En C++, mot-clé operator suivi de l'opérateur. Les règles de préséance et d'associativité sont les mêmes que les opérateurs usuels. respecter ces règles pour ne pas avoir des résultats allant contre l'intuition. Il faut éviter les surprises et la confusion. Le langage ne renforce pas la signification d'un opérateur donné. il est possible de définir l'opérateur + qui réalise une soustraction... À éviter!
6 Département dinformatique et de génie logiciel Exemples class Date { public: bool operator== (const Date& obj) const; bool operator< (const Date& obj) const; friend std::ostream& operator<<(std::ostream& os, const Date& obj); private: long m_temps; }; bool Date::operator==(const Date& obj) const { return m_temps == obj.m_temps; } bool Date::operator<(const Date& obj) const { return m_temps < obj.m_temps; }
7 Département dinformatique et de génie logiciel Exemples std::ostream& operator<<(std::ostream& os, const Date& obj) { os << obj.m_temps; return os; } // --- Exemple d'utilisation void main() { Date d1; Date d2(4,11,2001); bool bEgal = (d1 == d2); //--- d1.operator==(d2) bool bInferieur = (d1 < d2); //--- d1.operator<(d2) cout << d1 << endl; //--- operator<<(cout, d1) cout << d2 << endl; //--- operator<<(cout, d2) }
8 Département dinformatique et de génie logiciel Opérateurs complémentaires Le langage C++ ne rend pas équivalent les opérateurs v += w et v = v + w. Il faut définir les opérateurs +=, = et +. x++ pas nécessairement égal à x = x + 1 La règle : définir un ensemble complet d'opérateurs qui ont des interactions naturelles entre eux : a = a + b --> a += b.
9 Département dinformatique et de génie logiciel Conversion de type Conversion d'un objet d'un type en un autre type : possible avec la surcharge des opérateurs. Exemple : Soit une string, il peut être intéressant de définir une conversion en const char* pour passer facilement en argument de beaucoup de librairies. Il s'agit ici de déterminer ce qui est préférable : conversion implicite ou conversion explicite?
10 Département dinformatique et de génie logiciel Exemple class string { public: operator const char*() const; const char* c_str () const; private: char* m_strP; }; // --- Conversion implicite string::operator const char*() const { return m_strP; } // --- Conversion explicite const char* string::c_str() const { return m_strP; }
11 Département dinformatique et de génie logiciel Exemple // --- fopen (const char* fP) est une fonction du // --- C standard qui permet d'ouvrir un fichier. void main() { string nomFichier("fichier.txt"); // --- Conversion implicite, i.e. appel // --- de l'opérateur de conversion en const char* FILE *file1P = fopen(nomFichier); // --- Conversion explicite, i.e. appel // --- explicite de la méthode string::c_str(). FILE *file2P = fopen(nomFichier.c_str()); } Il faut éviter les conversions implicites : On ne contrôle pas ce qui ce passe et on risque d'avoir des surprises et des imprévus...
12 Département dinformatique et de génie logiciel Conversion avec un constructeur Il est possible de convertir un type vers un autre en passant par un constructeur. Exemple : un char* peut être convertis en string parce que la string a défini un constructeur avec un const char* : string (const char* strP); conversion implicite. La string pourrait avoir un constructeur acceptant un double. -Pour contrôler quand cette conversion se fera : ajouter le mot-clé explicit devant le constructeur : explicit string (double d); conversion explicite.
13 Département dinformatique et de génie logiciel Exemple class string { public: string(const char* strP); explicit string(double d); private: char* m_strP; }; string::string(const char* strP) { PRECONDITION (strP != 0); m_strP = new char[strlen(strP)+1]; strcpy(m_strP, strP); } string::string(double d) { ostringstream os; os << d; *this = os.str(); }
14 Département dinformatique et de génie logiciel Exemple void foo (string str); void main() { double d = 2.2; // --- Conversion implicite par le constructeur foo ("Bonjour la police"); // Error : no implicit conversion from double to string foo (d); // --- Ok: appel explicite pour la conversion foo (string(d)); }
15 Département dinformatique et de génie logiciel Opérateur [] Permet d'obtenir un comportement vectoriel pour des classes dont le concept correspond. Exemple : string il faut vérifier en précondition la validité de l'indice demandé.
16 Département dinformatique et de génie logiciel Exemple class string { char& operator[](int pos); const char& operator[](int pos) const; }; char& string::operator[](int pos) { PRECONDITION(pos >= 0 && pos < size()); return m_strP[pos]; } const char& string::operator[](int pos) const { PRECONDITION(pos >= 0 && pos < size()); return m_strP[pos]; } Version en modification Version en consultation
17 Département dinformatique et de génie logiciel Exemple // --- Utilisation de l'opérateur [] void main() { string str("toto"); // --- Appel en consultation cout << str[3]; // --- imprime 'o' // --- Appel en modification str[0] = 'm'; // --- str devient "moto" }
18 Département dinformatique et de génie logiciel Opérateur () Peut servir par exemple pour accéder une matrice à 2 dimensions. class Matrice2D { public: Matrice2D(int r, int c); double operator() (int r, int c) const; private: vector m_matrice; int m_nbRangee; int m_nbColonne; };
19 Département dinformatique et de génie logiciel Exemple double Matrice2D::operator()(int r, int c) const { PRECONDITION(r > 0 && r <= m_nbRangee); PRECONDITION(c > 0 && c <= m_nbColonne); return m_matrice[(r-1)*m_nbColonne + c]; } void main() { Matrice2D m(5,5);... // On met des données dans la matrice // --- On accède à l'élément de la 3e rangée // --- et de la 4e colonne. double x = m(3, 4); }
20 Département dinformatique et de génie logiciel Conversion en valeur de vérité Conversion pour l'état d'un objet : la conversion en void*. Exemple, la classe std::istream définit la conversion en void*. Intérêt : ce type de pointeur ne peut être utilisé que dans un test logique ou une assignation. Cela fournit une manière intéressante d'interroger l'état d'un objet. La librairie des iostream du standard utilise largement ce type de conversion.
21 Département dinformatique et de génie logiciel Exemple … template > class basic_istream;basic_istream typedef basic_istream > istream;istream … basic_istream An object of class basic_istream stores: A virtual public base object of class basic_ios.basic_ios basic_ios::operator void * operator void *operator void *() const; The operator returns a null pointer only if fail().fail void main() { int x; cin >> x; if (cin) // --- appel de l'opérateur de conversion { // --- pas d'erreur, continuer le traitement }
22 Département dinformatique et de génie logiciel Synthèse Surcharge dopérateur constructeur [] () vérité