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; // ... } }