【问题标题】:Why is it possible to place friend function definitions inside of a class definition?为什么可以将友元函数定义放在类定义中?
【发布时间】:2013-07-04 22:54:09
【问题描述】:

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

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;


};

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

【问题讨论】:

  • 如果您询问friend 函数的定义,那么您应该更改问题的标题以反映这一点。
  • 那我应该写什么呢?
  • 您可以这样做,因为标准明确允许这样做。至于标准允许它的原因——我的猜测是它被认为足够方便,值得。
  • 你可以问“为什么可以在类定义中放置友元函数定义?”

标签: c++ operator-overloading friend


【解决方案1】:

因为操作员需要知道所使用的表达式右侧的详细信息,如果它必须访问位于该侧的类型的私有数据,则它必须是具有该类的friend

如果您尝试将 intperson 进行比较,就像在您的示例中一样,选择有两个:

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

【讨论】:

  • 如果只有朋友声明在类定义中,而朋友函数定义保留在它之外,这仍然是可能的。
  • 谢谢,但我不知道为什么我们使用朋友,我问为什么我们可以在我们的类定义中移动朋友方法的定义。不违反什么吗?如果不是,为什么我们首先要有朋友,我们可以简单地将重载定义为成员函数(我知道成员函数的局限性)但我是说知道这一点,为什么编译器不抱怨我没有定义类定义之外的友元函数,因为它不需要在其中(因为它具有类参数)
  • @Hossein 显然它没有“违反任何事情”。否则你甚至不能问这个问题,因为它不会编译。
  • 违反我的意思是任何东西,例如良好的做法或类似的东西
【解决方案2】:

难道不应该在类之外显式定义友元函数吗?

Friend 函数可以在类声明中定义(给定一个函数体)。这些函数是内联函数,就像成员内联函数一样,它们的行为就像是在看到所有类成员之后但在类范围关闭之前(类声明的结尾)立即定义的。在类声明中定义的友元函数在封闭类的范围内。 quote

是只适用于

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

不过,还有一些常用的运算符通常被声明为友元函数,它们是 operator&lt;&lt;operator&gt;&gt;

【讨论】:

  • 非常感谢 :) 我并不是想将我所有需要的函数声明为朋友,我很想知道为什么朋友函数(仅用于操作重载而没有别的)可以在类中定义以及为什么允许它
  • @Hosse 在我给你的报价中并没有说“为什么允许这样做”,而是解释了为什么“不允许这样做”。
  • 我知道,但是“朋友函数可以在类声明中定义”部分说我们被要求这样做,我知道朋友函数不是所有事情的答案,因为你也明确提到过,当我这么说的时候,我指的是引用的部分,它有点清除了我的部分问题
  • 它们在“文件范围内”?不应该将它们插入到周围的范围中,无论是文件、命名空间、函数还是类?
【解决方案3】:

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

如果您尝试编写符合标准的代码,这可能会很重要。例如,要实现 C++ 规范标准中的 RandomNumberEngine 命名要求,必须提供operator&lt;&lt;。这必须是 friend 才能将 std::ostream&amp; 对象作为其第一个参数(否则它看起来像一个普通的单参数成员函数运算符重载)。通常friend 声明会放在类定义中,而函数定义会放在单独的.cpp 源文件中。但是如果你想要一个header-only实现,就必须在类中定义,避免多次定义错误。

【讨论】:

  • “在类中定义友元函数是唯一可行的方法” 也可以使用inline 函数。
  • @HolyBlackCat 是真的,在一定程度上。但是,我对此并不满意,因为具有非静态链接的内联不需要在翻译单元中相同,这只是自找麻烦。对于 operator,保持定义与其类尽可能密切相关是有意义的。
【解决方案4】:

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'
}

【讨论】:

    【解决方案5】:

    正如杰克所说,在需要访问私人数据的地方需要朋友功能。还有另一个目的。这与继承的类型有关。只有派生类及其朋友可以将指向私有基类的指针转换为派生类型。所以你有时可能想让一些函数成为派生类的朋友,以允许在函数体内这样做。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-07
      • 2011-05-16
      • 2018-04-18
      • 1970-01-01
      • 1970-01-01
      • 2012-01-13
      • 2016-11-14
      • 1970-01-01
      相关资源
      最近更新 更多