C++ 使用非成员函数是一种好的做法吗?

C++ 使用非成员函数是一种好的做法吗?,c++,c++11,C++,C++11,假设我有一个类,其中有多个逻辑相似的函数。因为我不想重复我自己,我在一个函数中提取类似的逻辑。如果similarLogic不使用任何类成员,那么将其作为非成员函数是否是一种良好的做法?还是有更好的方法 请注意,在我的例子中,similarLogic函数严格特定于MyClass,因此它不会在MyClass之外的任何地方使用 非成员函数的示例: MyClass.h class MyClass { public: int func1(); int func2(); }; MyClas

假设我有一个类,其中有多个逻辑相似的函数。因为我不想重复我自己,我在一个函数中提取类似的逻辑。如果similarLogic不使用任何类成员,那么将其作为非成员函数是否是一种良好的做法?还是有更好的方法

请注意,在我的例子中,similarLogic函数严格特定于MyClass,因此它不会在MyClass之外的任何地方使用

非成员函数的示例:

MyClass.h

class MyClass {
public:
    int func1();
    int func2();
};
MyClass.cpp

int similarLogic(int p_num){
    return 5 + p_num;
}

int MyClass::func1() {
    return similarLogic(1);
}

int MyClass::func2() {
    return similarLogic(2);
}

就我个人而言,我会在
MyClass.cpp
中的匿名名称空间中编写
similarLogic
。这样,它在翻译单元外就看不见了:

namespace {
    int similarLogic(int p_num){
        return 5 + p_num;
    }
}
其他方法,例如类的
私有成员(
static
或其他方法)污染了类定义。但这并不意味着这种方法是一个糟糕的选择。如果函数将来需要类成员数据,那么重构比使用我喜欢的方法更简单


无论您最终做什么,都不希望污染全局命名空间。

如果该功能仅由
MyClass
使用,则该函数应为私有成员函数

如果它不使用任何
MyClass
属性,您最终可以将其设置为静态,但这不是必需的

如果类似的逻辑可以应用于不同的类型,那么也可以使用模板函数。例如:

// MyClass.h

class MyClass {
public:
    int func1();
    int func2();
private:
    template<class T> void similarLogic(T arg); 
};

template<class T> MyClass::similarLogic(T arg) {
    // Generic implementation
}
template<> MyClass::similarLogic<float >(float arg) {
    // Specialization for int
}

// MyClass.cpp
int MyClass::func1() {
    // Call generic implementation for int
    return similarLogic<int>(1);
}

int MyClass::func2() {
    // Call specialization for float
    return similarLogic<float>(2.34);
}
//MyClass.h
类MyClass{
公众:
int func1();
int func2();
私人:
模板无效相似逻辑(T参数);
};
模板MyClass::similarLogic(T参数){
//通用实现
}
模板MyClass::similarLogic(浮点参数){
//int的专门化
}
//MyClass.cpp
int MyClass::func1(){
//调用int的泛型实现
返回相似逻辑(1);
}
int MyClass::func2(){
//调用float的专门化
返回相似逻辑(2.34);
}

通常,类上的方法用于更改实例的状态,而(free)函数则有望避免副作用。(我不是说它必须是,但它应该是。)本着这种精神,将重复代码放在自由函数的某个地方绝对没有错,如果它是无状态的,没有副作用的话,更是如此

然而,你的问题还有另一层可以回答。编译类的那一刻,它的布局就形成了一个二进制接口,使用类的每一个代码都链接到该接口。当您通过修改成员顺序、引入变量或添加方法来更改类时,您将为每个调用方引入突破性的更改。在最好的情况下,只需重新编译依赖代码,但如果您的类通过头文件公开,并通过共享或静态对象链接,则无论您添加的内容是否为私有的,您都会遇到麻烦

私有实现(PIMPL)习惯用法 为了解决这一问题,可以使用“PIMPL”习语,如上所述。从本质上讲,这一想法如下:

与在类定义中使用私有方法或成员声明不同,您只提供公共方法和指向匿名类的单个指针。此“匿名”类仅在支持类声明的.cpp文件中定义。外部世界看不到它,所有对您的逻辑私有的东西都在那里实现

借用cpppatterns,如下所示:

在头文件中,您仅使用可公开访问的成员和一个匿名声明类型的私有指针
foo::impl
定义类
foo

//foo.h-头文件
#包括
福班
{
公众:
foo();
~foo();
foo(foo&&);
foo&运算符=(foo&&);
私人:
类impl;
std::唯一的ptr pimpl;
};
在代码中,您定义了anonyous类
foo::impl
,并使
foo
的每个公共方法调用
foo::impl
中的私有实现。正是这个
foo::impl
类承载了您的实际状态:

//foo.cpp-实现文件
类foo::impl
{
公众:
无效内部工作()
{
内部数据=5;
}
私人:
int内部_数据=0;
};
foo::foo()
:pimpl{std::make_unique()}
{
pimpl->做内部工作();
}
foo::~foo()=默认值;
foo::foo(foo&&)=默认值;
foo&foo::operator=(foo&&)=默认值;
如果您尝试这样做,请注意,这里的最后三行是使其工作的关键,因为构造函数、析构函数和复制运算符只能在类型
foo::impl
已知时定义

与您的免费“私有”函数的想法非常相似,
foo::impl
中引入的核心逻辑的任何更改都不会被
foo
的任何用户看到,并且您可以让类方法在状态上运行

另一方面,成本是引入更多代码来编写和维护,以及(至少在理论上)将调用沿着私有指针传递到实际实现


所以。。。为工作选择合适的工具。你想到的那个很好。:)

首先,不要像那样将它放在全局名称空间中,因为这样可以从其他名称空间中使用它。要么使其
静态
,要么(在我看来更好)将其放入匿名命名空间中。除此之外,如果函数不需要类的任何私有数据,那也没关系。询问一个非成员函数(“自由函数”)是否自动是一个好的实践——不提供任何进一步的上下文,就像询问使用
int
是否是一个好的实践一样。你可以滥用免费的功能,你可以使用它们来获得巨大的好处。这个问题太宽泛了。
similarLogic
函数是否严格特定于
MyClass
?或者它可能是一个