W poprzedniej lekcji przeciążaliśmy operator+ jako funkcję zaprzyjaźnioną:
#include <iostream>
class Cents
{
private:
int m_cents{};
public:
Cents(int cents)
: m_cents{ cents }
{}
// dodaj centy + centy za pomocą przyjaciela funkcja
friend Cents operator+(const Cents& c1, const Cents& c2);
int getCents() const { return m_cents; }
};
// uwaga: ta funkcja nie jest funkcją składową funkcja!
Cents operator+(const Cents& c1, const Cents& c2)
{
// użyj konstruktora Cents i operatora+(int, int)
// możemy uzyskać bezpośredni dostęp do m_cents, ponieważ jest to funkcja znajomego
return { c1.m_cents + c2.m_cents };
}
int main()
{
Cents cents1{ 6 };
Cents cents2{ 8 };
Cents centsSum{ cents1 + cents2 };
std::cout << "I have " << centsSum.getCents() << " cents.\n";
return 0;
}Korzystanie z funkcji zaprzyjaźnionej do przeciążania operatora jest wygodne, ponieważ zapewnia bezpośredni dostęp do wewnętrznych elementów klas, na których operujesz. W powyższym przykładzie z Centami nasza znajoma wersja operatora+ uzyskała bezpośredni dostęp do zmiennej składowej m_cents.
Jeśli jednak nie potrzebujesz takiego dostępu, możesz zapisać przeciążone operatory jako normalne funkcje. Należy zauważyć, że powyższa klasa Cents zawiera funkcję dostępu (getCents()), która pozwala nam uzyskać m_cents bez konieczności posiadania bezpośredniego dostępu do prywatnych członków. Z tego powodu możemy zapisać naszego przeciążonego operatora+ jako nie-przyjaciela:
#include <iostream>
class Cents
{
private:
int m_cents{};
public:
Cents(int cents)
: m_cents{ cents }
{}
int getCents() const { return m_cents; }
};
// uwaga: ta funkcja nie jest funkcją składową ani funkcją zaprzyjaźnioną!
Cents operator+(const Cents& c1, const Cents& c2)
{
// użyj konstruktora Cents i operatora+(int, int)
// nie potrzebujemy tutaj bezpośredniego dostępu do prywatnych członków
return Cents{ c1.getCents() + c2.getCents() };
}
int main()
{
Cents cents1{ 6 };
Cents cents2{ 8 };
Cents centsSum{ cents1 + cents2 };
std::cout << "I have " << centsSum.getCents() << " cents.\n";
return 0;
}Ponieważ funkcje normalne i zaprzyjaźnione działają prawie identycznie (mają tylko różne poziomy dostępu do elementów prywatnych), generalnie nie będziemy ich rozróżniać. Jedyna różnica polega na tym, że deklaracja funkcji przyjaciela wewnątrz klasy służy również jako prototyp. W przypadku normalnej wersji funkcji będziesz musiał dostarczyć własny prototyp funkcji.
Cents.h:
#ifndef CENTS_H
#define CENTS_H
class Cents
{
private:
int m_cents{};
public:
Cents(int cents)
: m_cents{ cents }
{}
int getCents() const { return m_cents; }
};
// Należy jawnie podać prototyp operatora+, aby użycie operator+ w innych plikach wiedziało, że istnieje przeciążenie
Cents operator+(const Cents& c1, const Cents& c2);
#endifCents.cpp:
#include "Cents.h"
// uwaga: ta funkcja nie jest funkcją składową ani funkcją zaprzyjaźnioną!
Cents operator+(const Cents& c1, const Cents& c2)
{
// użyj konstruktora Cents i operatora+(int, int)
// nie potrzebujemy tutaj bezpośredniego dostępu do prywatnych członków
return { c1.getCents() + c2.getCents() };
}main.cpp:
#include "Cents.h"
#include <iostream>
int main()
{
Cents cents1{ 6 };
Cents cents2{ 8 };
Cents centsSum{ cents1 + cents2 }; // bez prototypu w Cents.h nie udałoby się tego skompilować
std::cout << "I have " << centsSum.getCents() << " cents.\n";
return 0;
}Ogólnie rzecz biorąc, normalna funkcja powinna być preferowana zamiast funkcji zaprzyjaźnionej, jeśli jest to możliwe przy użyciu istniejących funkcji składowych (im mniej funkcji dotyka elementów wewnętrznych klas, tym lepiej). Nie dodawaj jednak dodatkowych funkcji dostępu tylko po to, aby przeciążyć operatora jako normalną funkcję, a nie funkcję zaprzyjaźnioną!
Najlepsza praktyka
Preferuj przeciążanie operatorów jako normalne funkcje zamiast znajomych, jeśli można to zrobić bez dodawania dodatkowych funkcji.

