我公司里有一位同事,他的意见我非常尊重,但我根本无法理解他在C++中编写代码的首选方式.
例如,鉴于有一些A类,他将编写该类型的全局函数:
void foo( A *ptrToA ){}
Run Code Online (Sandbox Code Playgroud)
要么:
void bar( const A &refToA ){}
Run Code Online (Sandbox Code Playgroud)
在看到这样的全球函数时,我的第一直觉是:"为什么不是A的这些成员?" 他会坚持认为这与C++中的良好实践建议是一致的,因为foo和bar可以通过使用A的公共接口执行他们需要执行的所有操作.例如,他会认为这是完全一致的与Scott Meyers有效的C++推荐.我发现很难将这与第19章中的第19项相协调,它基本上说一切都应该是一个成员函数,但有一些例外(operator <<和operator >>以及需要动态类型转换的函数).此外,虽然我同意的功能可以做他们需要一个公共接口做什么,在我看来,这主要是人写有getter和setter类因此与A级的每一个数据成员的结果公共接口,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
};
Run Code Online (Sandbox Code Playgroud)
然后我会看到这样的函数:
void wheelsRelatedToCar( Car *aCar, list< Wheel > &wheels )
{
aCar->wheelsOnCar( wheels );
wheels.push_back( aCar->getSpare() );
}
Run Code Online (Sandbox Code Playgroud)
这是一个真实的例子,当然改变了类和函数的名称.为什么人们不想wheelsRelatedToCar成为Car的会员?在这个真实的例子中,Car和Wheel在同一个库中.全局函数是使用该库在特定应用程序的源文件中定义的,因此该参数使该函数特定于应用程序.我的回答是,这是一辆完全合法的汽车操作,属于汽车级别.是否有其他观点来看待它(除了不喜欢使用面向对象设计的人)?
Mic*_*urr 21
Scott Meyers主张非成员函数通常会改进封装:
Herb Sutter和Jim Hyslop也在"自给自足的标题"中谈到这一点(引用迈耶的文章)
这些想法在被重印(以更精致的形式)梅尔的"有效的C++"第三版,"第23项:不想非成员非友元函数成员函数",和萨特/ Alexandrescu的的"C++编码标准"," 44 - 更喜欢写非会员非友好职能".
我认为很多开发人员认为这不直观,可能有点争议.
Kea*_*eks 20
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
参考文献:
Bin*_*ier 13
好
您的配合是正确的,因为如果它们不是类的一部分,那么构建泛型方法会更容易(例如,为什么std :: find不是std :: vector的成员?因为它不适用于列表,映射等) .
你也是对的,因为暴露太多而无法做到这一点会破坏封装.
当您进入现实世界并开始编写应用程序并开始工作到最后期限时,您会发现每个应用程序都是一组竞争要求."我们需要获得A,B和C,但到下个月底.这是不可能的,他们可以拥有B&C,或A&C,但不是A和B.或者他们可以拥有 - A&B的理想版本和C"的优秀版本.
编写代码没有什么不同,有很多定义封装,通用性,内聚等理想级别的法律和规则,但是其中很多都是矛盾的,你花了很多时间试图满足它们所有你没有做到的事情.
我总是说这些校长和"法律"实际上只是指导方针,在你可以的地方跟随它们,找到你自己与你相处的水平...并期望这些水平每6个月左右改变一次:)
希望这可以帮助.