什么时候函数应该是成员函数? 我在公司有一位同事,我对他的意见非常尊重,但我根本就不懂他喜欢的C++代码编写风格。

什么时候函数应该是成员函数? 我在公司有一位同事,我对他的意见非常尊重,但我根本就不懂他喜欢的C++代码编写风格。,c++,C++,例如,如果有某个类A,他将编写以下类型的全局函数: void foo( A *ptrToA ){} 或: P>我看到这样的全局函数的第一本能是:“为什么这些成员不是A?”他会上下断定这与C++中的良好实践的建议一致,因为FoO和Bar可以使用A.的公共接口执行他们需要执行的所有操作。他会说这完全符合Scott Meyers有效的C++建议。我发现很难将这一点与书中的第19项相协调,书中的第19项基本上说所有内容都应该是成员函数,只有少数例外(操作符>和需要动态类型转换的函数)。此外,虽然我同

例如,如果有某个类A,他将编写以下类型的全局函数:

void foo( A *ptrToA ){}
或:

<> P>我看到这样的全局函数的第一本能是:“为什么这些成员不是A?”他会上下断定这与C++中的良好实践的建议一致,因为FoO和Bar可以使用A.的公共接口执行他们需要执行的所有操作。他会说这完全符合Scott Meyers有效的C++建议。我发现很难将这一点与书中的第19项相协调,书中的第19项基本上说所有内容都应该是成员函数,只有少数例外(操作符>和需要动态类型转换的函数)。此外,虽然我同意函数可以在A的公共接口上完成它们需要做的事情,但在我看来,这主要是人们编写类的结果,类A的每个数据成员都有getter和setter。因此,有了这个公共接口,A是一个过度美化的结构,您当然可以使用公共接口做任何事情。就我个人而言,我认为这不应该被利用,我认为应该被劝阻

显然,这只能在C++语言中,而不是纯面向对象的语言,所以我想一种方法是我的同事不喜欢纯面向对象的软件设计方法。是否有人知道支持此职位作为最佳实践的任何文献?或者是否有人同意这一点,并可能以与我的同事不同的方式向我解释这一点,以便我看到光明?还是每个人都同意我现在的感觉,这没有多大意义

编辑: 让我给出一个更好的代码示例

class Car
{
    Wheel frontLeft;
    Wheel frontRight;
    Wheel rearLeft;
    Wheel rearRight;
    Wheel spareInTrunk;

public:
    void wheelsOnCar( list< Wheel > &wheels )
    {
        wheels.push_back( frontLeft );
        wheels.push_back( frontRight);
        wheels.push_back( rearLeft);
        wheels.push_back( rearRight);
    }
    const Wheel & getSpare(){ return spareInTrunk; }
    void setSpare( const Wheel &newSpare ){ spareInTrunk = newSpare; }
    // There are getters and setters for the other wheels too,
    //but they aren't important for this example
};
等级车
{
左前轮;
右前轮;
左后车轮;
右后车轮;
车轮备用轴承;
公众:
无效车轮车辆(列表<车轮>&车轮)
{
车轮。向后推(左前);
车轮。向后推(右前方);
车轮。向后推(左后);
车轮。向后推(右后);
}
const Wheel&getSpare(){return spareInTrunk;}
void setpare(const-Wheel&newSpare){spareInTrunk=newSpare;}
//其他轮子也有getter和setter,
//但在这个例子中,它们并不重要
};
然后我将看到这样一个函数:

void wheelsRelatedToCar( Car *aCar, list< Wheel > &wheels )
{
    aCar->wheelsOnCar( wheels );
    wheels.push_back( aCar->getSpare() );
}
void wheels-relatedtocar(汽车*aCar,列表&车轮)
{
aCar->wheelsOnCar(车轮);
轮子。向后推(aCar->getSpare());
}

这是一个真实的例子,当然类和函数的名称也发生了变化。为什么人们希望
wheelsRelatedToCar
不是汽车的成员功能?在这个真实的例子中,汽车和车轮在同一个库中。全局函数是在使用该库的特定应用程序的源文件中定义的,因此该函数是特定于该应用程序的。我的回答是,这是一个完全合法的汽车操作,属于汽车类。是否有另一种观点来看待它(不喜欢使用面向对象设计的人除外)?

Scott Meyers主张非成员函数通常可以改进封装:

Herb Sutter和Jim Hyslop在“自给自足的标题”中也谈到了这一点(引用Meyer的文章)

这些想法已在“第23项:偏好非成员非朋友函数而非成员函数”和“44-偏好编写非成员非朋友函数”中重新发布(以更精细的形式)


我想很多开发人员都觉得这不直观,可能有点争议。

您可以回答自己的问题。如果全局函数只能使用类的光滑、流线型和未授权的公共接口来操作,那么它们应该是全局的。如果类已经变形以使这些函数成为可能,那么它就不是那么好了

Herb Sutter&Andrei Alexandrescu对此的建议:

避免会员费:在可能的情况下,更喜欢将功能设置为非会员非朋友。


非成员非朋友函数:

  • 通过最小化依赖项来改进封装:函数体不能依赖于类的非公共成员
  • 通过分离单片类来释放可分离的功能,从而减少耦合
  • 改进泛型性,因为很难编写不知道操作是否是给定类型的成员的模板

现在,为了回答您的问题(何时?),这里有一个算法来确定函数是否应该是成员和/或朋友:

If the function is one of the operators =, ->, [], or (), which must be members: => Make it a member Else if: a) the function needs a different type as its left-hand argument (as do stream operators, for example); or b) it needs type conversions on its leftmost argument; or c) it can be implemented using the class's public interface alone: => Make it a nonmember (and friend if needed in cases a) and b) ) If it needs to behave virtually: => Add a virtual member function to provide the virtual behavior, and implement the nonmember in terms of that Else: => Make it a member 如果函数是运算符=、->、[]或()之一,则必须是成员: =>成为会员 否则,如果: a) 函数需要一个不同的类型作为其左参数(例如,流运算符); 或者b)它需要在最左边的参数上进行类型转换; 或者c)可以单独使用类的公共接口来实现: =>使其成为非成员(在案例a和案例b中,如果需要,还可以成为朋友)) 如果需要虚拟操作: =>添加虚拟成员函数以提供虚拟行为,并根据该行为实现非成员 其他: =>成为会员
参考资料:

  • H.萨特和安德烈·亚历山德雷斯库。C++编码标准(Addison Wesley,2004)< /LI>
  • S.Meyers。“非成员函数如何改进封装”(C/C++用户杂志,18(2),2000年2月)
  • B.Stroustrup。C++程序设计语言(特殊3ReDebug)(Addison Wesley,2000)。§10.3.2, §11.3.2 If the function is one of the operators =, ->, [], or (), which must be members: => Make it a member Else if: a) the function needs a different type as its left-hand argument (as do stream operators, for example); or b) it needs type conversions on its leftmost argument; or c) it can be implemented using the class's public interface alone: => Make it a nonmember (and friend if needed in cases a) and b) ) If it needs to behave virtually: => Add a virtual member function to provide the virtual behavior, and implement the nonmember in terms of that Else: => Make it a member
    int diff(Date a,Date b); // number of days in the range [a,b) or [b,a)
    bool leapyear(int y);
    Date next_weekday(Date d);
    Date next_saturday(Date d);