Logo HE-ARC Logo HES-SO

1242.2 Langage C++ - 2025-2026

Chapitre 3

Surcharge des opérateurs

1. Introduction

2. Surcharges des opérateurs

3. Surcharge de << et >>

4. Surcharge de =

5. Autres surcharges (++, (), ...)

6. Bonnes pratiques

3.1 Introduction

Complex c1(2,3), c2(1,1), c3;

Comment les additionner ?


c3 = Complex::add(c1, c2);

ou

c3 = c1.Add(c2);


Idéalement, on aimerait :

c3 = c1 + c2;

3.2 Surcharge des opérateurs

Permet de redéfinir le comportement des opérateurs

Pour manipuler les objets comme des types simples


            
            Complex c1(2,3), c2(1,1), c3;
            c3 = c1 + c2;
            c1.show();
          

On ne peut pas redéfinir les opérateurs sur les types primitifs

Seulement pour les classes/struct définies par nous

Seuls les opérateurs existants peuvent être surchargés


✅ Conserver la sémantique initiale de l'opérateur

3.2 Surcharge des opérateurs

✅ Peuvent être surchargés



            =	+	-	*	/	^	%
            == 	+= 	-= 	*= 	/= 	^= 	%=
            < 	<= 	> 	>= 	<< 	>> 	<<=
            >>= 	++ 	-- 	& 	| 	! 	&=
            |= 	!= 	,	 [] 	()	&& 	||	
            ~	xor	xor_eq	and	and_eq	or 	or_eq
            not	not_eq	bitand	bitor	compl
          

⚠️ Dangeureux


            
            -> 	->* 	new 	new[]	delete	delete[]
          

⛔ Interdit


            
            .	.*	::	?:	sizeof
          

3.2 Surcharge des opérateurs

Pour chaque opérateur ♦, il existe une méthode operator♦(...)


c1 + c2; est transformé en c1.operator+(c2);

c1 = c2; est transformé en c1.operator=(c2);


p1 = p2 = p3; est transformé en p1.operator=(p2.operator=(p3));

3.2 Surcharge des opérateurs

A) Par une fonction membre



            // Complex.h
            class Complex 
            {
              public:
                // ⚠️ this and argument are const
                Complex operator+(const Complex &) const;
              private:
                  double m_real{0};
                  double m_imag{0};
            };
          


            // Complex.cpp
            Complex Complex::operator+(const Complex &c) const
            {
              return Complex(r + c.r, i + c.i);
            }
          

3.2 Surcharge des opérateurs

A) Par une fonction membre



            Complex c1, c2, c;
            int x, y;

            ...
            
            c = c1 + c2; // ✅ c = c1.operator+(c2);

            // ✅ if conversion exists
            c = c1 + y;  // ✅ c = c1.operator+(Complex(y));

            // ⛔ x.operator+(c2) does not compile
            c = x + c2;  // ⛔ x is not a Complex
          

3.2 Surcharge des opérateurs

B1) Par une fonction non-membre



            // ⚠️ Needs accessors
            Complex operator+(const Complex &x, const Complex &y)
            {
              return Complex(x.getR() + y.getR(), x.getI() + y.getI());
            }
          

B2) Par une fonction non-membre friend


            // Complex.h
            friend Complex operator+(const Complex&, const Complex&);
          

            // Complex.cpp
            Complex operator+(const Complex &x, const Complex &y)
            {
              return Complex(x.r + y.r, x.i + y.i);
            }
          

3.2 Surcharge des opérateurs

B) Par une fonction non-membre



            Complex c1, c2, c;
            int x, y;

            ...
            
            c = c1 + c2; // ✅ c = operator+(c1, c2);

            // ✅ if conversion exists
            c = c1 + y;  // ✅ c = operator+(c1, Complex(y));

            // ✅ if conversion exists
            c = x + c2;  // ✅ c = operator+(Complex(x), c2);
          

3.3 Surcharge de << et >>

Les opérateurs << et >> permettent d'insérer et d'extraire des objets dans les flux



            std::ostream& operator<<(std::ostream &s, const Point &p)
            {
              return s << '(' << p.m_x << ',' << p.m_y << ')';
            }
          

L'opérande de gauche est une référence sur le flux std::ostream

Cette fonction ne peut pas être membre de la classe Point

Le renvoi d'une référence permet de l'utiliser comme lvalue

