create
() - create reference-counted objects#include <x/ref.H> #include <x/ptr.H> class buttonObj : virtual public x::obj { public: buttonObj(int width, int height); ~buttonObj(); // ... }; typedef x::ref<buttonObj> button; typedef x::ptr<buttonObj> buttonptr; button quit(button::create(100, 100));
An x::ref
and an
x::ptr
defines a member
typedef
named base
. This is a
typedef
to a class with static methods, or other
members that are provided by the x::ref
or the
x::ptr
, to supplement the class
*
and ->
method operators.
An x::ref
's and x::ptr
's create
() is actually
a call to base::objfactory<
(), with
T
>::createT
being the x::ref
or a x::ptr
type that's
calling it, and is the expected return type from
create
().
By default, base
gets typedef-ed to
x::ptrref_base
, which defines the
objfactory
template class parameter with a
static create
() method that constructs a new
object, for the x::ref
or the x::ptr
.
x::ref
's and x::ptr
's create
() method
forwards its arguments to
base::objfactory<
().
The default implementation,
T
>::createx::ptrref_base::objfactory<
(),
constructs the object on the heap with T
>::createnew
,
and forwards the arguments to the object's constructor
(and optionally calls
constructor
() too).
In nearly all cases, it's sufficient to define a constructor in
the reference-counted subclass of x::obj
,
but it's possible to define a custom base
with
a create
() that does something else.
Reference-counted objects do not get explicitly
delete
d, as this is done by the reference-counting
templates; and
create
() removes all the new
s
too. With create
(), x::ref
, and x::ptr
, an application
that uses LibCXX will not have any new
s and
delete
s.
Outside of create
(), LibCXX itself
has a grand total of
two explicit new
s, to allocate two low-level objects
before most of its classes get initialized.
create
() is required to create
subclasses of x::obj
. An explicit
new
, and assigning the result to an x::ref
or an x::ptr
will not work. Assigning a native pointer,
to an existing
x::obj
subclass instance, to an
x::ref
or an x::ptr
is permitted, and has the expected result of incrementing the
internal reference count.
It can be convenient to use a reference-counted object with a
begin
() and end
(),
like an
x::weakmap
,
as a range iterator:
typedef x:ptr<widgetObj> widgetptr; typedef x::weakmap<std::string, widgetObj> weakwidgetmap; weakwidgetmap get_current_widgets(); auto current_widgets = get_current_widgets(); // ... for (auto widget: *current_widgets) { // ... }
Note the need to dereference current_widgets
since,
for
all practical purposes, it's a pointer.
begin
() and
end
() gets implemented in the object it points
to.
This shorter alternative seems intuitive, but it's actually ill-formed:
for (auto widget: *get_current_widgets()) { // ... }
Range iteration uses rvalue fereferences. The reference from
create
() goes out of scope and gets destroyed,
but the rvalue reference remains in scope, and gets invoked for the
begin
() and the
end
().
C++11 defines range iteration as equivalent to
(roughly speaking).
auto && range=*get_current_widgets(); for (auto iter=range.begin(); auto != range.end(); ++iter) { // ... }
get_current_widgets()
returns a
reference-counted object. *
dereferences, and
leaves it as an rvalue reference. The reference then gets destroyed,
at the end of the sequence point, and as it's the only reference to
the reference-counted object, it gets destroyed.
Meanwhile, the rvalue reference, to the destroyed object remains,
producing ill-formed results.
Rule of thumb to follow: when using the *
operator
to dereference a reference-counted object for a container-based
range operation, the *
operator should operate
on an lvalue, and never an rvalue.