Где бы вы использовали функцию friend против статической функции-члена?

Мы делаем функцию non-member другом класса, когда хотим, чтобы он получал доступ к этим частным членам класса. Это дает ему те же права доступа, что и статическая функция-член. Обе альтернативы предоставят вам функцию, которая не связана с каким-либо экземпляром этого класса.

Когда мы должны использовать функцию друга? Когда мы должны использовать статическую функцию? Если обе являются жизнеспособными вариантами решения проблемы, как мы оцениваем их пригодность? Есть ли тот, который должен быть предпочтительным по умолчанию?

Например, при реализации factory, который создает экземпляры класса foo, который имеет только частный конструктор, должна ли эта функция factory быть статическим членом foo (вы бы назвали foo::create()) или если это будет функция друга (вы бы назвали create_foo())?

Ответ 1

Раздел 11.5 "Язык программирования С++" Бьярна Страуступа утверждает, что обычные функции-члены получают 3 вещи:

  • доступ к внутренним компонентам класса
  • входят в объем класса
  • необходимо вызвать в экземпляре

friend получить только 1.

static функции получают 1 и 2.

Ответ 2

Вопрос, похоже, касается ситуации, когда программисту необходимо ввести функцию, которая выполняет не работу с любым экземпляром класса (следовательно, возможность выбора функции-члена static). Поэтому я ограничу этот ответ следующей дизайнерской ситуацией, где выбор между статической функцией f() и свободной функцией друга f():

struct A
{
    static void f();     // Better this...
private:
    friend void f();  // ...or this?
    static int x;
};

int A::x = 0;

void A::f() // Defines static function
{
    cout << x;
}

void f() // Defines friend free function
{
    cout << A::x;
}

int main()
{
    A::f(); // Invokes static function
    f();    // Invokes friend free function
}

Не зная ничего заранее о семантике f() и A (я вернусь к этому позже), этот ограниченный сценарий имеет простой ответ: предпочтительна функция static. Я вижу две причины для этого.


ГЕНЕРИЧЕСКИЕ АЛГОРИТМЫ:

Основная причина заключается в том, что можно написать такой шаблон, как следующий:

template<typename T> void g() { T::f(); }

Если у нас было два или более классов, которые имеют функцию static f() на своем интерфейсе, это позволило бы нам написать одну единственную функцию, которая в общем случае вызывает f() в любом таком классе.

Невозможно написать эквивалентную общую функцию, если мы сделаем f() свободную, не-членную функцию. Хотя верно, что мы могли бы помещать f() в пространство имен, так что синтаксис N::f() мог бы использоваться для имитации синтаксиса A::f(), было бы невозможно написать функцию шаблона, такую ​​как g<>() выше, потому что имена пространства имен не являются допустимыми аргументами шаблона.

ОТРАЖЕННЫЕ ЗАЯВЛЕНИЯ:

Вторая причина заключается в том, что если бы мы поставили свободную функцию f() в пространстве имен, нам не было бы позволено встроить ее определение непосредственно в определение класса без введения какого-либо другого объявления для f():

struct A
{
    static void f() { cout << x; } // OK
private:
    friend void N::f() { cout << x; } // ERROR 
    static int x;
};

Чтобы исправить это, мы должны были бы выполнить определение класса A следующим объявлением:

namespace N
{
    void f(); // Declaration of f() inside namespace N
}

struct A
{
    ...
private:
    friend void N::f() { cout << x; } // OK
    ...
};

Это, однако, побеждает наше намерение объявить и определить f() только в одном месте.

Кроме того, если мы хотим объявить и определить f() отдельно, сохраняя f() в пространстве имен, нам все равно придется вводить декларацию для f() до определения класса для A: не удается сделать это заставит компилятор жаловаться на то, что f() должен был быть объявлен в пространстве имен N до того, как имя N::f может быть использовано на законных основаниях.

Таким образом, мы теперь будем иметь f(), упомянутый в трех отдельных местах, а не только два (объявление и определение):

  • Объявление внутри пространства имен N до A definition;
  • Объявление friend внутри определения A;
  • Определение f() внутри пространства имен N.

Причина, по которой объявление и определение f() внутри N не может быть объединено (в общем), состоит в том, что f() должен иметь доступ к внутренним элементам A, и поэтому определение A должно быть когда f() определено. Тем не менее, как было сказано ранее, декларация f() внутри N должна быть видна до того, как будет сделано соответствующее объявление friend внутри A. Это фактически заставляет нас расколоть декларацию и определение f().


СЕМАНТИЧЕСКИЕ СООБРАЖЕНИЯ:

В то время как вышеприведенные две точки универсальны, есть причины, по которым можно было бы предпочитать f() как static сделать его friend of A или наоборот, которые управляются вселенной дискурса.

Чтобы прояснить, важно подчеркнуть тот факт, что функция-член класса, будь то static или не static, логически является частью этого класса. Он вносит свой вклад в его определение и, таким образом, дает концептуальную характеристику.

С другой стороны, функция friend, несмотря на то, что ей предоставляется доступ к внутренним членам класса, которым она является другом, по-прежнему является алгоритмом, который логически является внешним по отношению к определению класса.

Функция может быть friend более чем одного класса, но может быть членом только одного.

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

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


ВКУС:

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

Остальные различия стилистичны: хотим ли мы писать ключевое слово static или ключевое слово friend при объявлении функции и хотим ли мы писать квалификатор класса A:: класса при определении класса, а не N:: спецификатор области пространства имен. Таким образом, я больше не буду комментировать это.

Ответ 3

Разница ясно выражает намерение взаимосвязи между классом и функцией.

Вы используете friend, когда хотите преднамеренно указать сильную связь и особые отношения между двумя несвязанными классами или между классом и функцией.

Вы используете функцию члена static, если функция логически является частью класса, членом которой является.

Ответ 4

Статические функции используются, когда вы хотите, чтобы функция была одинаковой для каждого экземпляра класса. Такие функции не имеют доступа к указателю "this" и, следовательно, не могут получить доступ к нестационарным полям. Они часто используются, когда вы хотите использовать функцию, которая может быть использована без создания экземпляра класса.

Друг-функции - это функции, которые не входят в класс, и вы хотите предоставить им доступ к закрытым членам вашего класса.

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

Ответ 5

Функции (и классы) друга могут обращаться к частным и защищенным членам вашего класса. Редко бывает хороший пример использования функции или класса друга. Избегайте их вообще.

Статические функции могут обращаться только к статическим данным (то есть к областям данных класса). Они могут быть вызваны без создания экземпляра вашего класса. Статические функции отлично подходят для ситуаций, когда вы хотите, чтобы все экземпляры вашего класса вели себя одинаково. Вы можете использовать их:

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

Ответ 6

Стандарт требует, чтобы оператор =() [] и → должны быть членами, а для класса - операторы new, new [], delete и delete [] должны быть статическими. Если ситуация возникает, когда нам не нужен объект класса для вызова функции, а затем создайте функция статическая. Для всех других функций:
если для функции нужны операторы =() [] и → для потока I/O,
       или если ему нужны преобразования типов в его самом левом аргументе,   или если он может быть реализован с использованием только открытого интерфейса класса,   сделайте его нечленом (и приятелем при необходимости в первых двух случаях)
если он должен вести себя практически,
  добавьте функцию виртуального участника, чтобы обеспечить виртуальное поведение
  и реализовать с точки зрения того, что остальное
  сделайте его членом.

