C++
Ключевое слово Friend
Поиск…
Вступление
Хорошо продуманные классы инкапсулируют их функциональность, скрывая их реализацию, обеспечивая при этом чистый документированный интерфейс. Это позволяет перепроектировать или изменить, если интерфейс не изменился.
В более сложном сценарии может потребоваться несколько классов, которые полагаются на детали реализации друг друга. Классы и функции друзей позволяют этим одноранговым узлам получить доступ к деталям друг друга без ущерба для инкапсуляции и скрытия информации документированного интерфейса.
Функция друга
Класс или структура могут объявлять любую функцию, которая является ее другом. Если функция является другом класса, она может получить доступ ко всем ее защищенным и закрытым членам:
// Forward declaration of functions.
void friend_function();
void non_friend_function();
class PrivateHolder {
public:
PrivateHolder(int val) : private_value(val) {}
private:
int private_value;
// Declare one of the function as a friend.
friend void friend_function();
};
void non_friend_function() {
PrivateHolder ph(10);
// Compilation error: private_value is private.
std::cout << ph.private_value << std::endl;
}
void friend_function() {
// OK: friends may access private values.
PrivateHolder ph(10);
std::cout << ph.private_value << std::endl;
}
Модификаторы доступа не изменяют семантику друзей. Публичные, защищенные и частные объявления друга эквивалентны.
Объявления друзей не наследуются. Например, если мы подклассифицируем PrivateHolder
:
class PrivateHolderDerived : public PrivateHolder {
public:
PrivateHolderDerived(int val) : PrivateHolder(val) {}
private:
int derived_private_value = 0;
};
и попытайтесь получить доступ к его членам, мы получим следующее:
void friend_function() {
PrivateHolderDerived pd(20);
// OK.
std::cout << pd.private_value << std::endl;
// Compilation error: derived_private_value is private.
std::cout << pd.derived_private_value << std::endl;
}
Обратите внимание, что PrivateHolderDerived
члена PrivateHolderDerived
не может получить доступ к PrivateHolder::private_value
, в то время как функция друга может это сделать.
Метод Friend
Методы могут объявляться как друзья, так и функции:
class Accesser {
public:
void private_accesser();
};
class PrivateHolder {
public:
PrivateHolder(int val) : private_value(val) {}
friend void Accesser::private_accesser();
private:
int private_value;
};
void Accesser::private_accesser() {
PrivateHolder ph(10);
// OK: this method is declares as friend.
std::cout << ph.private_value << std::endl;
}
Класс друга
Целый класс может быть объявлен другом. Объявление класса друга означает, что любой член друга может получить доступ к закрытым и защищенным членам объявляющего класса:
class Accesser {
public:
void private_accesser1();
void private_accesser2();
};
class PrivateHolder {
public:
PrivateHolder(int val) : private_value(val) {}
friend class Accesser;
private:
int private_value;
};
void Accesser::private_accesser1() {
PrivateHolder ph(10);
// OK.
std::cout << ph.private_value << std::endl;
}
void Accesser::private_accesser2() {
PrivateHolder ph(10);
// OK.
std::cout << ph.private_value + 1 << std::endl;
}
Объявление класса друга не является рефлексивным. Если классы нуждаются в частном доступе в обоих направлениях, им нужны объявления друзей.
class Accesser {
public:
void private_accesser1();
void private_accesser2();
private:
int private_value = 0;
};
class PrivateHolder {
public:
PrivateHolder(int val) : private_value(val) {}
// Accesser is a friend of PrivateHolder
friend class Accesser;
void reverse_accesse() {
// but PrivateHolder cannot access Accesser's members.
Accesser a;
std::cout << a.private_value;
}
private:
int private_value;
};