The motivation for doing something like this would be to reduce compilation times by minimising include trees (in this case "file.h" would be pulled in from lots of places, and it will also in turn, pull in lots of other includes). It also improves testability/mockability. Due to the additional complexity, I would only be inclined to use this for "domain level" objects which are accessed via an API (i.e. do not use this where there is sparse usage).
You can effect this by abstracting foo
such that only its API is exposed in the header file. You then implement a subclass which pulls in the includes - but they are only pulled in once.
For more detail I would consult "Large-Scale C++ Software Design" from John Lakos. Allegedly the older version is better: https://www.amazon.co.uk/Large-Scale-C-Software-Design-APC/dp/0201633620
He has also spoken at various conferences about the new "module" feature: e.g. https://www.youtube.com/watch?v=EglLjioQ9x0
As a bare minimum example you would have the following. Obviously it would be better to return a unique_ptr<foo>
.
file.h
#ifndef _FILE_H_672b47fa_7d02_458a_bd42_4774a3765a1b
#define _FILE_H_672b47fa_7d02_458a_bd42_4774a3765a1b
struct foo {
virtual ~foo() = 0;
virtual void bar() = 0;
};
extern foo* fooFactory();
#endif
file.cpp
#include "file.h"
#include <...etc...>
...etc...
class fooImpl : public foo {
private:
Vector vector; // std::vector from <vector>
Map map; // std::map from <map>
...etc...
public:
~fooImpl();
void bar() override;
};
void fooImpl::bar() {
// Do something with private variables...
}
fooImpl::~fooImpl() {
// cleanup...
}
foo* fooFactory() {
return new fooImpl();
}
main.cpp
#include "file.h"
int main() {
foo* = fooFactory();
// Do something...
delete foo;
}
An alternative approach, as others have pointed out, would be to encapsulate a separate struct containing the "data" via a member pointer. It's a valid approach although it could make const-correctness harder to enforce. It's also related to the flyweight pattern which is useful when the data needs to be passed around different objects, see https://refactoring.guru/design-patterns/flyweight