你会在哪里使用一个朋友函数与静态成员函数?

当我们想要访问该类的私有成员时,我们将非成员函数作为一个类的朋友。 这使它具有与静态成员函数相同的访问权限。 两种select都会给你一个与该类的任何实例无关的函数。

我们何时必须使用朋友function? 我们何时必须使用静态函数? 如果两者都是解决问题的可行select,我们如何衡量其适宜性? 是否有一个应该是默认优先?

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

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

  1. 获得内部的类
  2. 是在class级的范围内
  3. 必须在实例上调用

friend只得到1。

static函数得到1和2。

这个问题似乎解决了程序员需要引入一个对类的任何实例都不起作用的函数(因此可能selectstatic成员函数)的情况。 因此,我将限制这个答案在下面的devise情况下,在静态函数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 (我稍后会回头f()语义 ,那么这个有限的场景有一个简单的答案: static函数是可取的 。 我看到两个原因。


通用algorithm:

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

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

如果我们有两个或两个以上的类在它们的接口上有一个static函数f() ,这将允许我们编写一个单独的函数,在任何这样的类上调用f()

如果我们使f()成为一个自由的非成员函数,那么就没有办法编写一个等价的generics函数。 虽然我们可以将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()的类定义之前引入一个f()的声明:如果不这样做会导致编译器抱怨f()必须在命名空间N声明,才能合法使用名称N::f

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

  • A定义之前A名字空间N内的声明;
  • A的定义里的friend声明;
  • 名字空间Nf()的定义

为什么在Nf()的声明和定义不能连接(一般)是因为f()应该访问A的内部,因此,当定义了f()时,必须看到A的定义。 然而,正如前面所说的, f()N内部的声明必须在A内相应的friend声明之前被看到。 这有效地迫使我们拆分f()的声明和定义。


语义考虑:

虽然上述两点是普遍有效的,但是为什么人们可能更倾向于把f()当作static不是使其成为Afriend ,反之亦然,这是由话语的宇宙所推动的。

为了澄清,重要的是强调一个类的成员函数,无论是static还是非static ,在逻辑上都是该类的一部分 。 它有助于其定义,从而提供了一个概念性的描述。

另一方面,一个friend函数尽pipe被授予访问类的内部成员的权限,但它仍然是一个逻辑上在类的定义之外的algorithm。

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

因此,在特定的应用领域中,devise者在决定是将前者作为friend还是后者的成员时,可能要考虑function和类的语义 (这不仅适用于staticfunction,而且也可能涉及其他语言约束的非static函数)。

函数在逻辑上是否有助于表征一个类和/或它的行为,还是它是一个外部algorithm? 如果不知道具体的应用领域,就不能回答这个问题。


味道:

我相信任何其他的争论都是纯粹从品味问题出发的:自由的friendstatic成员的方法,事实上,允许清楚地说明一个类的界面是一个点(类的定义),所以在devise上它们是等价的(当然,以上面的观察为模)。

剩下的不同是风格:在声明一个函数的时候,我们是要编写static关键字还是friend关键字,以及在定义类时是否要编写A:: class范围限定符,而不是N:: namespace范围限定符。 因此,我不会就此进一步发表评论。

不同之处在于明确expression了阶级与function之间关系的意图。

当你想有意表明两个无关的类或者一个类和一个函数之间的强耦合和特殊关系时,你使用了friend

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

朋友function(和class级)可以访问class级的私人和受保护的成员。 使用朋友function或class级的情况很less。 一般避免它们。

静态函数只能访问静态数据(即类范围的数据)。 他们可能被调用而不创build你的类的一个实例。 静态函数对于希望所有类的实例具有相同行为的情况非常有用。 你可以使用它们:

  • 作为callback函数
  • 操纵类范围的成员
  • 检索你不想在头文件中枚举的常量数据
  • 当你想要一个类的每个实例都是相同的函数时,使用静态函数。 这样的函数不能访问“this”指针,因此不能访问任何非静态字段。 当你想要一个可以使用的函数时,经常使用它们,而不需要实例化这个类。

    朋友function是不在课堂上的function,你想让他们访问你的class级的私人成员。

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

    该标准要求运算符=()[]和 – >必须是成员和类特定的
    操作符new,new [],delete和delete []必须是静态成员。 如果情况
    出现在我们不需要类的对象调用函数的地方,然后make
    函数静态。 对于所有其他function:
    如果一个函数需要运算符=()[]和 – >作为streamI / O,
    或者如果它最左边的参数需要types转换,或者如果它可以使用类的公共接口来实现的话,使它成为非成员(如果前两种情况需要的话,可以是朋友)
    如果它需要虚拟performance,
    添加一个虚拟成员函数来提供虚拟行为
    并在此方面执行
    其他
    使其成为会员。

    • select朋友而不是静态成员的一个原因是当函数需要用汇编(或其他语言)编写时。

      例如,我们总是可以在我们的.cpp文件中声明一个extern“C”的朋友函数

       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

    • 另一种情况是当你需要重载操作符时。 重载操作符只能通过朋友或非静态成员来完成。 如果运算符的第一个参数不是同一个类的实例,那么非静态成员也不起作用; 朋友将是唯一的select:

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

    如果函数不需要读取或修改类的特定实例的状态(意思是不需要修改内存中的对象),或者如果需要使用函数指针一个类的成员函数。 在第二个实例中,如果需要修改驻留对象的状态,则需要将其传入并使用本地副本。 首先,在执行某个任务的逻辑不依赖于某个对象的状态的情况下,可能会发生这样的情况,但是逻辑分组和封装将使其成为特定类的成员。

    当您创build的代码不是您的类的成员,并且不应该是您的类的成员,而是具有规避私有/受保护的封装机制的合法目的时,您可以使用朋友函数或类。 其中一个目的可能是你有两个类需要一些共同的数据,但是对这个逻辑进行两次编码将是不好的。 真的,我只使用了这个function,可能是我编码的类中的1%。 这是很less需要的。

    静态函数只能访问一个类的成员。 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 << aa << bb; } 

    f()可以访问A和B类的数据。

    静态函数是一个无法访问的函数。

    朋友函数是一个可以访问类的私有成员的函数。

    静态function可以用许多不同的方式使用。

    比如说简单的工厂function:

      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); .... 

    朋友是完全不同的故事,他们的使用正如刚才所描述的那样

    好友function可以访问其他类的私有和受保护的成员。 意味着他们可以用来访问私有或公共的所有数据。 所以朋友函数被用来访问那些静态方法不能的数据。

    这些方法是静态的,被称为很多次,在每个对象内部声明一个不同的位置,因为它们成本太高(就内存而言)。 这可以通过例子来明确:让类的名字是事实,它的数据成员是n(它表示的因子是关注的整数),那么在这种情况下声明find_factorial()为静态将是明智的决定!

    它们被用作callback函数来操作类作用域的成员来检索你不想在你的头文件中枚举的常量数据

    现在我们明确了以下问题。

    当使用朋友function? 当使用静态函数时?

    现在,如果两者都是解决问题的可行select,我们可以根据可访问性(私人数据的可访问性)和内存效率来调整它们的适用性。 默认情况下,没有人可以select,因为当我们需要更好的内存pipe理的时候,有很多情况,有时我们会关心数据的范围。

    例如:当我们不得不在每个小实例之后调用create()方法时,foo :: create()将优于create_foo(),而我们对数据范围(私有数据)不感兴趣

    如果我们有兴趣获得多个类的私人信息,那么create_foo()将优于foo :: create()。

    我希望这会帮助你!

    一个朋友函数不能被inheritance,而一个静态函数可以。 所以当一个目标可以通过静态函数和朋友函数来实现的时候,想想是否要inheritance它。

    这是我认为是这样的:

    朋友function – 当您需要访问不同的class级成员时,但class级不相关。 静态function – 当你不需要访问“this”指针。 但是,我有一种感觉,还有更多…

    1. 静态数据成员总是共享内存。
    2. 只有静态函数可以使用静态数据成员。
    3. 静态成员函数可以用类名来调用。
    4. 当我们在类中创build静态成员或成员函数的对象时,它们必须在类的外部定义。 它会自动初始化该值。
    5. 它始终使用关键字静态。
    6. 静态成员可以共享所有对象。
    7. 数据成员和成员函数的types和范围在类之外。
    8. 一个静态成员variables必须在类之外定义。