More C++ Idioms/Inner Class

Inner Class

edit

Intent

edit
  • Implementing multiple interfaces without multiple inheritance and yet provide natural looking up-casting.
  • Provide multiple implementations of the same interface in a single abstraction.

Also Known As

edit

Motivation

edit

Signature of a virtual function in two independent interfaces provided by two independent class libraries may collide. It is a problem especially when a single class has to implement both the colliding functions in different ways depending upon the interface you consider. For example,

class Base1 /// Provided by Moon
{
  public:
      virtual int open (int) = 0;
      /* virtual */ ~Base1() {}  // No polymorphic deletion allowed
};

class Base2 /// Provided by Jupitor
{
  public:
      virtual int open (int) = 0;
      /* virtual */ ~Base2() {}  // No polymorphic deletion allowed
};

class Derived : public Base1, public Base2
{
  public:
    virtual int open (int i)
    {
      // Call from which base class?
      return 0;
    }
    /* virtual */ ~Derived () {}
};

The inner class idiom can help solve this problem.

Solution and Sample Code

edit

Leaving the interface classes, Base1 and Base2 unchanged we can implement the Derived class as follows.

#include <iostream>
class Base1  /// Provided by Moon
{
 public:
  virtual int open() = 0;
  /* virtual */ ~Base1() {}  // No polymorphic deletion allowed
};

class Base2  /// Provided by Jupitor
{
 public:
  virtual int open() = 0;
  /* virtual */ ~Base2() {}  // No polymorphic deletion allowed
};

class Derived  // Note no inheritance
{
  class Base1_Impl;
  friend class Base1_Impl;
  class Base1_Impl : public Base1  // Note public inheritance
  {
   public:
    Base1_Impl(Derived* p) : parent_(p) {}
    int open() override { return parent_->base1_open(); }

   private:
    Derived* parent_;
  } base1_obj;  // Note member object here.

  class Base2_Impl;
  friend class Base2_Impl;
  class Base2_Impl : public Base2  // Note public inheritance
  {
   public:
    Base2_Impl(Derived* p) : parent_(p) {}
    int open() override { return parent_->base2_open(); }

   private:
    Derived* parent_;
  } base2_obj;  // Note member object here

  int base1_open() { return 111; }  /// implement
  int base2_open() { return 222; }  /// implement

 public:

  Derived() : base1_obj(this), base2_obj(this) {}
  Derived(Derived const&) : base1_obj(this), base2_obj(this) {}
  Derived(Derived&&) : base1_obj(this), base2_obj(this) {}
  Derived& operator=(Derived const&) { return *this; }
  Derived& operator=(Derived&&) { return *this; }

  operator Base1&() { return base1_obj; }  /// convert to Base1&
  operator Base2&() { return base2_obj; }  /// convert to Base2&
};                                         /// class Derived

int base1_open(Base1& b1) { return b1.open(); }

int base2_open(Base2& b2) { return b2.open(); }

int main(void) {
  Derived d;
  std::cout << base1_open(d) << std::endl;  // Like upcasting in inheritance.
  std::cout << base2_open(d) << std::endl;  // Like upcasting in inheritance.
}

Note the use of conversion operators in class Derived. (Derived class is really not a derived class!) The conversion operators allow conversion of Derived to Base1 even though they don't share inheritance relationship themselves! Use of member objects base1_obj and base2_obj eliminates the concern of object lifetime. Lifetime of member objects is same as that of the Derived object.

Known Uses

edit
edit

References

edit

Thinking in C++ Vol 2 - Practical Programming --- by Bruce Eckel.