More C++ Idioms/Inner Class
Inner Class
[edit | edit source]Intent
[edit | edit source]- 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 | edit source]Motivation
[edit | edit source]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 | edit source]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 source]Related Idioms
[edit | edit source]References
[edit | edit source]Thinking in C++ Vol 2 - Practical Programming --- by Bruce Eckel.