C++ 在实例方法和自由函数之间选择?

C++ 在实例方法和自由函数之间选择?,c++,C++,向类添加功能可以通过添加方法或定义将对象作为其第一个参数的函数来完成。我认识的大多数程序员都会选择添加实例方法的解决方案 然而,我有时更喜欢创建一个单独的函数。例如,在下面的示例代码中,区域和对角线被定义为自由函数而不是方法。我觉得这样更好,因为我认为这些功能提供了增强功能,而不是核心功能 这被认为是一种好的/坏的做法吗?如果答案是“视情况而定”,那么在添加方法还是定义单独的函数之间做出决定的规则是什么 class Rect { public: Rect(int x, int y, in

向类添加功能可以通过添加方法或定义将对象作为其第一个参数的函数来完成。我认识的大多数程序员都会选择添加实例方法的解决方案

然而,我有时更喜欢创建一个单独的函数。例如,在下面的示例代码中,
区域
对角线
被定义为自由函数而不是方法。我觉得这样更好,因为我认为这些功能提供了增强功能,而不是核心功能

这被认为是一种好的/坏的做法吗?如果答案是“视情况而定”,那么在添加方法还是定义单独的函数之间做出决定的规则是什么

class Rect
{
public:
    Rect(int x, int y, int w, int h) :
        mX(x), mY(y), mWidth(w), mHeight(h)
    {
    }

    int x() const { return mX; }

    int y() const { return mY; }

    int width() const { return mWidth; }

    int height() const { return mHeight; }

private:
    int mX, mY, mWidth, mHeight;
};


int Area(const Rect & inRect)
{
    return inRect.width() * inRect.height();
}


float Diagonal(const Rect & inRect)
{
    return std::sqrt(std::pow(static_cast<float>(inRect.width()), 2) + std::pow(static_cast<float>(inRect.height()), 2));
}
class Rect
{
公众:
矩形(整数x,整数y,整数w,整数h):
mX(x)、mY(y)、mWidth(w)、mHeight(h)
{
}
int x()常量{return mX;}
int y()常量{return mY;}
int width()常量{return mWidth;}
int height()常量{return mHeight;}
私人:
int mX,mY,mWidth,mHeight;
};
内部区域(const Rect和inRect)
{
返回范围宽度()*范围高度();
}
浮动对角线(常数直线和直线)
{
返回std::sqrt(std::pow(static_cast(inRect.width()),2)+std::pow(static_cast(inRect.height()),2));
}

我认为这取决于你的目标是什么。如果函数是成员函数,则可以使用。和->符号,它更好地与类集成。它还允许多态性。如果希望函数是虚拟的,则它必须是成员函数

然而,有一种学派认为,只有在函数需要时才使用成员函数是更好的封装方式。因此,如果函数实际上不需要访问成员变量(可能是因为它只需要使用公共函数就可以完成它需要做的事情),那么您可以将其作为一个单独的函数,而不是类的成员。这样,即使是意外,它也不会弄乱内部结构

另一个需要考虑的问题是,如果它不是一个成员函数,那么您必须担心是否可以或应该将其他类型强制转换为现在是该函数第一个参数的类型。有时这是可取的,有时不是。如果它是一个成员函数,那么这不是一个问题(或者一个机会,取决于您试图做什么)

就个人而言,我不喜欢与类分离的函数,但是当您无法控制类,或者出于兼容性原因或诸如此类的原因而无法更改它时,您常常被迫以这种方式编写函数

真的,我认为这取决于什么对你最重要,以及你真正想做什么。我认为将函数作为类的一部分可以使它更易于使用,更面向对象,但如果您限制对类的私有成员的访问量,则更好地进行加密。

std::string
探讨了这个问题。他倾向于相反的想法:大多数职能应该是全球性的,除非它们真的需要成为成员


我认为大多数现代C++都倾向于相同的思想。例如,如果您查看Boost,很大一部分是使用自由函数完成的。

如果您想将其用于其他不相关的类,则它应该是一个带有覆盖的自由函数

例如,您可以覆盖
浮动区域(六边形常量&)
浮动对角线(六边形常量&)
x
y
width
height
然而,对于不同的形状意味着不同的东西

抽象基类(或不相关的同名方法)是另一种解决方案,但自由函数是用户可扩展的,因此用户在添加自己的内容后可以有效地获得更统一的接口


避免使用名称不明确的自由函数,这样不相关的函数就不会成为“竞争性”重写。

我喜欢本文中Scott Meyers的这句话:>我将从一个笑话开始:如果您编写的函数既可以作为成员也可以作为非好友非成员实现,您应该更愿意将其实现为非成员函数。这个决定增加了类封装。当你想到封装时,你应该想到非成员函数。我唯一可能想到的是名称空间污染。float Geometry::Area(const Geometry::Rect&r)比float Geometry::Rect::Area()好吗?您并不真正希望空闲函数区域位于全局命名空间中,因此需要将其放入命名空间中。在这一点上,它看起来像一个静态类函数。@jmucchiello:是的,大多数自由函数应该在名称空间中。我不认为这会使函数看起来像静态成员函数——我习惯于
x::y
意思是“名称空间x中的函数y”。@jmucchiello:如果你把它放在相关的名称空间中,那么(无论是好是坏)它会被ADL找到。所以你可以做
Geometry::rectr(12,23);斯科特·梅耶斯能站在我这边吗。我现在可以死了:)一个旁注,使用功率恒定为2的pow与自己拼写xx+yy+z*z相比,效率极低。@Mark B:谢谢,我不知道。看经典。(“当您考虑封装时,您应该考虑非成员函数”)也请参见。