71

I have always thought of header files as a sort of 'public interface' describing a class, in which case it would be better to keep private fields and functions in the .cpp file.

I understand that private fields need to be in the header so that other classes can tell how much memory an instance of a class will consume, but it occurred to me as I was about to write a private helper function, that this function could be made static, in which case there was no need for it to be 'part of the class' at all, it could just as easily be a regular function in the class definition's .cpp file.

It then occurred to me that all private functions could potentially be rewritten to be static by accepting pointers/references to class fields instead of expecting to be defined in the class.

This would eliminate the need to declare any private functions in the header file.

I do like to follow conventions, so is it considered an established convention in C++, that non-static private functions should be in the header file? What about static functions or static constants?

I'm going to put in some code to explain what I'm getting at:

.h file:

#ifndef SOME_CLASS_H
#define SOME_CLASS_H

class SomeClass
{
private:
    int x;
public:
    void combineWithX(int y);
};

#endif

.cpp file

#include "SomeClass.h"

void someHelper(int* x)
{
    *x = (*x) + 1;
}

void SomeClass::combineWithX(int y)
{
    someHelper(&x);
    x += y;
}

Note that someHelper(int* x) in the .cpp file references the private member x in spirit, but not directly, and therefore does not need to appear in the header. I'm wondering if this sort of thing is considered 'bad style'.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ghost314
  • 791
  • 1
  • 6
  • 10
  • 4
    Note that the private functions would need to be friended from inside the class, or else they couldn't access private members. If they don't need private members, maybe those functions shouldn't themselves be members at all. – Mooing Duck Feb 05 '15 at 01:03
  • 3
    Since you cannot reopen a class for expansion once closed (C++11 9.2-p2), yes, your member functions *and* member vars *of that class* have to be mated in its decl. You can certainly do a [pimpl](http://stackoverflow.com/questions/843389/the-pimpl-idiom-in-practice)-implementation if desired. – WhozCraig Feb 05 '15 at 01:05
  • @WhozCraig thanks, I borrowed your link (originally I linked to a worse question) – M.M Feb 05 '15 at 01:13
  • @MooingDuck private functions can be put inside a private inner class, this way they don't have to be in header. https://stackoverflow.com/a/28734794/463758 – balki Aug 03 '17 at 16:10
  • 1
    See thes answers for meaningful explanations *why* private functions (even if they are static or non-virtual) have to appear in the class declaration: https://softwareengineering.stackexchange.com/a/239175/221200 https://softwareengineering.stackexchange.com/a/324450/221200 – jcsahnwaldt Reinstate Monica Feb 10 '19 at 14:27

3 Answers3

49

Private helper functions can be hidden from the public header file by moving them to an inner class. This works because the inner class is considered part of the class and can access the surrounding class's private members.

Unlike the PIMPL idiom, this does not have any dynamic allocation or indirection penalty. Compile times should be faster when editing/refactoring private functions, as there isn't any need to recompile all the files, including the public header.

Example:

public .h file

#ifndef SOME_CLASS_H
#define SOME_CLASS_H

class SomeClass
{
private:
    // Just forward declaring in public header.
    struct Private;
    int x;
public:
    void combineWithX(int y);
};

#endif

in .cpp file

#include "SomeClass.h"

// Declare all private member functions of SomeClass here
struct SomeClass::Private
{
  static void someHelper(SomeClass& self)
  {
    self.x = self.x + 1;
  }
};

void SomeClass::combineWithX(int y)
{
    Private::someHelper(*this);
    x += y;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
balki
  • 26,394
  • 30
  • 105
  • 151
  • 2
    Doesn't `Private` need to be declared as a friend in order for it to access private members of `SomeClass`? `friend struct Private`. I just checked if your code compiles and it does, but I don't understand why. A normal nested class/struct doesn't get any special permissions of its enclosing class. – GameSalutes Dec 31 '18 at 23:19
  • @GameSalutes I don't have the std reference, but this has always worked without any warnings. So I guess, nested struct does get full access to the enclosing struct as if it is part of it without having to mark it as `friend` – balki Dec 31 '18 at 23:35
  • 6
    I see I was mixing up my scoping rules. Turns out a nested class has same access level restrictions as other data members defined so it can access the private members of the enclosing class. What I was thinking of was the converse that the enclosing class cannot access the nested class internals. This is guaranteed by standard (cpp17 n4659) - 12.2.5 covering nested class declarations both the forward declaration and the access allowances. A great forum post: http://www.cplusplus.com/forum/general/181016/ Going to make use of this as it better encapsulates private member functions – GameSalutes Jan 02 '19 at 07:22
  • So we don't need to declare private functions in the header file but we need to declare `Private`... doesn't it end up being the same? – devoured elysium Nov 21 '21 at 17:32
  • 1
    @devouredelysium The main advantage is you don't have to change header file when you want to add/remove/modify private functions and so no need to recompile all the files that includes this header. – balki Dec 08 '21 at 15:28
  • @devouredelysium, in the example it would seem like there isn't much benefit, but imagine having 5 or more private methods - you still only need to write `struct Private` once. You can add/minus methods as you please - change the interface or behaviour of those methods without triggering a recompile - and it helps to keep the data and the public methods nice and clear in the header. – Elliott Jun 05 '22 at 07:06
  • Does using private helper functions cause some L1/L2 cache misses compared to inlining them in the function that uses them? They will probably be allocated not close to each other, right? So there might be some performance hit there, right? – Bawenang Rukmoko Pardian Putra Mar 06 '23 at 01:26
20

I agree that it is a problem that implementation details need to be exposed in a header file; it interferes with separation of interface and implementation.

Moving private helper functions to be free functions in the .cpp file (I presume that is what you meant by "static") won't work if those functions need to access private member variables.

You may be interested to look at the pImpl idiom (more)

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
  • "won't work if those functions need to access private member variables" that's unfortunate. Languages like Go make private variables module-based rather than class-based, makes much more sense IMO. And private helper functions would be possible in C++ if this was also the case. – David Callanan Aug 20 '21 at 11:31
1

Sometimes I see people using two different headers, a public header and a private one, like this:

// SomeClass.h - - - -   

SomeClass 
{
public:
   // public functions    
}

// SomeClass_.h - - - -

SomeClass
{
public:
    // public functions
private:
    // private functions
}

// SomeClass.cpp - - - -

#include "SomeClass_.h"

// SomeClass implementation

This has the advantage of a public header that isn't cluttered with private function declarations, but it requires the developer to maintain 3 copies of each function signature.

Header files have long been recognized by many as being problematic in general, not only because of the added maintenance overhead associated with them, but also because of the effect they have on the build environment. It's easy to write code or set build settings that cause a cascade of system or external headers to fail to compile, resulting in a large number of potentially obscure errors. C++20 Modules are an alternative that may be worth exploring.

Shavais
  • 2,476
  • 1
  • 27
  • 25
  • 1
    Wouldn't this change the size of the class? – Sebastian Jun 06 '22 at 11:28
  • @Sebastian, I think `Shavais` means that we put the private _methods_ in the ifdef block, not any data members. 'Still not sure if it'd invoke undefined behaviour or not - from a glance I can't see what the problem would be if there were no public methods after the private ones. – Elliott Jun 07 '22 at 17:11
  • @Elliott One could statically inherit and if the two classes have the same size, they are layout compatible? To avoid UB? – Sebastian Jun 07 '22 at 19:16
  • I removed the #ifdef block as that entirely misses the point of decluttering the header file. I don't think data members are typically changed between public and private headers, as that would alter sizeof's report, as you say. It's too bad to see this voted down so much, it seems like a perfectly good answer; public and private headers certainly seem to be fairly commonly encountered. And I can't be the only around who thinks headers are problematic, since C++20 devoted an entire new feature (modules) to trying to alleviate the problems with headers. – Shavais Jun 08 '22 at 20:35