当函数应该改变对象的状态时,是选择成员函数还是朋友函数
Whether to go for a member function or friend function when the function is supposed to change state of object?
在Bjarne Stroustrup的《C++编程语言》一书中,作者介绍了一个类Matrix,它必须实现一个函数inv()。在第11.5.1节中,他谈到关于这样做的两种可能性。一个是建立成员函数,另一个是make a friend函数inv()。然后在第11.5.2节的末尾,他谈到他说:
如果inv()真的反转了矩阵m本身,而不是返回一个新的矩阵是m的倒数,它应该是一个成员。
为什么会这样?友元函数不能更改矩阵的状态并返回参考该矩阵?是因为传递临时矩阵的可能性吗当我们调用函数时?。。
老实说,我认为做出这样一个决定的唯一原因是语法上的便利和传统。我将通过展示两者之间的差异来解释为什么,以及在做出决定时这些差异是如何重要的。
非会员好友功能和公共会员功能之间有什么区别?不多毕竟,成员函数只是一个具有隐藏的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()
。没有一个类突然因为这个或其他原因而被封装得很糟糕1
为了解决问题中的特定示例,inv
应该是成员还是非成员?我可能会让它成为一名成员,因为我在上面概述了家族性的论点。从非风格上讲,这没有什么区别。
1。这在C++中不太可能发生,因为在这一点上,这将是一个突破性的变化,没有实质性的好处。举一个极端的例子,它会破坏我上面写的matrix1x1
类,因为它使两个调用都不明确。
OOD(C++试图推广)固有的封装哲学规定,对象状态只能从内部修改。它在语法上是正确的(编译器允许),但应该避免。
让整个系统中的元素在不使用预定义接口的情况下相互更改是很容易出错的。对象存储和功能可能会发生变化,四处寻找使用对象特定部分的代码(可能很大)将是一场噩梦。
关于使用朋友有两个相反的论点:
一方面,friends减少了封装,因为现在您允许外部实体访问类的内部,并且内部只能由成员方法修改。
另一方面,friends实际上可以增加封装,因为您可以将对类内部的访问权限授予一小部分外部实体,从而无需将内部类属性向所有人公开,以便这些外部实体可以访问/操作它们。
双方都可以争论,但我倾向于同意第一种选择。
至于你的问题,正如PherricOxide在评论中提到的那样:如果一个类的内部属性需要修改,最好由成员方法来完成,从而强制封装。这与上面提到的第一个选项是一致的。
- 选择要调用的构造函数
- 无法获取菜单选择以运行函数.C++
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本
- LLVM 选择找不到函数传递
- C++ 带有默认参数的结构,可选择在构造函数中更改
- 如何在"push_*()"和"emplace_*()"函数之间进行选择?
- 如何在字符函数中选择某些字符?
- OpenCL 内置函数选择
- 编译时构造函数选择
- 内联函数选择条件
- c - 函数选择显示读取后stdin中有数据
- Clang 和 GCC 在使用大括号表示法和initializer_list时在构造函数选择上存在分歧
- 最小化函数选择和函数调用开销
- 当参数是引用时,可变模板构造函数选择失败
- 在运行时为调用函数选择对象,不包含基类和模板
- 为什么const临时函数选择调用非const成员函数而不是const成员函数?
- 如何消除构造函数选择的强制转换
- 是否有可能编写一个抽象类,其中构造函数选择适当的子类在c++中实例化
- c++模板函数选择
- vector insert中重载模板函数选择(模式匹配)是如何工作的