2012-10-10 29 views
5

在由Bjarne Stroustrup撰写的The C++ Programming Language中,作者介绍了一个类Matrix,它必须实现一个函数inv()。在第11.5.1节中,他谈到了 关于这样做的两种可能性。一个是做成员函数,另一个是 做一个朋友函数inv()。然后对部分11.5.2,其中他谈到 作出是否使用朋友或成员函数的选择结束时,他说:当函数应该改变对象的状态时,是否去成员函数或朋友函数?

如果INV()确实反转矩阵M本身,而比返回一个新的矩阵 是m的倒数,它应该是一个成员。

这是为什么?朋友功能不能改变矩阵的状态并将 引用返回给矩阵?是因为当我们调用该函数时可能会传递一个临时矩阵 ..

+1

可能在对象内部有更清晰定义的功能封装,而不是在不同的朋友功能中传播。 – PherricOxide

+2

相关http://stackoverflow.com/a/7821482/46642 –

+0

我认为这里值得赞扬的答案应该指出成员函数和非成员朋友之间的实际差异,并解释这些差异如何有助于作出决定,而不是用“因为它提供更多/更少的封装”而将所有东西都浪费掉了。 –

回答

9

说实话,我认为做出这样一个决定的唯一原因是语法方便和传统。我会解释为什么通过展示什么是(而非)两者之间的差异,以及在做出决定时这些差异如何重要。

非会员朋友功能和公共会员功能有什么不同?不多。毕竟,成员函数只是一个具有隐藏this参数的常规函数​​,并且可以访问该类的私有成员。

// what is the difference between the two inv functions? 
// --- our code --- 
struct matrix1x1 { // this one is simple :P 
private: 
    double x; 
public: 
    //... blah blah 
    void inv() { x = 1/x; } 
    friend void inv(matrix1x1& self) { self.x = 1/self.x; } 
}; 
matrix1x1 a; 

// --- client code --- 

// pretty much just this: 
a.inv(); 
// vs this: 
inv(a); 

void lets_try_to_break_encapsulation(matrix1x1& thingy) { 
    thingy.x = 42; // oops, error. Nope, still encapsulated. 
} 

它们都提供相同的功能,并且决不会改变其他功能可以做的事情。相同的内部结构暴露于外部世界:封装方面没有区别。其他函数完全可以做不同的事情,因为有一个修改私有状态的朋友函数。实际上,大多数函数都可以编写非会员朋友函数(虚函数和一些重载操作符必须是成员),以提供完全相同的封装量:用户不能编写任何其他朋友函数而不修改除了朋友功能以外,没有其他功能可以访问私人成员。我们为什么不这样做?因为它会违背99.99%的C++程序员的风格,并且没有很大的优势可以从中获益。

不同之处在于功能的性质和您称之为的方式。作为成员函数意味着你可以从它获得一个指向成员函数的指针,并且作为一个非成员函数意味着你可以得到一个指向它的函数指针。但这很少相关(特别是使用通用函数包装器,如std::function)。

其余的区别是句法。 D语言的设计者决定统一整个事情,并说你可以直接调用一个成员函数,如inv(a),然后调用一个自由函数作为它的第一个参数的成员,如a.inv()。没有任何班级因为这个或者任何东西而突然被封装得不好。

要解决问题中的特定示例,inv应该是成员还是非成员?我可能会让它成为一个成员,因为我上面概述的相似性论点。非风格化,它没有区别。


。这在C++中不太可能发生,因为在这一点上,这将是一个突破性改变,因为没有实质性的好处。对于一个极端的例子,它会打破我上面写的matrix1x1类,因为它使得两个调用都不明确。

+0

你已经提供了一个完全包含好友功能的例子,但是当有人让一个班级成为朋友时呢?可以说这确实减少甚至破坏封装。 – Brady

+0

关于成员函数vs朋友函数的问题值得讨论(它对于朋友类没有任何意义)。如果是关于课程,我仍然需要一个例子来接受这个声明。 –

+0

好点。我的回答是指一般的朋友(班级和功能)。 STL在几个地方使用好友功能,但我不认为它使用好友类。 – Brady

0

OOD(C++试图提升)所固有的封装理念规定对象状态只能从内部修改。 它在语法上是正确的(编译器允许它),但应该避免它。

如果不使用预定义的接口,整个系统中的元素会彼此改变是很容易出错的。 对象存储和功能可能会改变,并且寻找使用对象的特定部分的代码(可能很大)将是一场噩梦。

0

有关于使用朋友两个对立的论点:

一边说,朋友减少了封装,因为现在你让外部实体访问一个类的内部和内部只能由成员方法进行修改。

另一方面说朋友实际上可以增加封装,因为您可以将一个类的内部访问权限授予一小组外部实体,从而避免了将内部类属性公开给所有人的需要,因此这些外部实体可以访问/操作它们。

双方都可以争论,但我倾向于同意第一个选项。

至于你的问题,其作为PherricOxide在他的评论中提到:如果一个类的内部属性需要修改,它更好的是由成员方法完成,从而强制封装。这与上面提到的第一个选项是一致的。

+0

为什么downvote ?? ?? – Brady

相关问题