La surcharge d’opérateurs
La surcharge d’opérateur est une fonctionnalité de la programmation orientée objet qui permet à un programmeur de redéfinir la manière dont un opérateur spécifique fonctionne pour des objets d’une classe personnalisée. En d’autres termes, la surcharge d’opérateur offre la possibilité de modifier le comportement d’opérateurs, pour qu’ils fonctionnent avec des objets créés par l’utilisateur. La surcharge d’opérateur permet aussi de surcharger des opérateurs non mathématiques, comme les opérateurs de comparaison. Ces derniers aident à simplifier la comparaison d’objets complexes et d’améliorer là aussi la lisibilité du code.
La surcharge d’opérateur est généralement mise en œuvre en définissant une méthode spécifique au sein de la classe dont les objets doivent être manipulés par l’opérateur. Cette méthode porte un nom particulier qui varie en fonction du langage de programmation.
Les opérateurs arithmétiques
La surcharge des opérateurs arithmétiques permet aux programmeurs de redéfinir la manière dont les opérations arithmétiques de base (telles que l’addition, la soustraction, la multiplication, la division, …) sont effectuées sur des objets de classes personnalisées. En surchargeant ces opérateurs, les développeurs peuvent simplifier la syntaxe du code et rendre les opérations sur des objets plus naturelles et intuitives, en imitant le comportement des types de données primitifs.
Pour surcharger un opérateur arithmétique, il est nécessaire de définir une méthode spéciale au sein de la classe dont les objets doivent être manipulés par cet opérateur. La méthode doit suivre la syntaxe appropriée pour chaque langage de programmation. Par exemple, en Python, on utilise des méthodes spéciales telles que __add__
, __sub__
, __mul__
, etc., tandis qu’en C++ et C#, on utilise des noms tels que operator+
, operator-
, operator*
, etc. Lors de la définition de ces méthodes, il est important de respecter la logique et la cohérence des opérations arithmétiques pour garantir une utilisation intuitive et sans ambiguïté.
class Heure: def __init__(self, heures, minutes): self.heures = heures self.minutes = minutes def __add__(self, autre_heure): total_minutes = self.minutes + autre_heure.minutes total_heures = self.heures + autre_heure.heures # Gérer le dépassement de 60 minutes. while total_minutes >= 60: total_minutes -= 60 total_heures += 1 return Heure(total_heures, total_minutes)
class Heure { public: Heure(int heures, int minutes) : heures(heures), minutes(minutes) {} Heure operator+(const Heure& autre_heure) { int total_minutes = minutes + autre_heure.minutes; int total_heures = heures + autre_heure.heures; // Gérer le dépassement de 60 minutes. while (total_minutes >= 60) { total_minutes -= 60; total_heures += 1; } return Heure(total_heures, total_minutes); } int heures; int minutes; };
public class Heure { public int Heures; public int Minutes; public Heure(int heures, int minutes) { Heures = heures; Minutes = minutes; } public static Heure operator +(Heure h1, Heure h2) { int totalMinutes = h1.Minutes + h2.Minutes; int totalHeures = h1.Heures + h2.Heures; // Gérer le dépassement de 60 minutes. while (totalMinutes >= 60) { totalMinutes -= 60; totalHeures += 1; } return new Heure(totalHeures, totalMinutes); } }
Dans cet exemple, la classe Heure représente une heure avec des attributs pour les heures et les minutes. L’opérateur d’addition est surchargé en définissant la méthode spéciale. Lorsque deux objets de la classe Heure sont additionnés, leurs minutes et heures respectives sont additionnées. Si le total des minutes dépasse 60, une heure est ajoutée au total des heures, et les minutes sont ajustées en conséquence.
Opérateur | Python | C++ | C# |
---|---|---|---|
Addition | __add__(self, o) | T operator+(const T& a, const T& b) | public static T operator +(T a, T b) |
Soustraction | __sub__(self, o) | T operator-(const T& a, const T& b) | public static T operator -(T a, T b) |
Multiplication | __mul__(self, o) | T operator*(const T& a, const T& b) | public static T operator *(T a, T b) |
Division | __truediv__(self, o) | T operator/(const T& a, const T& b) | public static T operator /(T a, T b) |
Division entière | __floordiv__(self, o) | Non existant | Non existant |
Modulo | __mod__(self, o) | T operator%(const T& a, const T& b) | public static T operator %(T a, T b) |
Les opérateurs de comparaisons
La surcharge des opérateurs de comparaison permet de redéfinir le comportement des opérations de comparaison pour les objets d’une classe personnalisée. Ces opérateurs incluent l’égalité, la non-égalité, l’infériorité, la supériorité. En surchargeant ces opérateurs, les développeurs peuvent comparer des objets de manière plus naturelle et intuitive, en fonction de critères spécifiques à la classe.
Lors de la surcharge des opérateurs de comparaison, il est important de respecter les principes de base des relations d’ordre. Ces principes comprennent la réflexivité, la symétrie et la transitivité. De plus, les opérateurs <, >, <= et >= doivent former un ordre cohérent sur les objets de la classe. Pour clarifier ces termes techniques, imaginons que les objets de notre classe soient des coureurs participant à une course. Chaque coureur a un temps de course unique qui détermine sa position dans la compétition. La surcharge des opérateurs de comparaison revient à définir les règles pour déterminer qui est en tête, qui est à égalité et qui est derrière. Dans ce contexte, les principes de base des relations d’ordre sont là pour garantir un classement juste et cohérent des coureurs.
- La réflexivité s’assure qu’un coureur ne peut pas être en même temps devant et derrière lui-même.
- La symétrie garantit que si un coureur A est à égalité avec un coureur B, alors le coureur B est également à égalité avec le coureur A.
- La transitivité assure que si un coureur A est à égalité avec un coureur B et que le coureur B est à égalité avec un coureur C, alors le coureur A est également à égalité avec le coureur C.
En respectant ces principes lors de la surcharge des opérateurs de comparaison, on s’assure que la comparaison des objets (ou des coureurs) reste cohérente et que leur classement est logique et compréhensible pour tous les participants et les spectateurs. Rassurez-vous, ce jargon technique revient à dire que vos opérateurs de comparaisons doivent globalement être cohérents.
class Coureur: def __init__(self, nom, temps_course): self.nom = nom self.temps_course = temps_course def __eq__(self, autre_coureur): if isinstance(autre_coureur, Coureur): return self.temps_course == autre_coureur.temps_course return False def __lt__(self, autre_coureur): if isinstance(autre_coureur, Coureur): return self.temps_course < autre_coureur.temps_course return False def __le__(self, autre_coureur): if isinstance(autre_coureur, Coureur): return self.temps_course <= autre_coureur.temps_course return False
class Coureur { public: std::string nom; double temps_course; Coureur(std::string nom, double temps_course) : nom(nom), temps_course(temps_course) {} bool operator==(const Coureur& autre_coureur) const { return temps_course == autre_coureur.temps_course; } bool operator<(const Coureur& autre_coureur) const { return temps_course < autre_coureur.temps_course; } bool operator<=(const Coureur& autre_coureur) const { return temps_course <= autre_coureur.temps_course; } };
public class Coureur { public string Nom; public double TempsCourse; public Coureur(string nom, double tempsCourse) { Nom = nom; TempsCourse = tempsCourse; } public static bool operator ==(Coureur coureur1, Coureur coureur2) { return coureur1.TempsCourse == coureur2.TempsCourse; } public static bool operator <(Coureur coureur1, Coureur coureur2) { return coureur1.TempsCourse < coureur2.TempsCourse; } public static bool operator <=(Coureur coureur1, Coureur coureur2) { return coureur1.TempsCourse <= coureur2.TempsCourse; } }
Opérateur | Python | C++ | C# |
---|---|---|---|
Est égal | __eq__(self, o) | bool operator==(const T& a, const T& b) | public static bool operator ==(T a, T b) public bool Equals(T other) |
Est différent | __ne__(self, o) | bool operator!=(const T& a, const T& b) | public static bool operator !=(T a, T b) |
Contiens | __contains__(self, o) | Non existant | Non existant |
Est plus petit que | __lt__(self, o) | bool operator<(const T& a, const T& b) | public static bool operator <(T a, T b) public int CompareTo(T other) |
Est plus petit ou égal que | __le__(self, o) | bool operator<=(const T& a, const T& b) | public static bool operator <=(T a, T b) |
Est plus grand que | __gt__(self, o) | bool operator>(const T& a, const T& b) | public static bool operator >(T a, T b) |
Est plus grand ou égal que | __ge__(self, o) | bool operator>=(const T& a, const T& b) | public static bool operator >=(T a, T b) |
Les opérateurs de conversions
La surcharge des opérateurs de conversion, également appelée surcharge des opérateurs de cast, est une technique de programmation orientée objet qui permet de définir comment un objet d’une classe doit être converti en un autre type, soit un type primitif ou un autre type d’objet. Ces opérateurs de conversion personnalisés offrent une grande flexibilité pour manipuler et interagir avec des objets de différentes classes, en permettant aux programmeurs de contrôler explicitement la façon dont les objets sont convertis d’un type à un autre.
Bien que la surcharge des opérateurs de conversion procure une grande flexibilité, il faut utiliser cette fonctionnalité avec prudence pour éviter des conversions inattendues ou des comportements ambigus. Les développeurs doivent s’assurer que les conversions personnalisées sont cohérentes, logiques et ne créent pas de confusion pour les utilisateurs de la classe.
class Panier: def __init__(self): self.fruits = [] def __bool__(self): return len(self.fruits) > 0 def __int__(self): return len(self.fruits)
class Panier { public: std::vector<std::string> fruits; explicit operator bool() const { return !fruits.empty(); } explicit operator int() const { return fruits.size(); } };
public class Panier { public List<string> fruits = new List<string>(); public static explicit operator bool(Panier panier) { return panier.fruits.Count > 0; } public static explicit operator int(Panier panier) { return panier.fruits.Count; } }
Opérateur | Python | C++ | C# |
---|---|---|---|
Conversion vers un entier | __int__(self) | operator int() const; | public static explicit operator int(T obj) |
Conversion vers un réel | __float__(self) |
| public static explicit operator float(T obj) |
Conversion vers un booléen | __bool__(self) | operator bool() const; | public static explicit operator bool(T obj) |
Conversion vers une chaine | __str__(self) | operator std::string() const; friend std::ostream& operator<<(std::ostream& os, const T& obj); |
public override string ToString() |