2011-05-25 86 views
1

说我有一个private data member npublic get_n()函数的类。 例如,当输出操作符超载时,我可以使用get_n()或使它成为朋友并使用n。 有没有'最好'的选择?如果是这样,为什么? 或者差异将会被优化掉吗? 谢谢。简单的设计选择?

+1

为什么您认为get_n()函数与输出本身相比甚至更明显? – 2011-05-25 13:27:54

回答

1

你已经得到了很多有点矛盾的答案,所以你无疑需要的是几乎所有的答案都是矛盾的。

从效率的角度来看,它不可能有任何区别。只有返回值的函数无疑会内联生成,除非您通过关闭全部优化来明确禁止发生该值。

这只留下了一个问题,从设计的角度来看最好。至少国际海事组织,这是通常优于而不是首先有一个get_n。一旦你删除了设计问题,你问的问题就消失了:因为没有get_n开始,你不能写其他代码,以取决于它。

这仍然留下一个小问题,你应该怎么做,但是。这(当然)会导致更多的问题。特别是,n代表什么样的事情?我意识到你可能给出了一个假设的例子,但是一个好的设计(在这种情况下)取决于了解更多关于什么是n以及它是如何使用的,以及其中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的值。

至于为什么要这样做事,有几个简单的原则,我认为应遵循:

  1. 要么使一些真的私人,或者把它公开。 JavaBeans可能需要公开get_n(以及公共set_n)的私有成员(例如),但仍然是一个真正的不好的想法,并且显示了对目标方向的误解或封装,更不用说制造彻头彻尾的丑陋代码。
  2. 告诉别问。公众get_n通常意味着你最终得到的是执行读取/修改/写入循环的客户端代码,该对象充当哑数据容器。通常最好将其转换为客户端代码描述所需结果的单个操作,并且该对象本身执行读取/修改/写入以实现该结果。
  3. 最小化界面。你应该努力使每个对象具有最小的接口,而不会给用户造成过度的痛苦。消除像get_n这样的公共功能本身几乎总是一件好事,独立于封装的好处。

因为其他人评论了关于friend功能,我将添加我的两分钱的价值在这个问题上也是如此。听到意见的时间非常频繁,“应该避免朋友,因为它破坏了封装”。

我必须强烈反对,并进一步相信任何人认为在学习像程序员一样思考方面还有一些工作要做。程序员必须从抽象的角度思考,然后尽可能合理地在现实世界中实现这些抽象。

如果对象支持输入和/或输出,则输入和输出是份该对象的界面的,不管实现该接口是对象的一部分。 只有其他可能性是对象类型不支持输入和/或输出

这里的要点很简单:至少要支持常规约定,C++插入器和提取器必须写成免费(非成员)函数。尽管如此,插入和提取与其他任何操作一样,也是类接口的一部分,因此插入器/提取器与其他任何东西一样(是抽象的)也是类的一部分。

我会注意到这是我为什么喜欢在课堂上实现中的朋友函数的一部分,正如我在上面显示的那样。从逻辑的角度来看,他们是班上的一员,所以让他们看起来像班上的一员是件好事。因为实际上它们是类的一部分 - 而C++奇怪的要求是它们被作为自由函数实现,所以我们将重复上一次的重点:让它们访问类内部不可能破坏封装,因为实际上它们是类的一部分 - 并没有改变这个事实,只有一个,单一的,孤独的。

+0

谢谢你的详细解释,这正是我所希望的。你关于封装和朋友函数的一般用法的观点非常清楚和有用。 – Garp 2011-05-26 09:05:21

4

使用get_n,因为这不是proper usage of friend。如果get_n是一个简单的return n,编译器很可能会自动去inline

+5

-1对*朋友不友好的OOP *。不是这种情况。一个流行的例子是具有私人构造函数的类,它们拥有工厂级的朋友。这是依赖于朋友的非常好的面向对象设计。更详细的解释可以在[C++ FAQ lite](http://www.parashift.com/c++-faq-lite/friends.html#faq-14.2)中找到。 – 2011-05-25 13:25:22

+2

@ Space_C0wb0y:这与之不同,因为数据成员已公开公开。 – Puppy 2011-05-25 13:27:56

+0

@ Space_C0wb0y:感谢您的澄清(+1,因为我喜欢失败的概括),并且我知道* friend *的用法,但在这个特定问题的情况下,它确实不是解决方案。我将编辑我的答案更清楚。 – 2011-05-25 13:28:32

0

在这种情况下,最佳做法是让类实现toString方法,即输出操作符用于获取字符串表示形式。由于这是一个成员函数,因此它可以直接访问所有数据。它还具有额外的好处,您可以使此方法vritual,以便子类可以覆盖它,并且您只需要一个输出运算符的基类。

+0

它已经有一个'toString'方法 - 流插入操作符。 – Puppy 2011-05-25 13:46:52

+0

@DeadMG:流插入操作符不是一个方法(对于我来说就是指*成员函数*)。它总是必须是一个自由函数(对于用户定义的类型)。 – 2011-05-25 13:52:25

2

我会回答一个问题,你的问题:

  • 你为什么要创建摆在首位公开get_n()
+0

这是一个虚拟的例子。我可以有一个私有sz成员和size()const公共函数的简单容器。 – Garp 2011-05-25 13:58:56

+0

@Garp:那是一个修辞问题。 – 2011-05-25 14:00:11

0

运营商可以在不使用friend的情况下实施吗?是的 - 不要使用friend。没有 - 制造friend