使用非成员函数是一种好习惯吗?

时间:2019-09-13 12:21:25

标签: c++ c++11

假设我有一个类,其中有多个功能相似的逻辑。由于我不想重复自己,因此我在函数中提取了类似的逻辑。如果 similarLogic 不使用任何类成员,是否可以将其作为非成员函数?还是有更好的方法来做到这一点?

请注意,就我而言, similarLogic 函数严格地专用于 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);
}

3 个答案:

答案 0 :(得分:8)

我个人将assemblydirective写在instruction中的匿名命名空间中。这样,它在该翻译单元之外是不可见的:

similarLogic

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

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

答案 1 :(得分:2)

如果该功能仅由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);
}

答案 2 :(得分:1)

通常,一个类上的方法可以更改实例的状态,而(自由)函数希望没有副作用。 (我不是说必须要这样,但应该应该。)本着这种精神,将重复的代码放在某个自由函数中绝对没有错,即使它是无状态的,也更是如此。没有副作用。

但是,您可以在另一层回答您的问题。编译您的类后,其布局就会形成一个二进制接口,使用该类的每个代码都将链接到该二进制接口。当您通过修改成员顺序,引入变量或添加方法来更改类时,您引入到每个调用方的重大更改。在最佳情况下,仅重新编译相关代码,但是如果您的类是通过头文件公开并通过共享或静态对象进行链接的,那么您就很麻烦-不管添加的内容是否为{{1} }。

私有实施(PIMPL)习惯用法

为了解决这个问题,可以使用“ PIMPL”惯用语,例如在cpppatterns.com/patterns/pimpl.html上。本质上,这个想法是这样的:

您没有在类定义中包含私有方法或成员声明,​​而是仅提供公共方法和指向匿名类的单个指针。仅在支持类声明的.cpp文件中定义此“匿名”类。它对外部世界是不可见的,并且您的逻辑私有的所有内容都在此实现。

要从cpppatterns借用,它是这样的:

在头文件中,您仅使用可公开访问的成员和匿名声明类型为 wget https://chromedriver.storage.googleapis.com/77.0.3865.40/chromedriver_linux64.zip 的单个私有指针private来定义类foo

pimpl

在代码中,您都定义匿名类foo::impl,并使// foo.h - header file #include <memory> class foo { public: foo(); ~foo(); foo(foo&&); foo& operator=(foo&&); private: class impl; std::unique_ptr<impl> pimpl; }; 的每个公共方法都调用{{1}中的私有实现}。这是foo::impl类,它显示了您的实际状态:

foo

如果尝试此操作,请注意,这里的最后三行是使其起作用的关键,因为构造函数,析构函数和复制运算符只能在已知类型foo::impl时定义。

非常类似于您使用免费的“私有”功能的想法,foo::impl的任何用户都无法观察到// foo.cpp - implementation file class foo::impl { public: void do_internal_work() { internal_data = 5; } private: int internal_data = 0; }; foo::foo() : pimpl{std::make_unique<impl>()} { pimpl->do_internal_work(); } foo::~foo() = default; foo::foo(foo&&) = default; foo& foo::operator=(foo&&) = default; 中引入的核心逻辑的任何变化,并且您可以上课在状态上运行的方法。

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

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