C++ Programming/Idioms
Idioms
[edit | edit source]Handling self-assignment in an assignment operator
[edit | edit source]T::operator= that handles the case where the LHS and the RHS refer to the same object.
T& operator= (const T& that) { if (this == &that) return *this; // handle assignment here return *this; }
Notes:
- Keep in mind the differences between identity (the LHS and the RHS are the same object) and equality (the LHS and the RHS have the same value). T::operator= has to protect itself against identity since then, the code for assigning can conveniently and safely assume that the LHS and the RHS refer to different objects.
- There are other techniques that are superior but not applicable in all situations. For example, if all the members of the class T (say, mem1, mem2, ..., memN) provide a swap function, one could use the following code instead:
T& operator= (T that) { // that is constructed by the copy constructor mem1.swap (that.mem1); mem2.swap (that.mem2); ... memN.swap (that.memN); // now what were originally this->mem1, this->mem2, etc. get // destroyed when that gets destroyed, and that.mem1, etc. are // retained in *this return *this; }
Pointer To Implementation (pImpl)
[edit | edit source]The "pointer to implementation" (pImpl) idiom, also called the "opaque pointer" idiom, is a method of providing data and thus further implementation abstraction for Classes.
In C++ you have to declare member variables within the class definition which is then public and that this is necessary so that an appropriate memory space is allocated means that abstraction of implementation is not possible in "all" classes.
However, at the cost of an extra pointer dereference and function call, you can have this level of abstraction through the Pointer to Implementation.
class Book { public: void print(); private: std::string m_Contents; }
So somebody who works with the Book class only really needs to know about print(), but what happens if you wish to add more detail to your book class.
class Book { public: void print(); private: std::string m_Contents; std::string m_Title; }
Now all the users of book have to recompile because the object they know about got bigger, yet they still only call print().
pImpl would implement the following pattern so that this would not be a problem.
/* public.h */ class Book { public: Book(); ~Book(); void print(); private: class BookImpl; BookImpl* const m_p; }
and in a separate 'internal' header
/* private.h */ #include "public.h" #include <iostream> class Book::BookImpl { public: void print(); private: std::string m_Contents; std::string m_Title; }
The body of the Book class then would look something like
Book::Book(): m_p(new BookImpl()) { } Book::~Book() { delete m_p; } void Book::print() { m_p->print(); } /* then BookImpl functions */ void Book::BookImpl::print() { std::cout << "print from BookImpl" << std::endl; }
then invoked from a main function:
int main() { Book b; b.print(); }
You could also use std::unique_ptr<BookImpl> or equivalent to manage the internal pointer.