delegatingFrequently one wishes to define a class with a collection of member functions whose implementations are borrowed from an existing class. The most straightforward way to achieve this implementation reuse in C++ is through public inheritance:
class PreExisting { public: void foo() { ... } void bar() { ... } void baz() { ... } }; class NewClass : public PreExisting { // Implementation of foo, bar and baz inherited from PreExisting ... };
Sometimes, however, public inheritance may not be adequate, for several reasons:
public or protected constructors.
Even when inheritance is possible, however, it may not be dersirable. For instance:
An early version of C++ contained a delegation facility to address this problem. The following example is adapted from [Stroustrup], p. 272:
class PreExisting { /* As above */ }; class NewClass : *p { public: NewClass() : p(new PreExisting) { } private: PreExisting* p; }; void f(NewClass* q) { q->bar(); // meaning q->p->bar() }
Here, calls of member functions of B through an instance of C are automatically forwarded to p, which is known as the delegate. Unfortunately, this language construct was found to be a source of bugs and confusion an was removed. See [Stroustrup], pp 272-73.
In the current language, one solution is to redeclare the desired functions in the new class and to implement them by forwarding to an instance of the pre-existing class. For example:
class PreExisting { /* As above */ }; class NewClass { public: NewClass() : p(new PreExisting) { } void foo() { p->foo(); } void bar() { p->bar(); } void baz() { p->baz(); } private: PreExisting* p; };
delegating
Boost.Interfaces provides an alternative solution: the class template delegating can be used to automatically forward interface function calls to an object specified at runtime.
For example, using delegating, we can write:
class PreExisting { /* As above */ }; // Pseudocode interface IPreExisting { void foo(); void bar(); void baz(); }; class NewClass : public delegating<IPreExisting> { public: NewClass() : p(new PreExisting) { delegating<IPreExisting>::set(p); } // Inherits implementation of IPreExisting private: PreExisting* p; };
Note that this example is very similar to the example from Stroustrup. The differences are:
set rather than in the base class list.
Furthermore, the delegate is not restricted to having a predetermined type. In the above example, we could add member pointers to various types implementing IPreExisting and switch between them at runtime. The template delegating therefore provides a form of dynamic inheritance.
If the interface consisting of the functions to be forwarded is not needed for any other purpose, the solution using delegating may involve the same amount of work as the immediately preceding solution. But if the interface already exists, or if we need to use objects of unrelated types as delegates, using delegating can be much simpler.
In the future, Boost.Interfaces may provide support for COM-style aggregation, a form of delegation in which the delegate has direct access to the delegating object. See Future Directions: Aggregation.
Revised 13 Jan, 2005
© Copyright Jonathan Turkanis, 2005. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Sha'arei Tefila, an Orthodox Shul (Synagogue) in Salt Lake City, Utah Chabad Lubavitch of Utah