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 }
{}
// add Cents + Cents using a friend function
friend Cents operator+(const Cents& c1, const Cents& c2);
int getCents() const { return m_cents; }
};
// note: this function is not a member function!
Cents operator+(const Cents& c1, const Cents& c2)
{
// use the Cents constructor and operator+(int, int)
// we can access m_cents directly because this is a friend function
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; }
};
// note: this function is not a member function nor a friend function!
Cents operator+(const Cents& c1, const Cents& c2)
{
// use the Cents constructor and operator+(int, int)
// we don't need direct access to private members here
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; }
};
// Need to explicitly provide prototype for operator+ so uses of operator+ in other files know this overload exists
Cents operator+(const Cents& c1, const Cents& c2);
#endifCents.cpp:
#include "Cents.h"
// note: this function is not a member function nor a friend function!
Cents operator+(const Cents& c1, const Cents& c2)
{
// use the Cents constructor and operator+(int, int)
// we don't need direct access to private members here
return { c1.getCents() + c2.getCents() };
}main.cpp:
#include "Cents.h"
#include <iostream>
int main()
{
Cents cents1{ 6 };
Cents cents2{ 8 };
Cents centsSum{ cents1 + cents2 }; // without the prototype in Cents.h, this would fail to compile
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.

