Reputation: 3537
Consider this header file:
#ifndef __FOLDER_H__
#define __FOLDER_H__
#include <boost/filesystem.hpp>
class Folder
{
public:
Folder(char* arg);
private:
std::vector<boost::filesystem::path> files;
};
#endif
Everybody including Folder.h
will also include boost/filesystem.hpp
. However, there are no boost/filesystem types in the public interface of Folder
. boost/filesystem.hpp
kind of leaks out of Folder.h
for the technical reason of declaring a private variable.
I would like to avoid this. Would it be best to declare private variables in the implementation file Folder.cc
? Is there some syntax to declare a block of private variables in the implementation file?
Upvotes: 3
Views: 899
Reputation: 25388
At the cost of one level of indirection, you could consider doing something like this:
#ifndef FOLDER_H
#define FOLDER_H
#include <memory>
struct FolderPrivateVars;
class Folder
{
public:
Folder(char* arg);
private:
std::unique_ptr <FolderPrivateVars> private_vars;
};
#endif
And then in folder.cc
#include <boost/filesystem.hpp>
struct FolderPrivateVars
{
std::vector<boost::filesystem::path> files;
};
Folder::Folder(char* arg) : private_vars (std::make_unique <FolderPrivateVars> ())
{
...
}
Note that this approach hides all of Folder
s private variables from prying eyes, which would (for example) mean that modules using it would not need to be recompiled if these change. It might, however, have implications if you want to inherit from Folder
.
Upvotes: 1
Reputation: 1924
There are quite a few idioms to hide the implementation details of a given class. Two of the ones I tend to use are PIMPL and interfaces.
PIMPL
PIMPL is a paradigm where you define a private structure with no definition in the header file, and all of your private implementation details are stored in this private structure. You then reference that structure with a pointer to the implementation, traditionally called pImpl
(hence the name).
With the PIMPL idiom, Folder.h
becomes this:
//this replaces the include guards and is available in almost all modern compilers.
#pragma once
class Folder
{
public:
Folder(char* arg);
private:
struct FolderImpl* pImpl;
};
And in Folder.cc
, you can define FolderImpl
as follows:
#include <vector>
#include <boost/filesystem.hpp>
struct FolderImpl
{
std::vector<boost::filesystem::path> files;
}
From there, any operations that work with the files
member reference it by pImpl->files
.
Interfaces
Interfaces are actually something I "stole" from Microsoft COM. The basic idea is you declare an abstract class, one without any member variables, and inherit from this class in a private header file compiled into your library.
In the Interface idiom, Folder.h
becomes this:
class Folder
{
public:
virtual bool DoesFileExist(char* file) = 0;
virtual File* OpenFile(char* file) = 0;
...
static Folder* Create(char* arg);
};
Folder.cc
looks like this:
#include "Folder.h"
#include "FolderImpl.h"
Folder* Folder::Create(char* arg)
{
return new FolderImpl(arg);
}
And FolderImpl.h
is:
#include "Folder.h"
#include <vector>
#include <boost/filesystem.hpp>
class FolderImpl : public Folder
{
public:
FolderImpl(char* arg);
bool DoesFileExist(char* file) override;
File* OpenFile(char* file) override;
...
private:
std::vector<boost::filesystem::path> files;
};
Upvotes: 4