A subclass that inherits from two different reference-counted objects
inherits a single x::obj superclass, since
it's virtually inherited:
class widgetObj : virtual public x::obj { // .... }; class containerObj : virtual public x::obj { // .... }; class boxObj : public widgetObj, public containerObj { // .... }; typedef x::ref<widgetObj> widget; typedef x::ref<containerObj> container; typedef x::ref<boxObj> box; typedef x::ptr<widgetObj> widgetptr; typedef x::ptr<containerObj> containerptr; typedef x::ptr<boxObj> boxptr;
This is the reason why x::obj should always
be inherited virtually.
An x::ref or an x::ptr
to a subclass may be converted to an x::ref or an x::ptr to its
superclass. The opposite is also true: an x::ref or an x::ptr to a superclass
may be converted to an x::ref or an x::ptr to a subclass, but only if the
referenced object is actually an instance of the subclass, of course:
widget wRef; container cRef; // ... cRef=wRef; // ... wRef=cRef;
A runtime exception gets thrown if the
pointer or the reference cannot be converted.
Use dynamic_cast<> if it's unknown whether
the superclass reference refers to an instance of a subclass:
if (!wRef.null()) { widgetObj *wptr(&*wRef); if (dynamic_cast<boxObj *>(wptr)) { container cRef(wRef); // .... } }
Conversion between an x::ref or an x::ptr (source) to a different x::ref or an x::ptr
(destination) proceeds as follows. If the destination is a superclass
of the source, the conversion uses a static_cast.
Otherwise the conversion uses a dynamic_cast and
thrown an exception if the conversion fails.
A destination class can avoid the overhead of a
dynamic_cast by defining a
cast_from() class method that returns
a plain pointer to the destination class with a pointer to a plain
source class as a parameter:
class aObj : virtual public x::obj { public: virtual D *getd() { return nullptr; } }; class bObj : public aObj { }; class dObj : public bObj { public: static dObj *cast_from(aObj *a) { return a->getd(); } dObj *getd() override { return this; } }; // ... x::ref<aObj> return_a(); x::ref<dObj> d=return_a();
Normally an attempt to convert an x::ref of aObj
to dObj gets carried out via a
dynamic_cast. Here, dObj
defines a cast_from static class method that
takes a pointer to an aObj and returns a
DObj. This results in the
dynamic_cast getting replaced by the
cast_from() call.
The intended use is to have cast_from invoke
a virtual method in the source class. The base implementation in the
source class returns a nullptr by default, with the
destination class overriding and simply returning
this. The end result is a good enough facsimile for a
dynamic_cast that it ends up being used as a
pinch-hitter.
This design pattern replaces an otherwise expensive
dynamic_cast with a much cheaper virtual function
dispatch. The functionality of a full dynamic_cast
isn't matched identically, but this should suffice for most common
use cases.
Implicit conversion from an x::ref or an x::ptr to a different x::ref or an x::ptr
means that a function with an x::ref or an x::ptr parameter
participates in overload resolution if the corresponding parameter
is any other x::ref or an x::ptr. Sometimes it is desirable to turn off
overload resolution. This is done by using an
x::explicit_refptr
parameter:
#include <x/refptr_traits.H> typedef x::ref<class1Obj> class1; typedef x::ref<class2Obj> class2; void foo(const x::explicit_refptr<class1> &arg) { const class1 &rarg=arg; // ... } void foo(const x::explicit_refptr<class2> &arg) { const class2 &rarg=arg; // ... }
The first foo() participates in overload resolution
only if the corresponding parameter is an x::ref or an x::ptr of a
class1Obj, and the second one only
for a parameter of an x::ref or an x::ptr of a
class2Obj.
An explicit_ref of a x::ref still participates
in overload resultion of a x::ptr to the same underlying object.
An
x::explicit_arg
only participates in an overload resolution for the same exact
x::ref or an x::ptr:
#include <x/refptr_traits.H> typedef x::ref<class1Obj> class1; typedef x::ref<class1Obj> class1ptr; void foo(const x::explicit_arg<class1> &arg) { const class1 &rarg=arg; // ... } void foo(const x::explicit_arg<class1ptr> &arg) { if (arg) { const class2 &rarg=arg; // ... } }