В чем разница между использованием ключевого слова friend и использованием функции-члена для изменения закрытых переменных внутри класса?

0

Как задается вопрос...

В чем разница между:

class MyClass
{
public:
    MyClass(){
        m_a = 0;
    }
private:
    int m_a;
    friend void set_a(MyClass &a);
};


void set_a(MyClass &a)
{
    std::cout << a.m_a << std::endl;
    a.m_a = 500;
    std::cout << a.m_a << std::endl;
}



int main(void) {
    MyClass my_class_instance;
    set_a(my_class_instance);

    system("pause");
}

а также:

class MyClass
{
public:
    MyClass(){
        m_a = 0;
    }
    void set_a(){
        std::cout << this->m_a << std::endl;
        this->m_a = 500;
        std::cout << this->m_a << std::endl;
    }
private:
    int m_a;
};



int main(void) {
    MyClass my_class_instance;
    my_class_instance.set_a();

    system("pause");
}

Является ли это просто предпочтительной структурой функции или существуют реальные измеримые различия? Из того, что я могу сказать, обе функции достигают одинаковых результатов во всех обстоятельствах, за исключением случаев, когда у вас было несколько перегрузок для первого примера, которые занимали разные типы объектов.

Теги:

3 ответа

3
Лучший ответ

Поскольку C++ часто задает вопрос: используйте члена, когда можете, и друга, когда вам нужно.

Есть ситуации, когда делать friend свободный функция является предпочтительным, большинство ситуаций, связанных с тем, что первый параметр функции члена всегда этого класса (его скрытый *this параметр).

Одним из примеров является перегрузка арифметических операторов:
Предположим, вы пишете complex класс, который представляет собой комплексные числа. Используя член operator+() вы можете написать выражения типа complex + float, но не float + complex. Но вы можете сделать это со свободной формой operator+:

class complex
{
    ...
    friend complex operator+( float f , complex c );
};
2

Весь этот вопрос сводится к "Почему я должен использовать друзей в C++?". Ответ заключается в том, что при правильном использовании друзья усиливают инкапсуляцию. Это часто задаваемые вопросы:

Неужели друзья нарушают инкапсуляцию?

Конечно, ваш пример слишком короткий и слишком абстрактный. Некоторые лучшие, реальные примеры жизни, которые я мог придумать из головы, связаны с итераторами. У вас может быть много объектов-итераторов, относящихся только к одному объекту-контейнеру, и вы можете захотеть, чтобы итератор мог получить доступ к частным переменным-членам контейнера. В то же время вы не хотите, чтобы контейнер отображал эти переменные для остального мира.

Такой дизайн может быть отлично реализован с помощью функции friend.

0

Многие люди защищают то, что делают методы доступа, вы можете на более позднем этапе развития помешать неправильному доступу к переменным-членам (или даже полностью изменить переменные-члены), не нарушая ваших (правильных) клиентов.

Один классический случай

class ComplexNumber {
  double real, imaginary;
public:
  double re() { return re; }
  double setRe(double v) { return re = v; }
// and so on ...
};

в один прекрасный день вы обнаружите, при каком-то обслуживании, что вам нужны полярные координаты для этого числа, поэтому вы добавляете методы

  double rho() { /* calculate rho */ }
  double theta() { /* calculate theta */ }
  double setRho(double v) { /* calculate real, imaginary, based on the new rho */ }

и так далее.

Позже вы обнаружите, что пользователи класса используют гораздо более полярные, чем декартовы координаты для комплексных чисел, и что конверсии являются узким местом проблемы с производительностью, поэтому вы бросаете real и imaginary и сохраняете rho и theta и меняете методы getter и setter для нового - более эффективное - хранилище для rho, theta, re, im и т.д. Все клиенты вашего класса будут перекомпилировать без проблем, потому что вы изменили свою реализацию, но сохранили свои интерфейсы стабильными.

  • 2
    Извините, я не вижу, как это связано. Можете ли вы уточнить? Из того, что я вижу, вы можете использовать для этого как функции-члены, так и функции-друзья.
  • 0
    Я не понимаю, что вы пытаетесь продемонстрировать, но то, что я вижу, это плохой шаблон get / set, который разрушает точку инкапсуляции.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню