你会在哪里使用友元函数而不是静态成员函数?

Swa*_*pna 61 c++ friend-function static-functions

当我们希望它访问该类的私有成员时,我们将非成员函数作为类的朋友.这赋予它与静态成员函数相同的访问权限.两种选择都会为您提供一个与该类的任何实例无关的函数.

什么时候必须使用朋友功能?什么时候必须使用静态函数?如果两者都是解决问题的可行方案,那么我们如何权衡它们的适用性呢?是否有一个默认情况下应该首选?

例如,当实现一个工厂创建foo只有私有构造函数的类的实例时,工厂函数应该是foo(你会调用foo::create())的静态成员还是应该是友元函数(你会调用create_foo())?

pm1*_*100 69

Bjarne Stroustrup的第11.5节"C++编程语言"指出普通的成员函数有三件事:

  1. 访问类的内部
  2. 属于班级范围
  3. 必须在实例上调用

friend只得到1.

static 函数得1和2.

  • 什么时候在同一范围内有益还是没有? (6认同)
  • 最好的答案IMO,尽管历史上有不同的用途,但朋友比静态(-member函数)更通用,因为它可以做同样的工作但是它们的命名空间范围更灵活.一个小问题是,不能使用类实例调用友元函数(例如A a; a.static_member();但用法并不常见) (2认同)

And*_*owl 41

这个问题似乎解决了程序员需要引入一个不能在类的任何实例上工作的函数的情况(因此可以选择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
}
Run Code Online (Sandbox Code Playgroud)

如果没有事先对一无所知的语义f()A(我会回来的这个版本),这有限的情况下有一个简单的答案:static功能是优选的.我看到了两个原因.


通用算法:

主要原因是可以编写如下模板:

template<typename T> void g() { T::f(); }
Run Code Online (Sandbox Code Playgroud)

如果我们有两个或多个在其接口上具有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;
};
Run Code Online (Sandbox Code Playgroud)

为了解决上述问题,我们将在class的定义之前A加上以下声明:

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

struct A
{
    ...
private:
    friend void N::f() { cout << x; } // OK
    ...
};
Run Code Online (Sandbox Code Playgroud)

然而,这违背了我们f()在一个地方宣布和定义的意图.

此外,如果我们想f()在保留f()命名空间的同时单独声明和定义,我们仍然必须f()在类定义之前引入一个声明A:如果不这样做会导致编译器抱怨f()必须在内部声明的事实名称N前的命名空间N::f可以合法使用.

因此,我们现在f()提到三个不同的地方,而不仅仅是两个(声明和定义):

  • 在命名空间定义N之前的声明A;
  • friend内部A定义中的声明;
  • f()内部命名空间的定义N.

f()内部的声明和定义N无法连接(通常)的原因f()是应该访问内部的内部A,因此,A定义时必须看到f()定义.然而,正如前文所说,f()的内部声明N必须在相应的前看到friend里面的声明A作出.这有效地迫使我们分裂宣言和定义f().


语义考虑:

虽然上述两点是普遍有效的,有一个原因可能更宣布f()static在使之成为friendA或通过话语的宇宙驱动,反之亦然.

为了澄清,强调的事实是一个类的成员函数,无论是它是重要static或非static,是逻辑的一部分该类.它有助于其定义,从而提供它的概念表征.

在另一方面,一个friend函数,尽管被授予访问类它是朋友的内部成员,仍然是一种算法,在逻辑上是外部的类的定义.

函数可以是friend多个类,但它可以只是一个类的成员.

因此,在特定的应用程序域中,设计者可能希望在决定是否使前者成为后者或后者的成员时考虑到函数和类的语义friend(这不仅适用于static函数,而且适用于非- static其他语言限制可能介入的功能).

该函数在逻辑上是否有助于表征类和/或其行为,还是外部算法?如果不了解特定的应用领域,就无法回答这个问题.


味道:

我认为,除了刚刚给出的任何其他论点完全源于一个品味问题:实际上,自由friendstatic成员方法都允许清楚地说明一个类的接口是什么,在一个单独的位置(类的定义),所以它们在设计上是等价的(当然,以上述观察为模).

剩下的差异是风格的:我们是否要在声明函数时编写static关键字或friend关键字,以及A::在定义类而不是N::命名空间范围限定符时是否要编写类范围限定符.因此,我不会对此作进一步评论.

  • 很好的答案.这是我一直在寻找的东西. (2认同)

Alo*_*ave 11

差异清楚地表达了类和函数之间关系的意图.

friend当你想故意指出两个不相关的类之间或类和函数之间的强耦合和特殊关系时,可以使用.

static当函数在逻辑上是它所属的类的一部分时,可以使用成员函数.


syn*_*pis 5

当您想要一个对于类的每个实例都相同的函数时,可以使用静态函数。此类函数无法访问“this”指针,因此无法访问任何非静态字段。当您想要一个无需实例化类即可使用的函数时,通常会使用它们。

友元函数是类中不存在的函数,您希望它们能够访问类的私有成员。

这(静态与朋友)不是使用一个与另一个的问题,因为它们不是对立的。


the*_*ess 5

朋友函数(和类)可以​​访问您的类的私有成员和受保护成员。使用朋友功能或类的情况很少。总体上避免使用它们。

静态函数只能访问静态数据(即,类作用域的数据)。可以在不创建类实例的情况下调用它们。静态函数非常适合您希望类的所有实例以相同方式运行的情况。您可以使用它们:

  • 作为回调函数
  • 操纵班级成员
  • 检索您不想在头文件中枚举的常量数据
    • 这个答案有点误导。静态函数可以访问类私有数据;但它们必须有某种方式到达实例(即实例被传递给静态函数)。一个常见的例子是工厂方法,它创建一个对象,然后在将其返回给调用者之前对其内部进行修改 (3认同)

    tze*_*nes 1

    静态函数是无法访问this.

    友元函数是可以访问类的私有成员的函数。

    • 静态函数也可以访问类的内部 (10认同)