Home pimpl in C++
Post
Cancel

pimpl in C++

What is pimpl

In simple, pimpl is a c++ technique that removes implementation details of a class from its object representation by placing them in a seperate class and accessing it via an opaque pointer

What the hell is that?

Let’s start with simple example.

1
2
3
4
5
6
7
8
9
10
11
12
// Student.h
class Student
{
public:
    Student(const std::string& name);
    ~Student();
    void addBook(const std::string& bookName);
    void printAllBooks() const;
private:
    std::string m_name;
    std::vector<std::string> m_books;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Student.cpp
Student::Student(const std::string& name) : m_name(name)
{
}

Student::~Student()
{
    m_name = "";
    m_books.clear();
}

void Student::addBook(const std::string& bookName)
{
    m_books.push_back(bookName);
}

void Student::printAllBooks() const
{
    for (const auto& book : m_books) {
        std::cout << book << "\n";
    }
}

Now, what’s the problem. The problem is

  • All private variables in Student.h can be seen to all the users of this header file
  • When header file is updated, especially when privates variables are added or removed or modified, Then all the source code which includes this header file get recompiled again

How can we avoid this?

This can be avoided using c++ pimpl, What we just need to do is two things,

  • Create a forward declaration of the class with any name (say Impl) in header (Student.h) and add uinque pointer to it as a private member
  • Move all the private variable to Impl class and this class defintion should be present in Student.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
// Student.h
class Student
{
public:
    Student(const std::string& name);
    ~Student();
    void addBook(const std::string& bookName);
    void printAllBooks() const;
private:
    class Impl; //forward declaration
    std::unique_ptr<Impl> m_pImpl = nullptr;
};

Now in above header file we can see that all the private variables are hidden and burried in the source (Student.cpp) file. Only thing which is exposed or seen in the header file is that pointer to impl (Pointer to Implementation).

Let’s us look, how can we implement Impl class

Student.cpp looks like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// Student.cpp
class Student::Impl
{
public:
    Impl(const std::string& name) : m_name(name)
    {

    }

    ~Impl()
    {
        m_name = "";
        m_books.clear();
    }

    void addBook(const std::string& bookName)
    {
        m_books.push_back(bookName);
    }

    void printAllBooks() const
    {
        for (const auto& book : m_books) {
            std::cout << book << "\n";
        }
    }
private:
    std::string m_name;
    std::vector<std::string> m_books;
};

Student::Student(const std::string& name) : m_pImpl(std::make_unique<Impl>(name))
{
}

Student::~Student()
{
}

void Student::addBook(const std::string& bookName)
{
    if (m_pImpl) {
        m_pImpl->addBook(bookName);
    }
}

void Student::printAllBooks() const
{
    if (m_pImpl) {
        m_pImpl->printAllBooks();
    }
}

With this above approach we can avoid the exposion of the private details hidden from the header file and also compilation time can be imporved

What are the downfall of this approach

  • Very first drawback is, as you had seen in the example, an unnecessary wrapper for each of the function we need to write. Once the program grows bigger and bigger, maintaining this functions become headace
  • Simply we have introduced the pointer and allocated the memory in heap, which would have been much more simpler without pimpl

When can we go for pimpl implementation

  • During the development of the security related functionalities, where we want to hide the private variables, which might be a thridy party variables or any security related data structures or variables
  • When we dont want to break the binary interface which is dependent on private variables
This post is licensed under CC BY 4.0 by the author.

Be careful when using const reference with std::thread / std::async in C++. Otherwise you may be in soup if you don't know this ! :)

Simple way to implement Singleton design pattern without using pointers in c++