x::ref
or an x::ptr
from this
An x::ref
or an x::ptr
can be constructed from a native object pointer.
This is frequently used by class methods to create pointers or
references to
the this
object:
class widgetObj : virtual public x::obj { // ... }; typedef x::ref<widgetObj> widget; // ... void widgetObj::method() { // ... widget myRef(this); // ... }
This allows a class method to create an x::ref
or an x::ptr
to the object
pointed by this
, so that it can be passed along
to other objects or methods, for example. Note, however:
Creating an x::ref
or an x::ptr
from this
in the object's constructor
abort
()s. Consider the following scenario:
create
() creates the object. The new
object's reference count does not get incremented from its initial
value of zero, to one, until the object gets fully constructed.
With the initial reference count still zero, one
the constructors makes
an explicit x::ref
<classname
>(this
)
creating a new x::ref
. This increments the object's reference
count to one.
This x::ref
goes out of scope and gets destroyed, also before
the constructor finishes. This now decrements the reference count
back to zero. When an object's reference count goes to 0,
it gets destroyed. This is now happening before the object
is fully constructed. Even if that, by some miracle
of miracles, doesn't blow up, the remaining constructors continue
to execute, and eventually the original x::ref
attempts to
increment what it thinks is the initial reference count, in the
object that's already destroyed.
This is undefined behavior. The same holds true with
creating an x::ref
to an object its destructor. The
object's reference count is zero, and it's getting destroyed
no matter what. A new x::ref
(or an x::ptr, doesn't matter) to the object does not prevent that
from happening. The end result is an x::ref
or an x::ptr
to a destroyed object.
For this reason, attempting to create an x::ref
or an x::ptr
to
this
in its constructor or destructor immediately
abort
()s, to aid in debugging.
It's true that it's technically possible to have everything be legal
with a x::ref
or an x::ptr
from this
created in its
constructor, as long as the object succesfully finishes its full
construction. However, LibCXX takes the pessimistic view that
this can't be guaranteed (on the assumption that an exception can
get thrown at any time), and prevents this from happening.
Constructing an x::ref
or an x::ptr
from this
in the
constructor is a bad idea.
Technical note:
it's not really true that the a newly-constructed object starts
with a reference count of 0. Internally, LibCXX sets the
initial reference count to -1, and resets it to 0 just before
create
() creates and returns the first
x::ref
or an x::ptr
, which immediately updates it to 1. The reference
count of -1 gets used to detect an attempt to create an x::ref
or an x::ptr
in a constructor (or the destructor).
For this reason, new reference-counted objects must always get
created with create
() in order to
properly initialize the new object's reference count. Manually
instantiating the object with new
, and
assigning it to an x::ref
or an x::ptr
, also aborts the program.
constructor
() is an ordinary class method,
but if it exists
create
()
calls it automatically after constructing a new
object, but before returning the new
x::ref
or the new x::ptr
.
This is a convenient workaround for not being able to
create an x::ref
or an x::ptr
in
a real constructor.
A class with a constructor
() method must
inherit from
x::with_constructorObj
in addition to x::obj
class buttonObj : x::with_constructorObj, virtual public x::obj { public: buttonObj(int width, int height); void constructor(int width, int height); }; auto b=x::ref<buttonObj>::create(100, 200);
create
() forwards its parameters to
the newly-constructed object normally. Afterwards,
create
() calls the
constructor
() method with the same
parameters that were forwarded to create
().
The
constructor
() method must be callable with
the same parameters and should return void
.
When constructor
() gets invoked its object
is fully constructed, for the purpose of the C++ language itself,
and for the purpose of using x::ref
s and x::ptr
s.
The ban on creating x::ref
s and x::ptr
s from
this
in constructors do not apply to the
constructor
() method itseld. It's just
an ordinary class method.
A constructor
() may be viewed as a
“second phase” of constructing an object, and may be
utilized for other reasons besides the ability to create
x::ref
s and x::ptr
s from this. If the class's contract requires
some kind of an initialization method that always gets invoked after
its construction, constructor
() is this
method. This becomes a part of the class's contract.
x::with_constructorObj
should not be
virtually-inherited.
x::ref
or an x::ptr
class buttonObj : virtual public x::obj { // ... void clicked(); }; void buttonObj::clicked() { auto r=x::ref(this); };
A natural deduction guide for an x::ref
or an x::ptr
(also x::const_ref
and
x::const_ptr
too) simplifies creating an x::ref
or an x::ptr
from
this
(or from some other native pointer).
Note that this results in the
default base
typedef
of
x::ptrref_base
, for the constructed
reference or a pointer. However the deduction guide uses the
x::base_type
specialization, if one is
available:
class buttonObj : virtual public x::obj { // ... void clicked(); }; template<> struct x::base_type<buttonObj> { typedef buttonBase type; }; void buttonObj::clicked() { auto r=x::ref(this); };
Because this specialization overrides the second template parameter
to an x::ref
or an x::ptr
, this constructs a
x::ref<buttonObj, buttonBase>
.