当函数应该改变对象的状态时,是去成员函数还是朋友函数?

saj*_*jas 6 c++ member-functions friend-function

在Bjarne Stroustrup撰写的The C++ Programming Language一书中,作者介绍了一个必须实现函数inv()的类Matrix.在第11.5.1节中,他谈到了这样做的两种可能性.一个是建立一个成员函数,另一个是使朋友函数inv().然后在第11.5.2节结束时,他谈到选择是否使用朋友或成员函数,他说:

如果inv()确实反转了Matrix m本身,而不是返回一个与m相反的新Matrix,那么它应该是一个成员.

为什么会这样?朋友函数不能改变Matrix的状态并返回对该矩阵的引用吗?是因为在调用函数时传递临时矩阵的可能性是什么?

R. *_*des 10

说实话,我认为做出这样决定的唯一理由是语法上的便利和传统.我将通过展示两者之间的差异以及在做出决定时这些差异如何重要来解释原因.

非会员朋友功能和公共会员功能之间有什么区别?不多.毕竟,成员函数只是一个带有隐藏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.
}
Run Code Online (Sandbox Code Playgroud)

它们都提供相同的功能,决不会改变其他功能可以做的事情.相同的内部结构暴露于外部世界:封装方面没有区别.绝对没有其他函数可以做的不同,因为有一个友元函数可以修改私有状态.

事实上,人们可以编写大多数具有大多数功能的类作为非成员友元函数(虚函数和一些重载运算符必须是成员)提供完全相同的封装量:用户无法在不修改类的情况下编写任何其他朋友函数,并且没有朋友功能以外的功能可以访问私人会员.我们为什么不那样做?因为它违背了99.99%的C++程序员的风格,并且没有很大的优势可以从中获取.

不同之处在于功能的性质以及您调用它们的方式.作为成员函数意味着您可以从中获取指向成员函数的指针,并且作为非成员函数意味着您可以获得指向它的函数指针.但这很少相关(特别是像通用函数包装器一样std::function).

剩下的区别是句法.D语言的设计者决定统一整个事物并说你可以通过传递一个像这样的对象直接调用成员函数inv(a),并调用一个自由函数作为它的第一个参数的成员,就像a.inv().并没有因为这个或任何事情而突然被严重封装的课程.1

要解决问题中的特定示例,应该inv是成员还是非成员?我可能会把它变成一个成员,因为我上面概述了一个熟悉的论点.非风格,它没有任何区别.


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