非成员非友元函数真的增加了封装吗? 我目前正在读Scott Meyers的有效C++书,但我无法理解第二十三项。他说:
与成员函数相比,更喜欢非成员非友元函数。这样做可以提高封装性、封装灵活性和功能扩展性 虽然我能看到在类之外添加外部函数的意义,但我看不到它的优点。他谈到这些,因为他们正在增加封装。是的,这是正确的,因为非成员非友元函数不能访问类中声明为私有的任何成员变量。但是,这就是我无法回避的问题。拥有允许对象控制的成员函数在某种程度上是必要的——对于所有数据成员都是公共的POD,可以做什么?老实说,我看不出有什么实际用途。Having说,即使我们有非成员非好友函数,封装也不会改变,因为我们仍然需要!!公众!!成员函数与对象交互非成员非友元函数真的增加了封装吗? 我目前正在读Scott Meyers的有效C++书,但我无法理解第二十三项。他说:,c++,C++,与成员函数相比,更喜欢非成员非友元函数。这样做可以提高封装性、封装灵活性和功能扩展性 虽然我能看到在类之外添加外部函数的意义,但我看不到它的优点。他谈到这些,因为他们正在增加封装。是的,这是正确的,因为非成员非友元函数不能访问类中声明为私有的任何成员变量。但是,这就是我无法回避的问题。拥有允许对象控制的成员函数在某种程度上是必要的——对于所有数据成员都是公共的POD,可以做什么?老实说,我看不出有什么实际用途。Having说,即使我们有非成员非好友函数,封装也不会改变,因为我们仍然需要!!公众!
那么,为什么我——或者其他人——更喜欢非成员非朋友函数而不是成员函数呢?当然,我们可以在已经存在的成员函数上编写包装器,这些成员函数可能按逻辑顺序对它们进行分组,但仅此而已。我在这里看不到任何重新简化的封装。迈尔斯给出了他的理由。以下是摘录: 我们现在已经看到,衡量类中封装量的一种合理方法是计算如果类的实现发生更改可能会中断的函数数。在这种情况下,很明显,具有n个成员函数的类比具有n+1个成员函数的类更容易封装。正是这一观察结果证明了我倾向于使用非成员非友元函数而不是成员函数的论点:如果函数f可以实现为成员函数或非友元非成员函数,则将其设为成员将减少封装,而将其设为非成员则不会 Meyers没有说避免成员函数。他说,除非有必要,否则职能部门不应该是成员(或朋友)。显然,需要一些函数来访问类的私有成员,否则其他代码如何与类交互,对吗 但是可以访问类的私有成员的每个函数都与该类的私有实现细节相耦合。应该是成员(或朋友)的功能只能通过访问私人详细信息来有效实现。这些是类的基本函数。非原语函数是可以在原语函数之上高效实现的函数。使非原语函数成为成员(或朋友)会增加与私有细节耦合的代码量 此外,在编写能够修改对象私有成员的函数时,必须更加小心,以保留类不变量。仅举一个小例子:
std::list
具有sort
成员函数,因为它得益于list元素的自然移动能力std::sort
free函数我将用这个简单的例子来回答OP的问题“为什么有人更喜欢非成员非朋友函数而不是成员函数?”。考虑一个从地理空间数据生成图形模拟的应用程序。数据以一种类似于您在指南针上看到的表示形式(以度为单位,顺时针旋转,其中y轴上的0点为北/正)。当您将方向信息传递给渲染器时,它可能会以类似于您从trig(以弧度为单位,逆时针旋转,其中0点在x轴上为右/正)所使用的表示形式出现 由于方向的两种表示都可以存储为浮点,因此可以编写一对装箱基本体来强制执行某种类型安全性(这样就不会意外地将方位角传递给需要角度的渲染调用)。要在这两种表示之间进行转换,需要在方位角上编写名为AsAngle()的成员函数,并在角度上编写名为AsAzimuth()的成员函数 这里封装的第一个故障是角度和方位角依赖于彼此的类型。您需要在另一个的头文件中向前声明一个,并#将其包含在源文件中,以便它可以在转换函数中构造另一个。您可以通过让转换函数返回float而不是另一个类的对象来减少这种依赖性,但这并不能完全消除彼此之间的逻辑依赖性,因为封装的下一个细分是两个类还必须知道另一个类的内部细节 如果以后要切换到以度而不是弧度表示角度的渲染器,则需要更改此不同表示的角度类。然而,即使唯一的改变是角度的细节,一个完全独立的类,方位角,现在也必须改变,否则它将继续以弧度而不是度返回角度。如果您更新了Angle的AsAzimuth()成员,但忘记了更新Azizont的AsAngle()成员,则在没有错误的情况下,当您从头开始查看对Angle所做的更改以查找错误时,渲染可能会出现错误 方位角不必关心角度的内部细节,但当您将转换例程作为成员函数实现时,它必须关心。如果您将转换编写为非成员函数,则两个类都不需要再关心另一个类的细节——如何在两个表示之间转换的问题现在完全封装在一个单独的函数中 如果你不喜欢,我不会
class Angle
{
public:
float GetValue() const;
Azimuth AsAzimuth() const;
private:
float m_Value;
};
class Azimuth
{
public:
float GetValue() const;
Angle AsAngle() const;
private:
float m_Value;
};
class Direction
{
public:
Angle AsAngle() const
{
return Angle(Convert(m_Azimuth.GetValue());
}
Azimuth AsAzimuth() const
{
return m_Azimuth.GetValue();
}
private:
float Convert(const float) const
{
...conversion stuffs here...
}
Azimuth m_OriginalAzimuth;
};