Why use interface instead of pimpl
Submitted by st on
The opaque pointer (pimpl) idiom has been inherited from C language where it is used to encapsulate implementation details. However, both old-school and "modern" C++ dispence you from writing some ugly code, and allow to use interfaces with object factories.
The example of implementation with both pimpl and interface approach is as follows.
pimpl.hpp: pimpl implementation header
#include <string> #include <memory> using namespace std; class book { public: book(const string& title); book(const string& title, const string& author_name, const int page_count); // If you use pimpl with unique_ptr, you need to declare a destructor // which should be implemented in '*.cpp' file ~book(); public: // Main interface string title() const; string author_name() const; int page_count() const; private: class book_impl; // Defined and implemented in '*.cpp' file unique_ptr<book::book_impl> m_impl; // Opaque pointer }; using book_ptr = unique_ptr<book>;
pimpl.cpp: implementation
#include "pimpl.hpp" // Here we include some headers i.e. of third-party libraries // which are required for implementation only // Once being published, the 'pimpl.hpp' does not depend on them class book::book_impl { public: book_impl(const string& name, const string& author_name, const int page_count) : m_title(name), m_author_name(author_name), m_page_count(page_count) {} // Main interface implementation string title() const { return m_title; } string author_name() const { return m_author_name; } int page_count() const { return m_page_count; } private: string m_title; string m_author_name; int m_page_count; }; // Now we need to code the redirections of ALL calls book::book(const string& title) : book(title, "Unknown", 1) {} book::book(const string& title, const string& author_name, const int page_count) : m_impl(make_unique<book_impl>(title, author_name, page_count)) {} book::~book() {} string book::title() const { return m_impl->title(); } string book::author_name() const { return m_impl->author_name(); } int book::page_count() const { return m_impl->page_count(); }
no_pimpl.hpp: interface and factory header
#include <string> #include <memory> using namespace std; class book_intf { public: // Main interface virtual string title() const = 0; virtual string author_name() const = 0; virtual int page_count() const = 0; public: virtual ~book_intf() {} }; using book_intf_ptr = unique_ptr<book_intf>; class book_factory { public: static book_intf_ptr create(const string& title); static book_intf_ptr create(const string& title, const string& author_name, const int page_count); };
no_pimpl.cpp: implementation
#include "no_pimpl.hpp" class book_impl : public book_intf { public: book_impl(const string& title) : book_impl(title, "Unknown", 1) {} book_impl(const string& title, const string& author_name, const int page_count) : m_title(title), m_author_name(author_name), m_page_count(page_count) {} public: // Main interface implementation string title() const override { return m_title; } string author_name() const override { return m_author_name; } int page_count() const override { return m_page_count; } private: string m_title; string m_author_name; int m_page_count; }; // Book factories book_intf_ptr book_factory::create(const string& title) { return move(make_unique<book_impl>(title)); } book_intf_ptr book_factory::create(const string& title, const string& author_name, const int page_count) { return move(make_unique<book_impl>(title, author_name, page_count)); }
As you can see, redirection of calls is not required when using interfaces, so you do not need to define functions signatures twice with copy-past coding.
Running both:
#include "pimpl.hpp" #include "no_pimpl.hpp" #include <memory> #include <iostream> int main(int, char**) { auto book1 = std::make_unique<book>("Modern old-school C++", "Gang of thousands", 255); cout << "Title: " << book1->title() << "\nAuthor: " << book1->author_name() << "\nPages: " << book1->page_count() << endl; book_intf_ptr book2 = book_factory::create("Modern old-school C++", "Gang of thousands", 255); cout << "Title: " << book2->title() << "\nAuthor: " << book2->author_name() << "\nPages: " << book2->page_count() << endl; return 0; }