On pourra donc faire des appels en cascade :



            std::cout << p1 << p2 << '\n';
            operator<<(operator<<(operator<<(std::cout, p1), p2), '\n');
                                  |----------------------| ostream&
                      |---------------------------------------| ostream&
            |-------------------------------------------------------| ostream&
          

3.3 Surcharge de << et >>

Exemple d'utilisation d'operator>>



            int main()
            {
              Point p;
              std::print("Enter a point (x,y): ");
              std::cin >> p;
              std::cout << "Point entered: " << p << '\n';
              return 0;
            }
          

On veut que l'utilisateur respecte un format prédéfini :

parenthèse-X-virgule-Y-parenthèse


            
            Enter a point (x,y): ( 2 , 3 )
            Point entered: (2,3)
          

3.3 Surcharge de << et >>



            std::istream& operator>>(std::istream &in, Point &p)
            {
              char c;
              in >> c;
              if (c == '(')
              {
                in >> p.m_x >> c;
                if (c == ',')
                {
                  in >> p.m_y >> c;
                  if (c == ')')
                    return in;
                }
              }
              in.setstate(std::ios::failbit);

              return in;
            }
          

3.4 Surcharge de =

L'affectation est une opération prédéfinie

Doit parfois être redéfini pour des copies en profondeur

⚠️ Affectation vs construction par copie



            int main()
            {
              NamedPoint pt(1, 4, "P1"), copyPt;

              // ⚠️ Copy constructor
              NamedPoint copyPt2 = pt;

              // ⚠️ Assignment
              copyPt = pt;

              return 0;
            }
          

3.4 Surcharge de =



            class NamedPoint
            {
            public:
              NamedPoint(int = 0, int = 0, const char* = "?");
              NamedPoint(const NamedPoint &);
              virtual ~NamedPoint();
              NamedPoint& operator=(const NamedPoint &);

            private:
              int m_x{0};
              int m_y{0};
              char* m_name{nullptr};
            };
          

3.4 Surcharge de =



            // ✅ Return reference to allow chaining
            NamedPoint& NamedPoint::operator=(const NamedPoint &p)
            {
              // ✅ Avoid self-assignment
              if (this == &p)
              {
                return *this;
              }

              m_x = p.m_x;
              m_y = p.m_y;
              delete[] m_name;
              m_name = nullptr;
              m_name = new char[strlen(p.m_name) + 1];
              strcpy_s(m_name, strlen(p.m_name) + 1, p.m_name);

              return *this;
            }
          

3.5 Autres surcharges

Les opérateurs de conversion



            class Point
            {
            public:
              // Conversion int → Point
              Point(int a) : m_x(a), m_y(0) {}

              // Conversion Point → int: Manhattan distance
              operator int() const
              {
                return std::abs(m_x) + std::abs(m_y);
              }

            private:
              int m_x{0};
              int m_y{0};
            };
          

3.5 Autres surcharges

Les opérateurs d'incrémentation et de décrémentation



            class Counter
            {
            public:
              Counter(int x);
              // PRE - Returns reference: no copy, usable as lvalue
              Counter& operator++()
              {
                ++m_x;
                return *this;
              }
              // POST - Standard C++: use int parameter to distinguish from pre
              Counter operator++(int)
              {
                Counter temp(*this);
                ++m_x;
                return temp;
              }

            private:
              int m_x{0};
            };
          

3.5 Autres surcharges

Les foncteurs — operator()

Un objet appelable comme une fonction, mais qui porte son propre état



            class Linear
            {
            public:
              Linear(double a, double b) : m_a(a), m_b(b) {}

              double operator()(double x) const
              {
                return m_a * x + m_b;
              }

            private:
              double m_a{0};
              double m_b{0};
            };
          


            Linear f(2, 1);   // f(x) = 2x + 1
            Linear g(-1, 0);  // g(x) = -x

            std::println("f(4) = {}", f(4));  // 9
            std::println("g(7) = {}", g(7));  // -7
          

3.6 Bonnes pratiques

Surcharger comme membres

operator= (obligatoire)

operator[] (2 fois) (obligatoire)

operator++ et operator-- (recommandé)


Surcharger comme non-membres

operator<< et operator>> (obligatoire)

✅ les opérateurs arithmétiques (recommandé)

✅ les opérateurs de comparaison (recommandé)


📚 Surcharge des opérateurs (HE-ARC)

3.6 Bonnes pratiques

The Rule of 3


Si une classe définit le destructeur, le constructeur par copie ou l'opérateur d'affectation, elle devrait probablement définir les 3