user664303
user664303

Reputation: 2063

Avoiding declaring private functions in class header files (C++)

(In C++) I have a class whose structure is declared in a header file. That header file is included in lots of source files, such that when I edit it I need to recompile lots of files.

The class has a set of private functions which are only called in one source file. Currently they are declared in the class structure in the header file. When I add a new function of this type, or edit the arguments, it therefore causes recompilation of lots of files. I would like to declare the functions somewhere else, such that only the file that defines and calls them is recompiled (to save time). They still need to be able to access the internal class variables, though.

How can I achieve this?

Upvotes: 23

Views: 23626

Answers (5)

UPO33
UPO33

Reputation: 1

Here is a clean solution (no dynamic allocation or pointer redirect). keep variables and public functions in SomeClass, and write the implementations in SomeClass_Impl. those Impl functions can be moved to source file too (incase you like a totally clean header file).

.h

#pragma once

//forward declare the private 
struct SomeClass_Impl;

//public struct with public functions
struct SomeClass
{
    float Values[4] = {};

    SomeClass_Impl* Impl() { return (SomeClass_Impl*)this; }
    const SomeClass_Impl* Impl() const { return (const SomeClass_Impl*)this; }

    void Print() const;
};

.cpp

#include "SomeClass.h"
#include <iostream>

//private implementation, should only contains functions
struct SomeClass_Impl : SomeClass
{
    float GetSum() const
    {
        float Sum = 0;
        for(float V : Values)
            Sum += V;

        return Sum;
    }
    float GetMinValue() const
    {
        float MinVal = Values[0];
        for (int i = 1; i < 4; i++)
            MinVal = std::min(MinVal, Values[i]);

        return MinVal;
    }
};

static_assert(sizeof(SomeClass) == sizeof(SomeClass_Impl), "_Impl should not have any member variables.");


void SomeClass::Print() const
{
    //'this' can be casted to SomeClass_Impl without any cost   
    std::cout << "Sum:" << Impl()->GetSum() << " MinValue:" << Impl()->GetMinValue() << '\n';
}

Usage:

SomeClass A;
A.Values[0] = 1;
A.Values[1] = 2;
A.Values[2] = 3;
A.Values[3] = 4;

A.Print();

Upvotes: 0

Neil Kirk
Neil Kirk

Reputation: 21773

Create an abstract base class which contains only the public functions and reference this in your headers. Create your real class as an implementation somewhere else. Only source files which need to create your class need to see the implementation class header.

Upvotes: 1

user664303
user664303

Reputation: 2063

There is no way to declare member functions of a class outside the main class declaration. So, if you want to declare, outside of the class in question, functions that can access member variables of a particular instance of the class, then I see no alternative but to pass that instance to the function. Furthermore, if you want the functions to be able to access the private and protected variables you will need to put them in a new class and make the original class a friend of that. E.g.

header.h:

class FooImpl;

class Foo {
public:
   int bar();
   friend class FooImpl;
private:
   int var;
}

impl.cpp:

#include "header.h"

class FooImpl {
public:
   int bar(Foo &);
}

int FooImpl::bar(Foo &foo) {
return foo.var;
}

int Foo::bar() {
return FooImpl::bar(*this);
}

Upvotes: 6

Erik
Erik

Reputation: 91270

Use the pImpl idiom - Your visible class keeps a pointer to the real class and forwards calls to public member functions.

EDIT: In response to comments

// Foo.h:

class FooImpl; // Do *not* include FooImpl.h
class Foo {
public:
  Foo();
  ~Foo();
  //.. also need copy ctor and op=
  int bar();
private:
  FooImpl * Impl;
};

// FooImpl.h:

class FooImpl {
public:
  int bar() { return Bar; }
private:
  int Bar;
};

// Foo.cpp:

#include "FooImpl.h"

Foo::Foo() { Impl = new FooImpl(); }
Foo::~Foo() { delete Impl; }
int Foo::bar() { return Impl->bar(); }

Keep the actual implementation of your class in FooImpl - Foo should have copies of the public members of FooImpl and simply forward calls to these. All users will include only "Foo.h" - you can change all the private details of FooImpl without the users of Foo seeing any changes.

Upvotes: 14

Nikolai Fetissov
Nikolai Fetissov

Reputation: 84169

Are you looking for Compiler Firewall, a.k.a. PIMPL?

Upvotes: 2

Related Questions