Ключевое слово friend позволяет для класса определить дружественные функции, классы и методы.
Кстати, нет разницы какой модификатор у friend: public, protected, private работают одинаково.
Дружественные классы
struct String
{
friend struct StringBuffer;
private:
char * data_;
size_t len_;
};
struct StringBuffer
{
char * get_data_(String const & s)
{
return s.data_; // имеет доступ к private полям String
}
};
Дружественные функции
Дружественные функции можно определять прямо внутри класса (потому что они станут inline).
struct String
{
friend std::ostream & operator<<(std::ostream & os, String const & s)
{
os << s.data_;
}
private:
char * data_;
size_t len_;
};
Дружественные методы
Есть некоторые сложности:
struct String; // необходимо объявить для StringBuffer::print_String
struct StringBuffer
{
// можно только объявить, поскольку String еще не определён, мы не знаем какие у него есть поля
void print_String(String const & s);
};
struct String
{
friend void StringBuffer::print_String(String const & s);
private:
char * data_;
size_t len_;
};
void StringBuffer::print_String(String const & s) { std::cout << s.data_; }
Стоит понимать, что отношение наследования не задаёт отношение дружбы, это разные вещи: отношение дружбы сильнее чем отношение наследования (друг может использовать private поля, а наследник только protected).
Однако с friend тоже есть проблемы из-за которых его использование не желательно:
- Дружба не симметрична (из “А - друг В” не следует что “В - друг А”)
- Дружба не транзитивна (из “A - друг В, а В - друг С” не следует что “A - друг С”)
- friend довольно таки сильно связывает классы между собой, потом такой код сложно модифицировать
- friend нарушает инкапсуляцию!