私有成员函数和结构以及实现文件本地的帮助程序

private member functions & structs and helpers local to the implementation file

本文关键字:文件 实现 帮助程序 成员 函数 结构      更新时间:2023-10-16

考虑一个具有一组方法的类

class myClass {
  private:
    int someFunc(const SomeClass&);
  public:
    funcA();
    funcB();
    funcC();
}

在实现文件中,我有一个本地结构定义和一些在匿名名称空间中使用它的辅助函数

namespace {
  struct Foo { ... };
  int helperFunc(const Foo&){
    SomeClass c;
    // do stuff with Foo and SomeClass to calculate someInteger
    return someInteger;
  }
  Foo makeFoo(){
     Foo foo;
     // configure foo
     return foo;
  }
}
myClass::funcA(){
  Foo foo = makeFoo();
  return helperFunc(foo);
}
myClass::funcB(){
  Foo foo = makeFoo();
  return helperFunc(foo);
}
myClass::funcC(){
  Foo foo = makeFoo();
  return helperFunc(foo)
}

现在,我发现我需要将helperFunc更改为使用someFunc(c)myClass的私有方法。

理想情况下,我想

  • Foo的定义保持在匿名命名空间的本地
  • 保持someFunc私有
  • 保持helperFunc作为一个函数,因为它在类实现中被多次使用
  • 保持makeFoohelperFunc分离,因为在某些情况下它是单独使用的

作为最后的手段,可以将helperFunc转换为预编译宏,但我认为这不是很优雅,仍然希望有一些我不知道的技巧来实现我的目标。

我非常感谢任何建议。

您可以将std::function对象传递给helperFunc。可以使用std::bind:从您的私有方法构造此对象

namespace {
  ...
  int helperFunc(const Foo&, std::function<int(const SomeClass&)> func){
    SomeClass c;
    ...
    // Invoke passed-in function
    int funcResult = func(c);
    ...
    return someInteger;
  }
  ...
}
myClass::funcA(){
  Foo foo = makeFoo();
  return helperFunc(foo, std::bind(&myClass::someFunc, this, std::placeholders::_1));
}

有不止一种方法可以做到这一点。就我个人而言,我会选择:

namespace detail
{
    int myClassSomeFuncAccessor(const SomeClass&);
}
class myClass {
  private:
    int someFunc(const SomeClass&);
  public:
    int funcA();
    int funcB();
    int funcC();
  private:
    friend int detail::myClassSomeFuncAccessor(const SomeClass&);
};

它有优点也有缺点。

优点:

  • 将类及其访问器与实现类解耦

  • detail命名空间访问器表示这不是"官方公共"接口的一部分

缺点:

  • 解耦实际上是解耦的:任何人都可以通过detail命名空间访问器访问内部

我认为现在是使用C++关键字"friend"的最佳时机。只需让你的内部类封装的朋友。这是最干净且支持语言的解决方案。在Alex Allain的一篇关于这个话题的文章中,他很好地解释了C++中的朋友说是如何不被禁忌的

有些人认为,拥有友元类的想法违反了封装的原则,因为这意味着一个类可以访问另一个类的内部。然而,考虑这一点的一种方法是,朋友只是一个类的整体界面的一部分,它显示了世界。就像电梯修理工可以访问与电梯操作员不同的接口一样,有些类或函数需要扩展访问另一个类的内部。此外,使用friend允许类通过隐藏比除了类的friend之外的任何东西所需要的更多的细节来向外部世界呈现更严格的接口。