Opérateurs
Nom | C++ | Sens |
---|---|---|
Binaire | () [] -> |
-> |
Unaire | + - ++ -- ~ * & |
<- |
Unaire | new new[] delete delete[] (cast) |
-> |
Binaire | * / % |
-> |
Binaire | + - |
-> |
Binaire | << >> |
-> |
Binaire | == != |
-> |
Binaire | & |
-> |
Binaire | ^ |
-> |
Binaire | || |
-> |
Binaire | && |
-> |
Binaire | | |
-> |
Binaire | = += -= *= /= %= &= ^= |= <<= >>= |
<- |
Décomposition de comment une expression est gérer par l’ordinateur
((a+((f\times b)\times c)) = (d = (++a)))
flowchart TD
1[=] --> 2 & 3
2[+] --> 4 & 5
3[=] --> 8 & 9
4[a]
5[*] --> 6 & 7[c]
6[f]
7[b]
8[d]
9[++] --> 10
10[a]
Test d’opérateurs
testOp.cpp
:
#include<iostream>
class Frac {
int num;
int den;
public:
Frac(int n, int d) {
num = n;
den = d;
}
void afficher(void) {
std::cout << "num:" << num << ", den:" << den << std::endl;
}
Frac operator +(const Frac& a) const {
Frac retour(0, 0);
retour.num = a.num * den + num * a.den;
retour.den = a.den * den;
return retour;
}
friend Frac operator *(const Frac& a, const Frac& b);
};
Frac operator *(const Frac& a, const Frac& b) {
Frac retour(a.num, a.den);
retour.num *= b.num;
retour.den *= b.den;
return retour;
}
int main(void) {
Frac const a(3, 2);
Frac const b(1, 4);
Frac c = a + b; // Frac = a.operator + (b);
c.afficher();
return 0;
}
Il faut penser à modifier l’opérateur d’affectation quand on créer une classe qui utilises de la mémoire (pour renvoyer une copie d’un élément…) et éviter de perdre un élément.
Exemple depuis l’exemple précédent :
Frac operator =(const Frac& b) {
std::cout << "opérateur affectation" << std::endl;
num = b.num;
den = b.den;
return *this;
}
Ainsi dans le main…
c = a; // on garde a et on a un nouveau c
void maFonction(Frac a) {
a.afficher();
}
int main(void) {
maFonction(3);
}
Dans ce cas, le compilateur ne rale pas, malgré que la fonction maFonction
prenne un Frac
et non un int
, car il y a une conversion implicite de int
vers Frac
.
Bilan
Lorsqu’un classe (ex: class A { ... };
)est définie, alors par défaut est construit :
- un constructeur sans paramètre (ex:
A() { }
) - un destructeur (forcément sans paramètre) (ex:
~A() { }
) - un constructeur de recopie (ex:
A(A const & a) { ... }
) - un opérateur d’affectation (ex:
A operator =(const &) { }
)
Remarque
- L’écriture explicite par le concepteur de la classe d’un de ces quatres méthodes l’efface (en gros ces constructeurs par défaut ne sont pas construit si le dev en a écrit).
- On peut forcer le compilateur a ne pas construire l’une de ces quatres méthodes en écrivant :
<methode(...)=delete;
, exemple :
class A {
A(void) = delete; // constructeur
~A(void) = delete; // desctucteur
void operator =(...) = delete; // affectation
A(const A& a) = delete; // recopie
};
- On peut surcharger les opérateurs classiques de C++, exemple :
class A { ... };
A operator +(A a, A b) {// fonction qui renvoie un A
...
}
int main() {
A a;
A b;
...
... a + b ... sera transformé en : ... operator +(a, b) ...
...
return ...;
}
- Les opérateurs peuvent être surchargés à l’intérieur de la classe, exemple :
class A {
...
A operator +(A a) { // un seul paramètre car l'autre est *this
...
...
return ...;
}
};
- Conversion implicite par constructeur :
- SI A est une classe et E est l’identificateur d’un type alors un constructeur de la classe A(E\ a) définit une conversion implicite de E vers A.