为什么可以将友元函数定义放置在类定义中

Why is it possible to place friend function definitions inside of a class definition?

本文关键字:定义 函数 友元 为什么      更新时间:2023-10-16

朋友函数不应该在类之外显式定义吗
如果是这样,为什么我可以像任何成员函数一样在类定义中声明友元函数
这是什么
它只适用于某些操作员,如<操作员,还是适用于所有操作员
如果它适用于所有人,那么这样做有什么缺点吗
应该避免吗?如果是,为什么?

class person 
{
public:
    bool operator<(int num)
    {
        return  x < num ? true : false ;
    }
    bool operator<(person& p)
    {
        return  x < p.x ? true : false ;
    }
    friend bool operator<(int num, person &p)
    {
        return  p.x < num ? true : false ;
    }
    void setX(int num)
    {
        x = num;
    }
private:
    int x;

};

更新
我并不是要求选择非成员运算符重载或成员运算符重载
我想知道的是:
为什么允许我们在类定义中移动友元方法的定义
这没有违反任何规定吗?如果不是,我们为什么会有朋友呢
我们可以简单地将重载定义为成员函数(我知道成员函数的局限性),但我想说的是,知道这一点,编译器为什么不抱怨我没有在类定义之外定义友元函数,因为它不需要在类定义之内(因为它有类参数)那么,为什么我们可以在类定义中定义友元函数呢?

朋友函数不应该在类之外显式定义吗

友元函数可以在类声明中定义(给定函数体)。这些函数是内联函数,与成员内联函数一样,它们的行为就像是在看到所有类成员之后但在类作用域关闭之前(类声明的末尾)立即定义的一样。在类声明中定义的友元函数在封闭类的作用域中。报价

是否只适用于某些运算符,如<操作员还是适用于所有操作员

最好尽量避免使用友元函数,因为它们与您使用私有类作用域所做的相反,主要是"隐藏"变量。如果你所有的函数都是友元函数,那么拥有私有变量有什么用?

尽管如此,还是有一些常见的运算符经常被声明为友元函数,它们是operator<<operator>>

Alexandru Barbarosie的答案是正确的。这意味着我们可以在类中声明一个友元函数,它不是成员函数。这可以很好地组织代码。我认为一个例子可以帮助理解它,以防它不清楚。

#include <iostream>
class A {
    public:
        A(int val) : val(val) {}
        // The following isn't a member function, it is a friend 
        // function declared inside the class and it has file scope
        friend void draw (A &a) {
            std::cout << "val: " << a.val << "n";
        }
    private:
        int val;
};
int main() {
    A a(5);
    draw(a); // outputs "val: 5"
    //A::draw(a); // Error: 'draw' is not a member of 'A'
}

如果您要创建一个只包含头的类(这使部署变得更加容易),那么在该类中定义一个友元函数是唯一的方法,因为定义只能出现在一个翻译单元中。包含保护的常规技术不起作用,因为它只处理递归包含之类的事情。

如果您试图编写符合标准的代码,这可能是一件大事。例如,要实现C++规范标准中的RandomNumberEngine命名需求,需要提供operator<<。这必须是一个friend,才能将std::ostream&对象作为其第一个参数(否则它将看起来像一个普通的单参数成员函数运算符重载)。通常,friend声明将放在单独的.cpp源文件中的类定义和函数定义中。但是,如果您想要一个只有头的实现,则必须在类中定义它,以避免出现多个定义错误。

因为运算符需要知道所用表达式右侧的详细信息,所以如果它必须访问位于右侧的类型的私有数据,那么它需要是该类的friend

如果您试图比较intperson,就像您的例子中一样,有两种选择:

  • 您提供了从personint的隐式转换,以便<可以在不访问任何私有字段的情况下使用它
  • 或者将运算符声明为personfriend,以便它可以访问比较右侧的x

正如Jack提到的,在需要访问私有数据的地方需要友元函数。还有另一个目的。这与继承类型有关。只有派生类及其友元才能将指向私有基的指针转换为派生类型。因此,有时您可能希望将某个函数作为派生类的朋友,以允许在函数体内部进行此操作。