C++ Programming/Idioms

From Wikibooks, the open-content textbooks collection

Jump to: navigation, search

Contents

[edit] Idioms

[edit] Ensuring objects of a class are never copied

This is required e.g. to prevent memory-related problems that would result in case the default copy-constructor or the default assignment operator is unintentionally applied to a class C which uses dynamically allocated memory, where a copy-constructor and an assignment operator are probably an overkill as they won't be used frequently.

Just declare the copy-constructor and assignment operator, and make them private. Do not define them. As they are not protected or public, they are inaccessible outside the class. Using them within the class would give a linker error since they are not defined.

class C
{
    ...

    private:
        // Not defined anywhere
        C (const C&);
        C& operator= (const C&);
}

Remember that if C uses dynamically allocated memory, you must define the destructor ~C ().

Alternatively, when using the Boost library, the boost::noncopyable base class can be used:

class C : private boost::noncopyable
{
     ...
}

It's also trivial to implement an equivalent to boost::noncopyable in your own code, and use that. Many developers feel that using a base class like this is clearer than the explicit private member declarations; its name tells its purpose.

[edit] Handling self-assignment in an assignment operator

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;
}

[edit] Named constructor

To be able to create an object of a class C without using its constructor. This might be used for the following:

  1. To circumvent the restriction that constructors can be overloaded only if their signatures differ.
  2. Making the class non-inheritable by making the constructors private.

Just declare a static method that uses a private constructor to create the object and returns it (It could also return a pointer or a reference but this complication seems useless). Here's an example for a class that stores a temperature that can be specified in any of the different temperature scales.

class Temperature
{
    public:
        static Temperature fahrenheit (double f);
        static Temperature celsius (double c);
        static Temperature kelvin (double k);
    private:
        Temperature (double temp);
        double _temp;
};

Temperature::Temperature (double temp) _temp (temp) {}

Temperature Temperature::fahrenheit (double f)
{
    return Temperature ((f + 459.67) / 1.8);
}

Temperature Temperature::celsius (double c)
{
    return Temperature (c + 273.15);
}

Temperature Temperature::kelvin (double k)
{
    return Temperature (k);
}

[edit] Pointer To Implementation (pImpl)

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 realy 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.

class Book
{
public:
  void print();
private:
  class BookImpl;
  BookImpl* m_p;
}

and in a separate 'internal' header

class 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();
}
void Book::print()
{
  m_p->print();
}

You could also use std::auto_ptr<BookImpl> or equivalent to manage the internal pointer.

[edit] Further reading