Let's begin with something basic and conventional
struct Book { Book (string const& title, string const& author); ... string const& title() const; string const& author() const; private: string title_; string author_; int price_; };
Now, due to reasons mentioned in the Introduction, we would like to deploy the Pimpl technique with the shared/pointer properties. In that setting the Book definition might change as follows:
struct Book { Book(string const& title, string const& author); ... string const& title() const; string const& author() const; bool operator== (Book const& other) const { return impl_ == other.impl_; } bool operator!= (Book const& other) const { return !operator==(other); } explicit operator bool () const { return bool(impl_); } bool operator! () const { return !bool(impl_); } private: struct implementation; std::shared_ptr<implementation> impl_; };
Applying the Pimpl idiom is fairly straightforward as std::shared_ptr takes care of much of the implementation-related hassle. The auto-generated special member functions (the destructor, the copy constructor, etc.) suffice and the comparison, Boolean-conversion operators аrе easy.
Still, from design perspectives lumping the application logic with the infrastructure scaffolding is unfortunate. In our (admittedly simple) example more than half of the code is the Pimpl-related implementation detail. For one class in isolation that might not be that big a deal. On a larger scale though, reading and maintaining extra code, making sure nothing is forgotten or misplaced is a tiring and error-prone exercise. The following, therefore, seems like an improvement:
struct Book : boost::impl_ptr<Book>::shared { Book(string const& title, string const& author); string const& title() const; string const& author() const; };
With no loss in functionality it is shorter, application-focused and reasonably self-documented. Importantly, it consists of nothing but pure public interface. It is not even a class as there is nothing to restrict access to.
Note | |
---|---|
With such a clean separation of concerns (public interface and private implementation) the technique is ideal for building large-scale systems and, importantly, managing orderly evolution of those systems. |