假设我有一个具有private data member n
和public get_n()
功能的课程。
例如,当重载输出运算符时,我可以使用get_n()
或将其设为好友并使用n
。
有“最佳”选择吗?如果是这样,为什么?
或者差异是否会被优化掉?
感谢。
答案 0 :(得分:4)
使用get_n,因为这不是proper usage of friend。如果get_n
是一个简单的return n
,编译器很可能会自动inline。
答案 1 :(得分:2)
我将用一个问题回答你的问题:
get_n()
?答案 2 :(得分:1)
你已经得到了许多有些矛盾的答案,所以你无疑需要的是另外一个与几乎所有这些相矛盾的答案。
从效率的角度来看,它不太可能有任何区别。除非您通过关闭所有优化来明确禁止发生这种情况,否则毫无疑问会生成仅返回值的函数。
这只留下了一个从设计角度来看更可取的问题。至少IMO,通常优先于而不是首先有get_n
。删除该设计问题后,您提出的问题就会消失:由于没有get_n
开始,您无法编写其他代码以依赖它。
尽管如此,这仍然会留下一个小问题。这(当然)导致更多问题。特别是n
代表什么样的东西?我意识到你可能给出了一个假设的例子,但是一个好的设计(在这种情况下)取决于对n
是什么以及它是如何使用的更多了解,以及{{1}的类型}是一个成员,以及如何使用 it 。
如果n
是叶子类的成员,您希望不从中派生,那么您应该使用直接写n
的友元函数:
n
简单,直接,有效。
但是,如果class whatever {
int n;
friend std::ostream &operator<<(std::ostream &os, whatever const &w) {
return os << w.n;
}
};
是您希望使用(或被使用)作为基类的成员,那么您通常希望使用“虚拟虚拟”函数:
n
但是,请注意,这假设您对写出整个对象感兴趣,而且至少在当前实现中,这意味着写出class whatever {
int n;
virtual std::ostream &write(std::ostream &os) {
return os << n;
}
friend std::ostream &operator<<(std::ostream &os, whatever const &w) {
return w.write(os);
}
};
的值。
至于为什么你应该这样做,我认为应该遵循一些简单的原则:
n
的私有成员(并且通常也不需要公共get_n
)(例如)但仍然是坏想法,并显示粗略对面向对象或封装的误解,更不用说制作彻头彻尾的丑陋代码了。set_n
经常意味着您最终会得到执行读取/修改/写入循环的客户端代码,该对象充当哑数据容器。通常最好将其转换为客户端代码描述所需结果的单个操作,并且对象本身执行读取/修改/写入以实现该结果。get_n
这样的公共函数本身几乎总是一件好事,不论它是否适合封装。由于其他人对get_n
函数发表了评论,我也会在这个问题上加上我的两分钱。听到评论“应该避免朋友,因为它破坏了封装”这一点是相当频繁的。
我必须强烈反对,并进一步相信任何认为在学习像程序员一样思考方面还有一些工作要做的人。程序员必须考虑抽象,然后在现实世界中尽可能合理地实现这些抽象。
如果一个对象支持输入和/或输出,那么输入和输出是该对象接口的一部分,并且该接口的任何实现该对象的一部分。 仅其他可能性是对象的类型不支持输入和/或输出。
这里的要点非常简单:至少要支持常规约定,C ++插入器和提取器必须编写为自由(非成员)函数。尽管如此,插入和提取与其他任何操作一样,都是类接口的一部分,并且(因此)插入器/提取器与其他任何东西一样,都是类的一部分(作为抽象)。 / p>
我注意到记录,这是为什么我更喜欢在类里面实现所涉及的朋友函数的一部分,正如我在上面所示。从逻辑的角度来看,它们是课堂的一部分,所以让它们看起来像课程的一部分是一件好事。
我将重复最后一次强调:让他们访问类内部不可能破坏封装,因为实际上它们是类的一部分 - 而C ++的奇怪要求是它们被实现为自由函数不要用一个单独的单一的iota来改变这个事实。
答案 3 :(得分:0)
在这种情况下,最佳做法是让类实现toString
方法,输出操作符用于获取字符串表示。由于这是一个成员函数,它可以直接访问所有数据。它还有一个额外的好处,你可以使这个方法vritual
,以便子类可以覆盖它,你只需要一个基类的输出操作符。
答案 4 :(得分:0)
可以在不使用friend
的情况下实施运营商吗?是 - 请勿使用friend
。 No-make friend
。