8

If a class has lots of private functions it seems undesirable to have them declared in the header file. It makes it harder for clients to parse the interface of the class and it increases compilation time.

One option is, of course, the pimpl idiom, but that adds a layer of indirection and also results in a memory allocation.

What follows is a example of hiding helper functions in a friend class that is defined in the implementation file.

Is there a name for this idiom?

Is there any reason not to do it?

//Foo.h
class Foo
{
public:
    void FuncA();
    void FuncB();
    
private:
    int x;
    // more data...

    friend struct FooHelpers;
};
//Foo.cpp
struct FooHelpers
{
    static void Helper1(Foo& f)
    {
        // manipulate private data..
        f.x++; //etc.
    }
    // Possibly more funcs...
};

void Foo::FuncA()
{
    //....
    FooHelpers::Helper1(*this);
    //....
}
//....
AILien
  • 832
  • 4
  • 11
  • you know that you can also make a function a friend of a class? Classes with only static methods are commonly discouraged, so I don't think there is an idiom that does exactly that – 463035818_is_not_an_ai Apr 26 '21 at 09:06
  • @largest_prime_is_463035818 but then I would need to declare every helper function in the header file. The goal was to not have these functions in the header file at all since users of the class shouldn't need to know about them. – AILien Apr 26 '21 at 09:10
  • 1
    @largest_prime_is_463035818 the problem with making each function a friend is again that it bloats the class definition even more than just decalring those functions. It just adds a `friend` before each line... – Stefan Riedel Apr 26 '21 at 09:10
  • 5
    I've found this which does a similar thing to what you are doing: https://stackoverflow.com/a/28734794 – Artyer Apr 26 '21 at 09:21
  • 1
    I think you should generally aim for small classes. `[If a class has lots of private functions]` it's mostly better to redesign that class (or make multiple classes). Of course that doesn't always work, and if you really want to hide all those private methods, a private friend helper class could be an acceptable approach. Although "classes with only static methods" are generally discouraged", this approach is easily understandable. – Stefan Riedel Apr 26 '21 at 09:23
  • Don't expect this to have much effect on compile time as a function declaration is a simple thing to parse. If compile time is the goal, then any complex member variables probably have a higher effect since they might require additional header files to be included. – olm Apr 26 '21 at 09:44
  • 1
    The main reason not to do it: It makes the code harder to read, which in most cases are more important than the compile time. – olm Apr 26 '21 at 09:45
  • 1
    @zkoza if I "define them in the unnamed namespace in the same file as the class implementation" then they won't have access to private data. I don't understand what you mean by "put your class and the functions in a separate namespace and hide the helper functions inside it", how would the functions be hidden in this case? – AILien Apr 26 '21 at 10:19
  • @olm I concede it makes the implementation file slightly harder to read. But I think it can be worth it to have an easier to read header file, which will usually be read more often than the implementation. – AILien Apr 26 '21 at 10:24
  • @olm regarding compilation time, exposing the internal functions in the header could necessitate including more header files if any of the those functions have default parameters of types not already defined. – AILien Apr 26 '21 at 10:29
  • @AILien You're right. – zkoza Apr 26 '21 at 11:12
  • No need of `friend struct`, inner `struct` does the job too [Demo](https://godbolt.org/z/Gs3orvK88). – Jarod42 Apr 26 '21 at 13:02
  • You're right. This is what https://stackoverflow.com/a/28734794 talks about. I've now learnt that the inner struct approach is better because if there was a 3rd class that had given Foo friend access then the inner struct would have that privilege too, whereas the friend FooHelpers class in my example would not. – AILien Apr 26 '21 at 14:33

1 Answers1

-1

Is there a name for the idiom of using a friend class to hide what would have been private functions?

The Attorney-Client idiom.

Is there any reason not to do it?

As suggested also in some comments, here's a reason not to do it: while it provides a patterned option, it avoids dealing with the root of the problem - the design of the original class (class Foo).

In your question you write:

If a class has lots of private functions it seems undesirable to have them declared in the header file.

There's some subjectivity to this question so allow me to restate it more objectively with the following question.

Are there ways to avoid private member functions?

You pointed out one way, to use the PIMPL idiom. As also suggested in some comments, here's some more:

  1. Refactor the original (a.k.a. client) class to instead use non-member non-friend functions or lambda expressions that manipulate your class member data through parameters - so that it doesn't need private member functions.
  2. Split up the class into multiple smaller classes so that each of the smaller classes declares less of the original class's private member functions. Like with member data, the more private member functions a class has, the more likely - at least in my opinion - the class is violating the single responsibility principal. So this kind of effort can reduce the private member functions as well making your class design more robust.

Say we start with the following code:

//Foo.h
class Foo
{
public:
    void FuncA();
    void FuncB();
    
private:
    void multiplyByTwo();

    int x;
    // more data...
};
//Foo.cpp
void Foo::multiplyByTwo()
{
    x = x * 2;
}

void Foo::FuncA()
{
    multiplyByTwo();
}

Here's an example using the first way of what this could be changed to:

//Foo.h
class Foo
{
public:
    void FuncA();
    void FuncB();
    
private:
    int x;
    // more data...
};
//Foo.cpp
namespace {
int multiplyByTwo(int x)
{
    return x * 2;
}
}

void Foo::FuncA()
{
    x = multiplyByTwo(x);
}

In addition to eliminating the declaration of multiplyByTwo as a private member function of class Foo, this change also more idiomatically expresses the intention of the multiplyByTwo function in now saying in C++ syntax what's its input and output.

Louis Langholtz
  • 2,913
  • 3
  • 17
  • 40