朋友和内联方法,有啥意义?

Posted

技术标签:

【中文标题】朋友和内联方法,有啥意义?【英文标题】:friend AND inline method, what's the point ?朋友和内联方法,有什么意义? 【发布时间】:2010-09-27 17:25:44 【问题描述】:

我在标题中看到我没有给自己写以下内容:

class MonitorObjectString: public MonitorObject 
   // some other declarations
   friend inline bool operator==(MonitorObjectString& lhs, MonitorObjectString& rhs)  return(lhs.fVal==rhs.fVal); 

我不明白为什么这个方法被声明为朋友。我认为如果函数在另一个地方定义并且需要访问类的内部成员,这将是有意义的,但这里不是这种情况,因为它是内联的,甚至不需要访问成员。

你怎么看? “朋友”没用吗?

【问题讨论】:

【参考方案1】:

从语法上讲...

friend 关键字仍然需要告诉编译器这个函数不是一个类的成员,编辑:而是一个可以看到私有成员的非成员函数类。


但是,这可以像这样更干净地实现:

/* friend */ inline bool operator ==(const MonitorObjectString& rhs) const
 return fVal == rhs.fVal; 

(当然,我假设fVal 是一个合适的类型,可以在不影响其常量性的情况下进行比较。)

【讨论】:

这里不需要朋友? 结果类似;但一般来说,非修改运算符应该更喜欢非成员,因为如果运算符是成员函数,编译器无法将左侧类型转换为类类型;而它可以使用非成员函数。 您的“更干净”的解决方案被许多人认为是糟糕的设计。如果我创建一个可以隐式转换为“MonitorObjectString”的“ShmooptyString”,则代码 MOS==SS 将编译,但 SS==MOS 不会。问题是参数允许隐式转换,但您不能隐式转换“this” 我忘记了一些事情:当被比较的对象没有值语义时,重载运算符 == 没有意义。继承没有意义。 Eduardo,如何“更干净”?它具有相同的私有成员访问权限并已成为成员。所以它有更强的耦合,这更糟。朋友的定义会更干净恕我直言。【参考方案2】:

它们不是相互排斥的。 “朋友”表示非成员函数可以访问类的私有成员。 “内联”意味着没有函数调用调用,函数的主体在每个调用点都被复制(在汇编中)。

【讨论】:

好吧,我明白了。但是既然这个方法是类的成员,为什么要声明它为朋友呢? 如果是朋友,则不是班级成员。 @Barth - Eduardo 是正确的。如果你把朋友放在函数前面,它就是独立的!不是方法。这就是为什么它需要两个!参数,没有隐含的“this”。 关键字'inline'并不意味着它实际上是内联代码。这是对编译器的“提示”。只有在有意义的情况下,编译器才会内联。 但无论如何,“内联”具有效果。这不是没用的。它将为 ODR 提供一些特别有用的前提。无论如何,这里的内联 是多余的,因为在友元定义中定义的函数是隐式内联的【参考方案3】:
friend inline bool operator==(MonitorObjectString& lhs, MonitorObjectString& rhs)  
    return(lhs.fVal==rhs.fVal); 

有时称为friend definition,因为它是一个定义函数的友元声明。它将函数定义为围绕它出现的类的命名空间的非成员函数。实际上,内联是多余的:如果它是友元定义,则隐式声明内联。它的一些优点和缺点:

它使操作符对正常查找不可见。您可以调用它的唯一方法是使用参数相关查找。这将使命名空间没有大量正常可见的运算符声明。请注意,这还将禁用使用对 MonitorObjectString 的隐式转换来调用它的能力(因为如果在查找要调用的候选对象时两种参数类型不匹配,则依赖于参数的查找将找不到该函数)。 名称的查找始于友元定义所在的类的范围。这意味着不需要写出长类型名称或其他名称。只需像在类的普通成员函数中那样引用它们即可。 作为朋友,该函数看到MonitorObjectString的内部。但这既不好也不坏。这取决于实际情况。例如,如果有函数 getFVal() 让函数成为朋友是毫无意义的。也可以使用getFVal

我曾经喜欢这种朋友定义风格的运算符,因为它们可以直接访问类成员,并且出现在类定义中——所以我可以“一目了然”。然而,最近我得出的结论是,这并不总是一个好主意。如果您可以(并且应该)纯粹使用类的公共成员函数来实现该运算符,则应该将其设为非友元(和非成员)运算符,定义在该类的同一命名空间中。它确保如果您更改某些实现 - 但保持类的接口相同 - 运算符仍然可以工作,并且您的级联更改更少,因为您知道它无法访问实现细节。

然而,我更喜欢这种风格而不是编写成员运算符,因为命名空间范围内的运算符函数具有与参数对称的附加特性:它们不会将左侧特殊对待,因为两者边只是普通参数,而不是绑定到*this 的对象参数。如果左侧或右侧属于您的类的类型,则可以隐式转换另一侧 - 无论它是左侧还是右侧。对于同样在没有友元定义语法的情况下定义的函数(传统上,在命名空间范围内),您将具有选择性地包含使这些运算符可用或不可用的标头的功能。

【讨论】:

"如果是友元定义,则隐式声明为内联。"这是不正确的。就像成员函数一样,如果在类主体之外定义非成员友元函数,则不会隐式声明为内联。 @JMC 我指的是朋友的定义。不是没有friend关键字的定义(即在类之外)。 在这种情况下,您当然是正确的。我理解术语“朋友定义”包括碰巧是朋友的函数的任何定义。尽管如此,它听起来好像它的朋友是决定因素,我宁愿说:它被隐式声明为内联,因为朋友定义只能出现在类体内,这使得它们在任何情况下都是内联的。 @JMC 为什么没有同样的问题? “朋友定义只能出现在类体内”在您的解释下是错误的。至于歧义,我认为我最初的句子“......被称为朋友定义。它......”已经足够清楚了。但似乎“朋友定义”不是官方术语(我是通过将“朋友声明”与“A声明也是定义如果......”规则相结合来构建它),所以我会补充说明。 @JMC 我同意如果函数不是内联的那将是非常愚蠢的。但是对于作为定义的友元函数声明,实际上有一个明确的规则,即它们是隐式内联的。所以我感觉更好地反映这种明确的依赖关系,而不是依赖“类内”的东西。

以上是关于朋友和内联方法,有啥意义?的主要内容,如果未能解决你的问题,请参考以下文章

“受保护的朋友”和“私人保护”有啥区别?

无法与 typedef 成为朋友:有啥特别的原因吗?

在类声明中定义朋友功能

❤️女朋友面试被问内联函数❤️一文讲的明明白白!

bypass waf入门之sql注入 [ 内联注释篇 ]

SVN权限设置两种方法有啥不同?