Ответ 7

  • Одна из причин, почему предпочитаете друга по статическому члену, - это когда функция должна быть записана в сборке (или на каком-то другом языке).

    Например, мы всегда можем иметь внешнюю функцию "C" друга, объявленную в нашем .cpp файле

    class Thread;
    extern "C" int ContextSwitch(Thread & a, Thread & b);
    
    class Thread
    {
    public:
        friend int ContextSwitch(Thread & a, Thread & b);
        static int StContextSwitch(Thread & a, Thread & b);
    };
    

    И позже определяется в сборке:

                    .global ContextSwitch
    
    ContextSwitch:  // ...
                    retq
    

    С технической точки зрения, мы могли бы использовать статическую функцию-член для этого, но определение ее в сборке будет непросто из-за изменения имени (http://en.wikipedia.org/wiki/Name_mangling)

  • Другая ситуация - когда вам нужно перегружать операторов. Операторы перегрузки могут выполняться только через друзей или нестатических членов. Если первый аргумент оператора не является экземпляром одного и того же класса, то нестатический член также не будет работать; Друг был бы единственным вариантом:

    class Matrix
    {
        friend Matrix operator * (double scaleFactor, Matrix & m);
        // We can't use static member or non-static member to do this
    };
    

Ответ 8

Вы использовали бы статическую функцию, если функция не нуждается в чтении или изменении состояния конкретного экземпляра класса (что означает, что вам не нужно изменять объект в памяти), или если вам нужно использовать указатель функции на функцию-член класса. В этом втором случае, если вам нужно изменить состояние резидентного объекта, вам нужно передать this in и использовать локальную копию. В первом случае такая ситуация может произойти, когда логика для выполнения определенной задачи не зависит от состояния объекта, но ваша логическая группировка и инкапсуляция должны быть частью определенного класса.

Вы используете функцию или класс друга, когда вы создали код, который не является членом вашего класса и не должен быть членом вашего класса, но имеет законную цель для обхода частных/защищенных механизмов инкапсуляции. Одна из целей этого может заключаться в том, что у вас есть два класса, которые нуждаются в некоторых общих данных, но для кодирования логики дважды было бы плохо. Действительно, я использовал эту функциональность только в 1% классов, которые я когда-либо кодировал. Это редко необходимо.

Ответ 9

Статическая функция может обращаться только к членам одного класса. Функция Friend имеет доступ к нескольким классам, что объясняется следующим кодом:

class B;
class A { int a; friend void f(A &a, B &b); };
class B { int b; friend void f(A &a, B &b); };
void f(A &a, B &b) { std::cout << a.a << b.b; }

f() может обращаться к данным как класса A, так и B.

Ответ 10

Статическая функция - это функция, которая не имеет доступа к this.

Функция-друга - это функция, которая может обращаться к закрытым членам класса.

Ответ 11

Статическая функция может использоваться по-разному.

Например, как простая функция factory:

  class Abstract {
  private:
    // no explicit construction allowed
    Abstract(); 
    ~Abstract();

   public:
     static Abstract* Construct() { return new Abstract; }
     static void Destroy(Abstract* a) { delete a; }
   };

   ...
   A* a_instance = A::Conctruct();
   ...
   A::Destroy(a_instance);

Это очень упрощенный пример, но я надеюсь, что он объяснит, что я имел в виду.

Или как функция потока, работающая с вашим классом:

 class A {

 public:
    static void worker(void* p) {
            A* a = dynamic_cast<A*>(p);
            do something wit a;
    }   
 } 

 A a_instance;
 pthread_start(&thread_id, &A::worker, &a_instance);
 .... 

Друг - совершенно другая история, и их использование в точности соответствует описанию

Ответ 12

Функции Friend могут обращаться к частным и защищенным членам других классов. Значит, они могут использоваться для доступа ко всей погоде данных, которая является конфиденциальной или общедоступной. Поэтому функции друга используются для доступа к тем данным, которые не могут быть использованы статическими методами.

Эти методы сделаны статическими, которые называются так много раз, что объявляют другое местоположение внутри каждого объекта, поскольку они становятся слишком дорогостоящими (с точки зрения памяти). Это можно сделать с помощью примера: Пусть имя класса является фактом, а его членом данных является n (который представляет целое число, факторный фактор которого является проблемой) то в этом случае объявление find_factorial() как статического будет разумным решением!

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

Теперь мы ясно видим следующие вопросы.

Когда используется функция friend? Когда используется статическая функция?

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

Например: foo:: create() будет предпочтительнее, чем create_foo(), когда мы должны вызвать метод create() после каждого небольшого экземпляра времени, и нас не интересует объем данных (частные данные)

И если нам интересно получить личную информацию более чем одного класса, тогда create_foo() будет предпочтительнее, чем foo:: create().

Надеюсь, это поможет вам!

Ответ 13

Функция друга не может быть унаследована, пока статическая функция может быть. Поэтому, когда цель может быть достигнута как с помощью статической функции, так и с функцией друга, подумайте, хотите ли вы наследовать ее или нет.

Ответ 14

Вот что я думаю:

Функция Friend - когда вам нужен доступ к другому члену класса, но классы не связаны. Статическая функция - когда вам не нужен доступ к указателю 'this'. Но, у меня есть чувство, что к этому есть еще больше.

Ответ 15

  • Элементы статических данных всегда используют память.
  • только статическая функция может использовать статические элементы данных.
  • статическая функция-член может быть вызвана с именем класса.
  • Они должны быть определены вне класса, когда мы создаем объект статического члена или функцию-член в классе. Он автоматически инициализирует значение.
  • Он всегда использовал ключевое слово static.
  • Статические члены могут делиться всеми объектами.
  • Тип и объем элементов данных и функция-член находятся вне класса.
  • Статическая переменная-член должна быть определена вне класса.