Reputation: 12531
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
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
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