user1785730
user1785730

Reputation: 3537

Private variable declaration in implementation file

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

Answers (2)

catnip
catnip

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 Folders 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

Alex
Alex

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

Related Questions