Ari Seyhun
Ari Seyhun

Reputation: 12531

Do I need to define all private functions and variables in class header C++

In a C++ class, should I be placing my private functions and variables in the private section of the class header definition, or in the class source file and why?

For example:

// Header
class MyClass {
public:
    void doSomething();
private:
    int a = 0;
}

// Source
void MyClass::doSomething()
{
    // Do something with `a`
}

or

// Header
class MyClass {
public:
    void doSomething();
}

// Source
int a = 0;

void MyClass::doSomething()
{
    // Do something with `a`
}

I've always thought, when programming it's best to make the scope of a function/variable as small as possible. So shouldn't restricting the scope of the var a to the scope of the source file be best?

Upvotes: 3

Views: 775

Answers (2)

Oliv
Oliv

Reputation: 18051

You could use the pimpl idiom... Alternatively, you could use this variant of the pimpl idiom, where the memory of the implementation is directly provided by the interface class:

In file MyClass.hpp :

class MyClass{
  private:
    std::byte buffer[N];
  public:
    MyClass();
    void public_method();
    ~MyClass();
  };

In class MyClass.cpp:

#include "MyClass.hpp"
namespace{
  struct MyClassImpl{
     private:
       int val=0;
     public:
       void secret_method(){/*...*/}
     };
  inline const MyClassImpl& 
  get(const std::byte* buffer){
    //In theory, in C++17 you should use std::launder here to be standard compliant
    //in practice, compilers generate the expected code without std::launder
    //and with std::launder they generate horrible and inefficient code!
    return *reinterpret_cast<const MyClassImpl*>(buffer);
    }
  inline MyClassImpl& 
  get(const std::byte* buffer){
    //idem here to be c++17 standard compliant launder is necessary
    //in practice, it would be a mistake.
    return *reinterpret_cast<MyClassImpl*>(buffer);
    }
 }

 MyClass::MyClass(){
   new(buffer) MyClassImpl{};
   }
 MyClass::~MyClass(){
   get(buffer).~MyClassImpl();
   }
 void
 MyClass::public_method(){
    /*....*/
    get(buffer).secret_method();
    /*....*/
    }

Compared to classical pimpl idiom:

  • Pros: less memory access, no memory allocation on the heap, more efficient

  • Cons: error prone, the size of the implementation "leak" in the interface.

Upvotes: 0

john
john

Reputation: 87959

They are not equivalent. First example

// Header
class MyClass {
public:
    void doSomething();
private:
    int a = 0;
}

// Source
void MyClass::doSomething()
{
    ++a;
    cout << a << endl;
}

int main()
{
    MyClass x, y;
    x.doSomething();
    y.doSomething()
}

Output

1
1

Second example

// Header
class MyClass {
public:
    void doSomething();
}

int a = 0;

// Source
void MyClass::doSomething()
{
    ++a;
    cout << a << endl;
}

int main()
{
    MyClass x, y;
    x.doSomething();
    y.doSomething()
}

Output

1
2

In the first example a is a class variable so x and y have their own copy of a. In the second example there is only one global variable a so the output is different.

Upvotes: 2

Related Questions