The conventional approach to dynamic polymorphism in C++ is to use abstract base classes.
The Xc++ language provides an alternative way to achieve dynamic polymorphism.
An interface is analogous to an abstract base class with pure virtual functions. However it is more flexible, less type intrusive and typically provides better performance. There are numerous advantages of interfaces over abstract base classes
The following defines an interface named IShape
:
$interface IShape
{
float64 GetArea() const;
float64 GetPerim() const;
};
Note the clean syntax given that all methods are implicitly public pure virtual functions. These
particular methods are declared const
because they shouldn't mutate the object implementing the interface.
For any interface T
a ptr<T>
is a simple, lightweight data type that supports inexpensive
comparisons, copy and assignment.
A ptr<IShape>
represents a pointer to an object that implements the IShape
interface.
A ptr<const IShape>
only allows the const
methods on the interface to be called.
Note that ptr<const IShape>
associates constness with the object rather than the pointer
to the object.
Given only the above definition of interface IShape
it is possible to write code that calls
the IShape
methods such as GetArea
on a ptr<IShape>
, regardless of what kind of object
implements the interface, i.e. with dynamic polymorphismn. For example:
void WriteArea(ptr<const IShape> s)
{
std::cout << "Area = " << s->GetArea() << std::endl;
}
Consider the following class written in straight C++:
class Circle
{
public:
Circle() : r(3.0) {}
float64 GetArea() const {return 3.14*r*r;}
float64 GetPerim() const {return 2*3.14*r;}
private:
float64 r;
};
Notice that Circle
doesn’t inherit from any base classes, it is written without
any dependency on interface IShape
.
Circle
class is probably only 8 bytes (the size of its float64
member r
).
There are no virtual methods so there is no vtable pointer.
Nevertheless we consider Circle
to have implemented the IShape
interface,
because it has implemented all the IShape
methods with the required signature and
semantics.
The remarkable idea is that a dynamic polymorphic capability can be retrospectively applied to a class.
The coercion into an interface pointer is very simple:
Circle c;
ptr<IShape> s = &c; // Coercion to IShape
WriteArea(s);
or just
Circle c;
WriteArea(&c);
If Circle
hadn’t implemented the methods in the IShape
interface
then the attempted coercion would have resulted in a compile time error.
Interface pointers are implemented in terms of a pair of pointers.
Xcpp interfaces provide the means to "wire up" software components more efficiently than with conventional C++ or with languages like C# and Java.
The CD player example shows how the play and stop
buttons are "wired up" to call the Play()
and Stop()
methods without heap allocations.
Multiple inheritance of interfaces is supported. This allows one to speak of the subinterfaces and superinterfaces of a given interface.
There is a special interface called IObject which supports
When a $interface is reflected an instance of a ReflectedInterface is registered in the ReflectedInterface registry.
A unique_interface_ptr is a smart pointer which represents
exclusive ownership of an object that implements IObject
and will call the
Destroy
method when it destructs.