#ооп #классы #cpp
Какой смысл несут дружественные функции для класса и дружественные классы для класса: class B; // class A definition class A { int n1, n2; friend void print_fields(A a); // практический смысл? friend class B; // практический смысл? public: set_fields(n_1,n_2) { n1 = n_1; n2 = n_2; } }; void print_fields(A a) { cout << a.n1 << " " << a.n2 << endl; } Зачем так делать: friend void print_fields(A a); Если данный метод и так является фактически публичным членом класса( раз он дружественный, то доступен отовчюду ) и, соответственно, тмеет прямой доступ к начинки класса? Это тоже не совсем понятно: friend class B; Ведь класс А можно "запротектить" (protected) и все будет ок. Объясните, пожалуйста.
Ответы
Ответ 1
Иногда действительно возникает необходимость из вне иметь доступ к закрытым полям данных твоего класса. В этом случае без друзей не обойтись. Вот пример - ты написал класс описывающий игрового юнита. Этот класс содержит внутреннее представление m_HP и открытый интерфейс Injure - нанести урон, Heal - лечить и IsAlive - проверка, жив ли юнит. class Unit { public: friend void TestUnit(); Unit(unsigned maxHP) : m_maxHP(maxHP) , m_HP(maxHP) {}; void Injure(unsigned val) { m_HP -= val; if( m_HP < 0 ) { m_HP = 0; } }; void Heal(unsigned val) { m_HP += val; if( m_HP > m_maxHP ) { m_HP = m_maxHP; } } bool IsAlive() const { return m_HP > 0; } private: unsigned m_maxHP; int m_HP; }; Ты решаешь обернуть функционал класса юнит тестами, что бы быть уверенным в том, что функции void Injure(unsigned val) и void Heal(unsigned val) работают корректно: void TestUnit() { Unit unit(100); unit.Injure(10); assert(unit.m_HP == 90); // check private data field unit.Heal(20); assert(unit.m_HP == 100); // check private data field } int main() { TestUnit(); } Как видишь, написание теста было бы невозможно без обращения к внутреннему представлению класса. Именно для этого мы объявили функцию void TestUnit() дружественной к классу Unit Upd: Еще один пример использования дружественных функций при проектировании собственного класса и обеспечения его функциональности. Ниже приведен очень простой пользовательский класс оборачивающий целое число. Для помещения экземпляров данного класса в выходной поток самым естественным образом необходимо определить функцию operator<< не являющуюся экземпляром класса #includeclass WrappedInt { public: explicit WrappedInt(int val) : m_value(val) {} friend std::ostream& operator<<(std::ostream& os, const WrappedInt& val); private: int m_value; }; std::ostream& operator<<(std::ostream& os, const WrappedInt& val) { os << val.m_value; return os; } а для того, что бы эта функция имела доступ к закрытым полям класса, таким как m_value, мы и делаем ее дружественной по отношению к классу WrappedInt.
Комментариев нет:
Отправить комментарий