Boost logo

Boost :

From: Howard Hinnant (hinnant_at_[hidden])
Date: 2002-03-11 18:37:27


On Monday, March 11, 2002, at 03:33 PM, brangdon_at_[hidden]
wrote:

> In-Reply-To: <B59D39CC-3457-11D6-8200-003065D18932_at_[hidden]>
> On Sun, 10 Mar 2002 13:50:36 -0500 Howard Hinnant (hinnant_at_[hidden])
> wrote:
>> Note that the base class got destructed first. During the period that
>> Derived was busy relocating its members, it was operating with a
>> destructed Base.
>
> This isn't quite right. Although the source Base is destructed, the
> destination Base has now been constructed. And it is the destination
> Derived which is busy relocating the members. It need not and should not
> invoke code of the source Derived. It can talk to its own Base, if it
> wishes.

No matter where you consider this to point to (old or new address), at
some point you're in this configuration:

          new address old address

            +-----+ destructed
            | B | B
            +-----+

            not yet +-----+
          constructed | D |
               D +-----+

Base might have a virtual method:

virtual void notify_construction();

that notifies a list of listeners that the Base is being constructed.

And Derived might have overridden that method in order to notify yet
another list of listeners that Derived has been constructed.

And of course ~Base() and ~Derived() would tell their respective
listeners that they were being destructed. For example:

class Dervied
     : public Base
{
     Dervied() : owns_(0) {notify_construciton();}
     ~Dervied() {notify_destruction(); delete owns_;}
     virtual void notify_construction()
        {client_->add(this);}
     virtual void notify_destruction()
        {client_->remove(this);}
     listner* client_;
     resource* owns_;
};

It would be natural in relocate construct to try to say:

Derived(relocate from x)
      : Base(relocate from x), client_(x.client_), owns_(x.owns_)
{
     x.notify_destruction();
     notify_construction();
}

But as you said (and I agree), code can not be executed through x
because its Base is already gone.

So perhaps we need a new term, at least for educational purposes:

     A lame duck object is an object that you can reference data members,
     but not base objects, nor member functions whether derived or not.

As soon as an object is bound to a "relocate from reference", it becomes
a lame duck object. Or maybe just: as soon as any member of an object
(base or direct member) is relocated, then that object is a lame duck.
Violating the access rules of a lame duck object results in undefined
behavior (no diagnostic required).

So in my example, the notify_xxx methods would have to be redesigned so
that this could call notify_destruction() but with the address of x.

Derived(relocate from x)
      : Base(relocate from x), client_(x.client_), owns_(x.owns_)
{
     notify_destruction(&x);
     notify_construction();
} // x is destructed now x.~Derived() never called

This all seems doable so far. But it does not seem simple. Looks like
mine field...
Non-destructive move is tons simpler than this.

Derived(move from x)
      : Base(move from x), client_(x.client_), owns_(x.owns_)
{
     notify_construction();
     x.owns_ = 0;
}

-Howard


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk