Învață să personalizezi comportamentul operatorilor pentru clasele tale
Supraîncărcarea operatorilor (operator overloading) este o caracteristică puternică a C++ care permite definirea comportamentului personalizat pentru operatori standard (+, -, *, /, ==, etc.) când sunt aplicați obiectelor claselor definite de utilizator.
Operatorii pot fi supraîncărcați ca funcții membre sau funcții non-membre (prietene):
// Sintaxa generală pentru funcție membră ReturnType operatorop(ParameterList); // Sintaxa generală pentru funcție non-membră friend ReturnType operatorop(ParameterList);
class Complex { private: double real, imag; public: Complex(double r = 0, double i = 0) : real(r), imag(i) {} // Supraîncărcarea operatorului + ca funcție membră Complex operator+(const Complex& other) const { return Complex(real + other.real, imag + other.imag); } // Supraîncărcarea operatorului << ca funcție prieten friend ostream& operator<<(ostream& os, const Complex& c) { os << c.real << " + " << c.imag << "i"; return os; } };
| Categorie | Operatori | Exemple |
|---|---|---|
| Aritmetici | + - * / % | Adunare, scădere, înmulțire |
| Relaționali | == != < > <= >= | Comparații între obiecte |
| Logici | && || ! | Operații logice |
| Atribuire | = += -= *= /= | Atribuire și operații combinate |
| Increment/Decrement | ++ -- | Prefix și postfix |
| Acces | [] () -> | Acces la elemente, apel funcție |
| I/O | << >> | Input și output streams |
Cuvântul cheie friend permite funcțiilor sau claselor externe să acceseze membrii privați și protejați ai unei clase. Aceasta este esențială pentru supraîncărcarea anumitor operatori.
2 * vector)class Complex { private: double real, imag; public: Complex(double r = 0, double i = 0) : real(r), imag(i) {} // Friend function pentru operator+ (permite 2 + obj) friend Complex operator+(const Complex& c1, const Complex& c2); // Friend function pentru operator* cu scalar friend Complex operator*(double scalar, const Complex& c); friend Complex operator*(const Complex& c, double scalar); // Friend function pentru operator<< (output) friend ostream& operator<<(ostream& os, const Complex& c); // Friend function pentru operator>> (input) friend istream& operator>>(istream& is, Complex& c); }; // Implementarea friend functions în afara clasei Complex operator+(const Complex& c1, const Complex& c2) { return Complex(c1.real + c2.real, c1.imag + c2.imag); } // Permite: 5.0 * complex_number Complex operator*(double scalar, const Complex& c) { return Complex(scalar * c.real, scalar * c.imag); } // Permite: complex_number * 5.0 Complex operator*(const Complex& c, double scalar) { return Complex(c.real * scalar, c.imag * scalar); } ostream& operator<<(ostream& os, const Complex& c) { os << c.real; if(c.imag >= 0) os << " + " << c.imag << "i"; else os << " - " << -c.imag << "i"; return os; } istream& operator>>(istream& is, Complex& c) { cout << "Partea reală: "; is >> c.real; cout << "Partea imaginară: "; is >> c.imag; return is; }
class Matrix; // Forward declaration class Vector { private: double* data; size_t size; public: Vector(size_t n) : size(n) { data = new double[size]; } // Matrix este friend class - poate accesa toți membrii privați friend class Matrix; // Friend function pentru înmulțire matrice-vector friend Vector operator*(const Matrix& m, const Vector& v); }; class Matrix { private: double** data; size_t rows, cols; public: Matrix(size_t r, size_t c) : rows(r), cols(c) { data = new double*[rows]; for(size_t i = 0; i < rows; ++i) data[i] = new double[cols]; } // Poate accesa direct data[] din Vector pentru eficiență void setColumn(size_t col, const Vector& v) { if(col < cols && v.size == rows) { for(size_t i = 0; i < rows; ++i) data[i][col] = v.data[i]; // Acces direct la membri privați } } friend Vector operator*(const Matrix& m, const Vector& v); };
class Rational { private: int numerator, denominator; public: Rational(int n = 0, int d = 1) : numerator(n), denominator(d) {} // MEMBER FUNCTION - operandul stâng trebuie să fie Rational // Apel: rational1.operator+(rational2) Rational operator+(const Rational& other) const { return Rational( numerator * other.denominator + other.numerator * denominator, denominator * other.denominator ); } // FRIEND FUNCTION - permite conversii implicite pentru ambii operanzi // Apel: operator+(rational1, 2) sau operator+(2, rational1) friend Rational operator*(const Rational& r1, const Rational& r2) { return Rational( r1.numerator * r2.numerator, r1.denominator * r2.denominator ); } // Constructor de conversie - permite int -> Rational Rational(int n) : numerator(n), denominator(1) {} }; // Utilizare: // Rational r1(3, 4); // Rational r2 = r1 + Rational(2); // OK cu member function // Rational r3 = 2 + r1; // EROARE cu member, OK cu friend! // Rational r4 = r1 * 2; // OK cu friend // Rational r5 = 2 * r1; // OK cu friend
class Matrix2x2 { private: double m[2][2]; public: Matrix2x2(double a = 0, double b = 0, double c = 0, double d = 0) { m[0][0] = a; m[0][1] = b; m[1][0] = c; m[1][1] = d; } // Friend pentru adunare matrice friend Matrix2x2 operator+(const Matrix2x2& m1, const Matrix2x2& m2) { return Matrix2x2( m1.m[0][0] + m2.m[0][0], m1.m[0][1] + m2.m[0][1], m1.m[1][0] + m2.m[1][0], m1.m[1][1] + m2.m[1][1] ); } // Friend pentru înmulțire cu scalar (ambele direcții) friend Matrix2x2 operator*(double k, const Matrix2x2& mat) { return Matrix2x2( k * mat.m[0][0], k * mat.m[0][1], k * mat.m[1][0], k * mat.m[1][1] ); } friend Matrix2x2 operator*(const Matrix2x2& mat, double k) { return k * mat; // Reutilizăm operatorul de mai sus } // Friend pentru comparație friend bool operator==(const Matrix2x2& m1, const Matrix2x2& m2) { for(int i = 0; i < 2; ++i) for(int j = 0; j < 2; ++j) if(m1.m[i][j] != m2.m[i][j]) return false; return true; } // Friend pentru afișare formatată friend ostream& operator<<(ostream& os, const Matrix2x2& mat) { os << "[" << mat.m[0][0] << " " << mat.m[0][1] << "]" << endl; os << "[" << mat.m[1][0] << " " << mat.m[1][1] << "]"; return os; } };
| Criteriu | Funcție Membră | Funcție Friend |
|---|---|---|
| Sintaxă apel | obj1.operator+(obj2) |
operator+(obj1, obj2) |
| Acces la membri privați | Direct (este membră) | Necesită declarare friend |
| Conversii implicite stânga | ❌ Nu permite | ✅ Permite |
| Operatori I/O (<<, >>) | ❌ Nu pot fi membri | ✅ Trebuie să fie friend |
| Încapsulare | ✅ Păstrează încapsularea | ⚠️ Încalcă parțial |
| Comutativitate | ❌ Doar obj * scalar | ✅ scalar * obj și obj * scalar |
Exemplele sunt organizate pe trei nivele pentru a facilita înțelegerea graduală a conceptelor.
class Point { private: int x, y; public: Point(int x = 0, int y = 0) : x(x), y(y) {} // Operator + simplu Point operator+(const Point& p) const { return Point(x + p.x, y + p.y); } // Operator == pentru comparație bool operator==(const Point& p) const { return (x == p.x && y == p.y); } // Afișare simplă void display() const { cout << "(" << x << ", " << y << ")" << endl; } }; // Utilizare: Point p1(3, 4), p2(1, 2); Point p3 = p1 + p2; // (4, 6) if(p1 == p2) { /* ... */ }
class SimpleCounter { private: int count; public: SimpleCounter(int c = 0) : count(c) {} // Prefix increment SimpleCounter& operator++() { ++count; return *this; } // Operator de conversie la int operator int() const { return count; } // Operator unar - (negare) SimpleCounter operator-() const { return SimpleCounter(-count); } }; SimpleCounter c(5); ++c; // count = 6 int val = c; // conversie implicită la int SimpleCounter neg = -c; // count = -6
class Box { private: double length, width, height; public: Box(double l = 1, double w = 1, double h = 1) : length(l), width(w), height(h) {} double volume() const { return length * width * height; } // Comparare pe bază de volum bool operator<(const Box& b) const { return volume() < b.volume(); } bool operator>(const Box& b) const { return volume() > b.volume(); } bool operator==(const Box& b) const { return volume() == b.volume(); } }; Box box1(2, 3, 4), box2(1, 5, 5); if(box1 < box2) { cout << "Box1 este mai mică" << endl; }
class Fraction { private: int numerator, denominator; int gcd(int a, int b) const { return b == 0 ? a : gcd(b, a % b); } void simplify() { int g = gcd(abs(numerator), abs(denominator)); numerator /= g; denominator /= g; if(denominator < 0) { numerator = -numerator; denominator = -denominator; } } public: Fraction(int n = 0, int d = 1) : numerator(n), denominator(d) { if(d == 0) throw runtime_error("Denominator zero!"); simplify(); } // Operatori aritmetici Fraction operator+(const Fraction& f) const { return Fraction( numerator * f.denominator + f.numerator * denominator, denominator * f.denominator ); } Fraction operator-(const Fraction& f) const { return Fraction( numerator * f.denominator - f.numerator * denominator, denominator * f.denominator ); } Fraction operator*(const Fraction& f) const { return Fraction( numerator * f.numerator, denominator * f.denominator ); } Fraction operator/(const Fraction& f) const { if(f.numerator == 0) throw runtime_error("Împărțire cu zero!"); return Fraction( numerator * f.denominator, denominator * f.numerator ); } // Operatori compuși Fraction& operator+=(const Fraction& f) { *this = *this + f; return *this; } // Operatori de comparație bool operator==(const Fraction& f) const { return numerator == f.numerator && denominator == f.denominator; } bool operator<(const Fraction& f) const { return numerator * f.denominator < f.numerator * denominator; } // Conversie la double operator double() const { return static_cast<double>(numerator) / denominator; } // Friend pentru I/O friend ostream& operator<<(ostream& os, const Fraction& f) { os << f.numerator; if(f.denominator != 1) os << "/" << f.denominator; return os; } };
class MyString { private: char* str; size_t len; public: MyString(const char* s = "") { len = strlen(s); str = new char[len + 1]; strcpy(str, s); } MyString(const MyString& other) { len = other.len; str = new char[len + 1]; strcpy(str, other.str); } ~MyString() { delete[] str; } // Operator de atribuire MyString& operator=(const MyString& other) { if(this != &other) { delete[] str; len = other.len; str = new char[len + 1]; strcpy(str, other.str); } return *this; } // Operator + pentru concatenare MyString operator+(const MyString& other) const { char* temp = new char[len + other.len + 1]; strcpy(temp, str); strcat(temp, other.str); MyString result(temp); delete[] temp; return result; } // Operator [] pentru acces la caractere char& operator[](size_t index) { if(index >= len) throw out_of_range("Index invalid!"); return str[index]; } const char& operator[](size_t index) const { if(index >= len) throw out_of_range("Index invalid!"); return str[index]; } // Operatori de comparație bool operator==(const MyString& other) const { return strcmp(str, other.str) == 0; } bool operator<(const MyString& other) const { return strcmp(str, other.str) < 0; } // Friend pentru output friend ostream& operator<<(ostream& os, const MyString& s) { os << s.str; return os; } // Friend pentru input friend istream& operator>>(istream& is, MyString& s) { char buffer[1000]; is >> buffer; s = MyString(buffer); return is; } };
template<typename T> class Matrix { private: vector<vector<T>> data; size_t rows, cols; public: Matrix(size_t r, size_t c, T val = T()) : rows(r), cols(c), data(r, vector<T>(c, val)) {} // Operator () pentru acces element T& operator()(size_t i, size_t j) { if(i >= rows || j >= cols) throw out_of_range("Index în afara matricei!"); return data[i][j]; } const T& operator()(size_t i, size_t j) const { if(i >= rows || j >= cols) throw out_of_range("Index în afara matricei!"); return data[i][j]; } // Operator + pentru adunare matrice Matrix operator+(const Matrix& other) const { if(rows != other.rows || cols != other.cols) throw invalid_argument("Dimensiuni incompatibile!"); Matrix result(rows, cols); for(size_t i = 0; i < rows; ++i) for(size_t j = 0; j < cols; ++j) result(i, j) = data[i][j] + other.data[i][j]; return result; } // Operator * pentru înmulțire matrice Matrix operator*(const Matrix& other) const { if(cols != other.rows) throw invalid_argument("Dimensiuni incompatibile pentru înmulțire!"); Matrix result(rows, other.cols, T()); for(size_t i = 0; i < rows; ++i) for(size_t j = 0; j < other.cols; ++j) for(size_t k = 0; k < cols; ++k) result(i, j) += data[i][k] * other.data[k][j]; return result; } // Operator * pentru înmulțire cu scalar template<typename Scalar> friend Matrix operator*(const Scalar& s, const Matrix& m) { Matrix result(m.rows, m.cols); for(size_t i = 0; i < m.rows; ++i) for(size_t j = 0; j < m.cols; ++j) result(i, j) = s * m.data[i][j]; return result; } // Transpusă Matrix operator~() const { Matrix result(cols, rows); for(size_t i = 0; i < rows; ++i) for(size_t j = 0; j < cols; ++j) result(j, i) = data[i][j]; return result; } };
template<typename T> class SmartPtr { private: T* ptr; size_t* ref_count; void release() { if(ref_count && --(*ref_count) == 0) { delete ptr; delete ref_count; } } public: // Constructor explicit SmartPtr(T* p = nullptr) : ptr(p) { ref_count = ptr ? new size_t(1) : nullptr; } // Copy constructor SmartPtr(const SmartPtr& other) : ptr(other.ptr), ref_count(other.ref_count) { if(ref_count) ++(*ref_count); } // Destructor ~SmartPtr() { release(); } // Operator de atribuire SmartPtr& operator=(const SmartPtr& other) { if(this != &other) { release(); ptr = other.ptr; ref_count = other.ref_count; if(ref_count) ++(*ref_count); } return *this; } // Operator -> pentru acces la membri T* operator->() const { if(!ptr) throw runtime_error("Dereferențiere pointer null!"); return ptr; } // Operator * pentru dereferențiere T& operator*() const { if(!ptr) throw runtime_error("Dereferențiere pointer null!"); return *ptr; } // Operator bool pentru verificare validitate operator bool() const { return ptr != nullptr; } // Operator == pentru comparație bool operator==(const SmartPtr& other) const { return ptr == other.ptr; } // Get reference count size_t use_count() const { return ref_count ? *ref_count : 0; } }; // Utilizare: SmartPtr<int> sp1(new int(42)); SmartPtr<int> sp2 = sp1; // Share ownership cout << *sp1 << endl; // Dereferențiere if(sp1) { // Verificare validitate // ... }
template<typename T> class DynamicArray { private: T* data; size_t size; public: // Clasa Iterator internă class Iterator { private: T* ptr; public: Iterator(T* p) : ptr(p) {} // Operator * pentru dereferențiere T& operator*() const { return *ptr; } // Operator -> pentru acces membri T* operator->() const { return ptr; } // Operator ++ prefix Iterator& operator++() { ++ptr; return *this; } // Operator ++ postfix Iterator operator++(int) { Iterator temp = *this; ++ptr; return temp; } // Operator -- prefix Iterator& operator--() { --ptr; return *this; } // Operator + pentru aritmetică pointer Iterator operator+(int n) const { return Iterator(ptr + n); } // Operator - pentru diferență ptrdiff_t operator-(const Iterator& other) const { return ptr - other.ptr; } // Operatori de comparație bool operator==(const Iterator& other) const { return ptr == other.ptr; } bool operator!=(const Iterator& other) const { return ptr != other.ptr; } bool operator<(const Iterator& other) const { return ptr < other.ptr; } }; DynamicArray(size_t n) : size(n) { data = new T[size]; } ~DynamicArray() { delete[] data; } // Funcții begin și end pentru range-based for Iterator begin() { return Iterator(data); } Iterator end() { return Iterator(data + size); } // Operator [] pentru acces indexat T& operator[](size_t i) { return data[i]; } }; // Utilizare: DynamicArray<int> arr(5); for(auto it = arr.begin(); it != arr.end(); ++it) { *it = 42; } // Sau cu range-based for: for(auto& elem : arr) { elem = 100; }
template<typename T> class Polynomial { private: vector<T> coefficients; // a0 + a1*x + a2*x^2 + ... public: Polynomial(initializer_list<T> coeffs) : coefficients(coeffs) {} // Operator () pentru evaluare polinom T operator()(T x) const { T result = T(); T power = T(1); for(const auto& coeff : coefficients) { result += coeff * power; power *= x; } return result; } // Operator + pentru adunare polinoame Polynomial operator+(const Polynomial& other) const { size_t maxSize = max(coefficients.size(), other.coefficients.size()); vector<T> result(maxSize, T()); for(size_t i = 0; i < coefficients.size(); ++i) result[i] += coefficients[i]; for(size_t i = 0; i < other.coefficients.size(); ++i) result[i] += other.coefficients[i]; return Polynomial(initializer_list<T>( result.begin(), result.end())); } // Operator * pentru înmulțire polinoame Polynomial operator*(const Polynomial& other) const { vector<T> result(coefficients.size() + other.coefficients.size() - 1, T()); for(size_t i = 0; i < coefficients.size(); ++i) for(size_t j = 0; j < other.coefficients.size(); ++j) result[i + j] += coefficients[i] * other.coefficients[j]; return Polynomial(initializer_list<T>( result.begin(), result.end())); } // Derivată Polynomial derivative() const { if(coefficients.size() <= 1) return Polynomial({T()}); vector<T> result; for(size_t i = 1; i < coefficients.size(); ++i) result.push_back(coefficients[i] * T(i)); return Polynomial(initializer_list<T>( result.begin(), result.end())); } friend ostream& operator<<(ostream& os, const Polynomial& p) { bool first = true; for(size_t i = 0; i < p.coefficients.size(); ++i) { if(p.coefficients[i] != T()) { if(!first) os << " + "; os << p.coefficients[i]; if(i > 0) os << "x^" << i; first = false; } } return os; } }; // Utilizare: Polynomial<double> p1{2, 3, 1}; // 2 + 3x + x^2 Polynomial<double> p2{1, -1}; // 1 - x auto p3 = p1 * p2; // Înmulțire double val = p1(2.0); // Evaluare: 2 + 6 + 4 = 12 auto dp = p1.derivative(); // Derivată: 3 + 2x
class Vector2D { private: double x, y; public: Vector2D(double x = 0, double y = 0) : x(x), y(y) {} // Operator + (adunare vectori) Vector2D operator+(const Vector2D& v) const { return Vector2D(x + v.x, y + v.y); } // Operator - (scădere vectori) Vector2D operator-(const Vector2D& v) const { return Vector2D(x - v.x, y - v.y); } // Operator * (înmulțire cu scalar) Vector2D operator*(double scalar) const { return Vector2D(x * scalar, y * scalar); } // Operator == (egalitate) bool operator==(const Vector2D& v) const { return (x == v.x && y == v.y); } // Operator += (adunare cu atribuire) Vector2D& operator+=(const Vector2D& v) { x += v.x; y += v.y; return *this; } // Operator << pentru afișare friend ostream& operator<<(ostream& os, const Vector2D& v) { os << "(" << v.x << ", " << v.y << ")"; return os; } };
class SafeArray { private: int* data; size_t size; public: SafeArray(size_t n) : size(n) { data = new int[size]; for(size_t i = 0; i < size; ++i) data[i] = 0; } ~SafeArray() { delete[] data; } // Operator [] pentru acces la elemente int& operator[](size_t index) { if(index >= size) { throw out_of_range("Index în afara limitelor!"); } return data[index]; } // Versiune const pentru operator [] const int& operator[](size_t index) const { if(index >= size) { throw out_of_range("Index în afara limitelor!"); } return data[index]; } };
class Counter { private: int value; public: Counter(int v = 0) : value(v) {} // Operator ++ prefix (++counter) Counter& operator++() { ++value; return *this; } // Operator ++ postfix (counter++) Counter operator++(int) { Counter temp = *this; ++value; return temp; } // Operator -- prefix (--counter) Counter& operator--() { --value; return *this; } // Operator -- postfix (counter--) Counter operator--(int) { Counter temp = *this; --value; return temp; } int getValue() const { return value; } };
const pentru operatori care nu modifică obiectul=, [], (), ->, operatori unari<<, >>+=, -=, etc.)class String { private: char* str; size_t length; public: // Operator de atribuire cu verificare auto-atribuire String& operator=(const String& other) { // Verificare auto-atribuire if(this != &other) { // Eliberare memorie veche delete[] str; // Copiere date noi length = other.length; str = new char[length + 1]; strcpy(str, other.str); } return *this; } };
Alege nivelul de dificultate și vezi exemple în acțiune:
Salvează această secțiune pentru consultare rapidă când implementezi operatori!
class MyClass { private: Type data; public: // === OPERATORI ARITMETICI === MyClass operator+(const MyClass& rhs) const; MyClass operator-(const MyClass& rhs) const; MyClass operator*(const MyClass& rhs) const; MyClass operator/(const MyClass& rhs) const; MyClass operator%(const MyClass& rhs) const; // === OPERATORI COMPUȘI === MyClass& operator+=(const MyClass& rhs); MyClass& operator-=(const MyClass& rhs); MyClass& operator*=(const MyClass& rhs); MyClass& operator/=(const MyClass& rhs); // === INCREMENT/DECREMENT === MyClass& operator++(); // Prefix ++obj MyClass operator++(int); // Postfix obj++ MyClass& operator--(); // Prefix --obj MyClass operator--(int); // Postfix obj-- // === OPERATORI UNARI === MyClass operator-() const; // Negare MyClass operator+() const; // Plus unar bool operator!() const; // NOT logic MyClass operator~() const; // Complement // === COMPARAȚIE === bool operator==(const MyClass& rhs) const; bool operator!=(const MyClass& rhs) const; bool operator<(const MyClass& rhs) const; bool operator>(const MyClass& rhs) const; bool operator<=(const MyClass& rhs) const; bool operator>=(const MyClass& rhs) const; // === C++20 Three-way comparison === auto operator<=>(const MyClass& rhs) const = default; // === ATRIBUIRE === MyClass& operator=(const MyClass& rhs); // Copy MyClass& operator=(MyClass&& rhs) noexcept; // Move C++11 // === ACCES === Type& operator[](size_t index); const Type& operator[](size_t index) const; Type* operator->(); const Type* operator->() const; Type& operator*(); const Type& operator*() const; // === APEL FUNCȚIE === RetType operator()(Args... args); // === CONVERSII === operator bool() const; // Conversie la bool explicit operator int() const; // Conversie explicită // === I/O (FRIEND) === friend ostream& operator<<(ostream& os, const MyClass& obj); friend istream& operator>>(istream& is, MyClass& obj); // === OPERATORI BINARI SIMETRICI (FRIEND) === friend MyClass operator+(Type lhs, const MyClass& rhs); friend MyClass operator*(Type lhs, const MyClass& rhs); };
| Tip Operator | Membru/Friend | Return Type | Const? | Exemplu |
|---|---|---|---|---|
| Aritmetic binar | Membru sau Friend | Obiect nou (by value) | Da | T operator+(const T&) const |
| Compus (+=, -=) | Membru | Referință (*this) | Nu | T& operator+=(const T&) |
| Prefix ++/-- | Membru | Referință (*this) | Nu | T& operator++() |
| Postfix ++/-- | Membru | Obiect nou (copie) | Nu | T operator++(int) |
| Comparație | Membru sau Friend | bool | Da | bool operator==(const T&) const |
| Atribuire (=) | DOAR Membru | Referință (*this) | Nu | T& operator=(const T&) |
| Subscript [] | DOAR Membru | Referință element | 2 versiuni | T& operator[](size_t) |
| I/O (<< >>) | DOAR Friend/Non-membru | Referință stream | - | friend ostream& operator<< |
Încearcă să rezolvi aceste exerciții pentru a-ți consolida cunoștințele despre supraîncărcarea operatorilor.
Creează o clasă Time care reprezintă timpul în ore și minute. Implementează:
Nu uita să gestionezi depășirea minutelor (60 minute = 1 oră)
Implementează o clasă pentru distanțe în metri și centimetri cu:
Creează o clasă Set pentru mulțimi de întregi cu:
Folosește std::set sau std::unordered_set intern pentru stocare
Implementează numere întregi mari (depășesc long long) cu:
Creează clase pentru arbori de expresii matematice cu:
Folosește pattern-ul Visitor sau polimorfism pentru diferite tipuri de noduri
Implementează o clasă template Graph cu:
obj = obj poate cauza probleme// ❌ GREȘIT - operator << ca funcție membră class Wrong { ostream& operator<<(ostream& os) { // Aceasta ar necesita apel: obj << cout; (invers!) return os; } }; // ✅ CORECT - operator << ca friend class Correct { friend ostream& operator<<(ostream& os, const Correct& obj) { // Permite apel normal: cout << obj; return os; } };
După ce ai înțeles supraîncărcarea operatorilor, explorează: