Topic: deferencing the this pointer


Author: Michiel.Salters@cmg.nl (Michiel Salters)
Date: Tue, 28 May 2002 15:27:41 GMT
Raw View
Allan_W@my-dejanews.com (Allan W) wrote in message news:<23b84d65.0205241536.240bd881@posting.google.com>...
> > Allan_W@my-dejanews.com (Allan W) wrote
> > > So, if the method is NOT virtual, it cannot act like a virtual
> > > function!
>
> Michiel.Salters@cmg.nl (Michiel Salters) wrote
> > But it can be in a vtable, though. As long as the override of
> > a non-virtual gets a different offset in the vtable, putting
> > non-virtuals in vtables doesn't hurt .(until you dereference 0,
> > which was the original issue ).
> >
> > Is this unlikely? I know a C implementation which already
> > calls all functions via a function pointer table; the reasons
> > for it (dynamic function updating) apply unchanged to C++.
>
> Let's say that foo has 15 virtual functions. bar() is not one
> of them. You're saying that foo::bar() might be in position 16
> of foo's vtable. This is clearly true, under the "as-if" rule.
> But what would be the point of doing that? What advantage does
> it give to the user, to the library code, to anyone?

On at least one current system, the OS can patch a running proces.
It does so by calling all functions via a function pointer from
a table. Implementing C++ on such a system would probably reuse
this table as a vtable.

> As you say, "the override of a non-virtual [would have to get]
> a different offset in the vtable." Basically this means that any
> class derived from foo would also have to have foo::bar() in
> position 16, even if the derived class has it's own function
> named bar(). This makes the vtable of foo and all derived classes
> larger, for no obvious gain.

Actually, you can overlap the vtables, even better if you use
negative indices for the non-virtuals.

> Instead of emitting code that says, "call the function in position
> 16 of the vtable," it would be better to say, "call the function
> foo::bar() directly." Note that it's also faster to call a function
> directly -- only one or two machine cycles, usually, but why pay
> even that when there's absolutely no advantage to it?

Well, as it happens there is such an advantage for certain OSes

Regards,
--
Michiel Salters

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Allan_W@my-dejanews.com (Allan W)
Date: Fri, 24 May 2002 20:09:33 CST
Raw View
> Allan_W@my-dejanews.com (Allan W) wrote
> > So, if the method is NOT virtual, it cannot act like a virtual
> > function!

Michiel.Salters@cmg.nl (Michiel Salters) wrote
> But it can be in a vtable, though. As long as the override of
> a non-virtual gets a different offset in the vtable, putting
> non-virtuals in vtables doesn't hurt .(until you dereference 0,
> which was the original issue ).
>
> Is this unlikely? I know a C implementation which already
> calls all functions via a function pointer table; the reasons
> for it (dynamic function updating) apply unchanged to C++.

Let's say that foo has 15 virtual functions. bar() is not one
of them. You're saying that foo::bar() might be in position 16
of foo's vtable. This is clearly true, under the "as-if" rule.
But what would be the point of doing that? What advantage does
it give to the user, to the library code, to anyone?

As you say, "the override of a non-virtual [would have to get]
a different offset in the vtable." Basically this means that any
class derived from foo would also have to have foo::bar() in
position 16, even if the derived class has it's own function
named bar(). This makes the vtable of foo and all derived classes
larger, for no obvious gain.

Instead of emitting code that says, "call the function in position
16 of the vtable," it would be better to say, "call the function
foo::bar() directly." Note that it's also faster to call a function
directly -- only one or two machine cycles, usually, but why pay
even that when there's absolutely no advantage to it?

...or is there an advantage, that I somehow missed?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Allan_W@my-dejanews.com (Allan W)
Date: Wed, 22 May 2002 16:12:40 CST
Raw View
fjh@cs.mu.OZ.AU (Fergus Henderson) wrote
> The C++ committee wanted to permit implementations in which all method
> calls were implemented in essentially the same way as virtual method calls,

Perhaps they wanted to permit this, but they failed to do so. 5.2.2/1:

    ... The function called in a member function call is normally
    selected according to the static type of the object expression
    (clause 10), but if that function is virtual and is not specified
    using a qualified-id then the function actually called will be the
    final overrider (10.3) of the selected function in the dynamic
    type of the object expression [Note: the dynamic type is the type
    of the object pointed or referred to by the current value of the
    object expression. 12.7 describes the behavior of virtual function
    calls when the object-expression refers to an object under
    construction or destruction.]

So, if the method is NOT virtual, it cannot act like a virtual
function!

    struct foo {
        // NOTE: Not virtual (at least not here)
        void bar() { /* ... */ }
    };
    struct foo2 : public foo {
        void bar() { /* ... */ }
    };
    void baz(foo *f) {
        f->bar(); // MUST call foo::bar(),
                  // even if f points to a foo2
    }

The code in baz() has to call foo::bar(), because foo::bar is not
virtual.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Michiel.Salters@cmg.nl (Michiel Salters)
Date: Thu, 23 May 2002 17:12:31 GMT
Raw View
Allan_W@my-dejanews.com (Allan W) wrote in message news:<23b84d65.0205221144.42760e38@posting.google.com>...
> fjh@cs.mu.OZ.AU (Fergus Henderson) wrote
> > The C++ committee wanted to permit implementations in which all method
> > calls were implemented in essentially the same way as virtual method calls,
>
> Perhaps they wanted to permit this, but they failed to do so. 5.2.2/1:
>
>     ... The function called in a member function call is normally
>     selected according to the static type of the object expression
>     (clause 10), but if that function is virtual and is not specified
>     using a qualified-id then the function actually called will be the
>     final overrider (10.3) of the selected function in the dynamic
>     type of the object expression [Note: the dynamic type is the type
>     of the object pointed or referred to by the current value of the
>     object expression. 12.7 describes the behavior of virtual function
>     calls when the object-expression refers to an object under
>     construction or destruction.]
>
> So, if the method is NOT virtual, it cannot act like a virtual
> function!

But it can be in a vtable, though. As long as the override of
a non-virtual gets a different offset in the vtable, putting
non-virtuals in vtables doesn't hurt .(until you dereference 0,
which was the original issue ).

Is this unlikely? I know a C implementation which already
calls all functions via a function pointer table; the reasons
for it (dynamic function updating) apply unchanged to C++.

Regards,
--
Michiel Salters

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Michiel.Salters@cmg.nl (Michiel Salters)
Date: Tue, 14 May 2002 22:27:02 GMT
Raw View
whatiscpp@yahoo.com (John the newbie) wrote in message news:<102a8848.0205131020.4575f2de@posting.google.com>...
> Hi everybody,
>
> this is a strange tale of changing one's mind twice :)
> In the first few days of my C++ learning I used to wonder if it wasn't
> a trouble that the compiler created a copy of each member function for
> every object I instantiated :) Yes, now I know that's an absurdity but
> I didn't know about the this pointer yet and, if I remember well, my
> book (an old Schildt edition) brought me to such an idea.

Well, with such a book that's not surprising to this group's veterans.
A new book might clear up more than we can do in a few posts. Have a
llok at ACCU's book list (www.accu.org)

> Anyhow, when I heard of 'this' I suddenly began to think that given,
> let's say,
>
> class foo {
>   public:
>     void m();
> };
>
> then each call of the form
>
> <foo object>.m();
>
> goes exactly as it was
>
> m(&<foo object>), with m() declared as:
>
> void m (foo* ).
>
> The only differences are in name lookup and access checking of course.
> And I still think this is the reason why, after doing these two
> important things, a compiler can straightly translate C++ into C.
> If I'm wrong about this late sentence please correct me.

Wrong: virtual functions come into play, inheritance may lead to 'this'
adjustments and additionally, 'this' is never a null pointer.

> Anyhow, yesterday I examined the following recursive function to
> deallocate a binary tree:
>
> typedef struct node_ {
>   int val; // actual data
>   struct node_ * l;
>   struct node_ * r;
>
> } node;
>
> void destroy(node *p) {
>     if (!p)
>       return;
>
>     destroy (p->l);
>     destroy (p->r);
>     free(p);
> }
>
> Now, with the logic I presented above the C++ "equivalent" would be:
>
>
> class node {
>  //....
>
>  void destroy();
> };
>
> void node::destroy ()  { // member function!
>
>     if (!this)
>       return;
>
>     l->destroy();
>     r->destroy();
>     delete this;
>
> }
>
>
> But since <null pointer>->destroy() is undefined this suddenly reveals
> me that, again, the C form and the C++ one are not equivalent.
> In fact, I'm prevented to implement the function recursively.

Come on;
void node::destroy ()  { // member function!
     if (l) l->destroy();
     if (r) r->destroy();
     delete this;
}
is also recursive and saves a function call when p==0

> So I wonder, why doesn't the standard explicitly allow a call made
> through the operator -> with a null left hand, i.e. not considering it
> of a null pointer deferentiation?
>
> I want to stress the fact that when this == NULL destroy() doesn't
> access data members of its class and the call 0->destroy() doesn't
> need to access any object (I think).

Too easy to shoot yourself in the foot, and it doesn't play well
with virtual functions. In addition to virtual functions lookup,
(multiple) inheritance will require adjustment so that when you
call Base::foo() with a Derived*, 'this' is adjusted by the offset
of Derived in Base. Adjusting NULL just doesn't work.

Regards,
--
Michiel Salters

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "AT" <coveraaten@imation.com>
Date: Wed, 15 May 2002 17:57:39 GMT
Raw View

--
_________________
Remove "cover" when reply.
"John the newbie" <whatiscpp@yahoo.com> wrote in message
news:102a8848.0205131020.4575f2de@posting.google.com...
> Hi everybody,
>
> this is a strange tale of changing one's mind twice :)
> In the first few days of my C++ learning I used to wonder if it wasn't
> a trouble that the compiler created a copy of each member function for
> every object I instantiated :) Yes, now I know that's an absurdity but
> I didn't know about the this pointer yet and, if I remember well, my
> book (an old Schildt edition) brought me to such an idea.
>
> Anyhow, when I heard of 'this' I suddenly began to think that given,
> let's say,
>
> class foo {
>   public:
>     void m();
> };
>
> then each call of the form
>
> <foo object>.m();
>
> goes exactly as it was
>
> m(&<foo object>), with m() declared as:
>
> void m (foo* ).
>
>
> The only differences are in name lookup and access checking of course.
> And I still think this is the reason why, after doing these two
> important things, a compiler can straightly translate C++ into C.
> If I'm wrong about this late sentence please correct me.
>

You are not alone on this way. As I remember it was a discovery for me also.
The mainstream you got right. Functions are the same for the class. This why
the access is checked only on the level of classes but not objects. i.e.
objects of the same class may access their private/protected members. The
only way for the functions to distiguish objects is if they have a pointer
to data structure (this).

But there are differences. For example, you may not get an address of a
member function (unless it is static), since it is nonsense. Member
functions are sensible only in context of an object. Also you may consider
inheritance.


>
> Anyhow, yesterday I examined the following recursive function to
> deallocate a binary tree:
>
>
> typedef struct node_ {
>   int val; // actual data
>   struct node_ * l;
>   struct node_ * r;
>
> } node;
>
> void destroy(node *p) {
>     if (!p)
>       return;
>
>     destroy (p->l);
>     destroy (p->r);
>     free(p);
> }
>
>
>
> Now, with the logic I presented above the C++ "equivalent" would be:
>
>
> class node {
>  //....
>
>  void destroy();
> };
>
> void node::destroy ()  { // member function!
>
>     if (!this)
>       return;
>
>     l->destroy();
>     r->destroy();
>     delete this;
>
> }
>
>
> But since <null pointer>->destroy() is undefined this suddenly reveals
> me that, again, the C form and the C++ one are not equivalent.
> In fact, I'm prevented to implement the function recursively.
> So I wonder, why doesn't the standard explicitly allow a call made
> through the operator -> with a null left hand, i.e. not considering it
> of a null pointer deferentiation?
>
> I want to stress the fact that when this == NULL destroy() doesn't
> access data members of its class and the call 0->destroy() doesn't
> need to access any object (I think).
>

Not sure about the point you are trying to make.
What you wrote is perfectly valid for point of view of C++. I would not say
that you gonna be awarded Nobel Prize for that but othewise it is fine.
However you're wrong in stating that you are not toching members. Once you
write
r-> you alredy got the member. If you don't have this member properly
initialized (let say r and l are 0xFFFF) then next destroy will get
this==0xFFFF. Then the destroy will be look like

void destroy()
{
if (!0xFFFF)
   return;
 (((node*)0xFFFF)->r)->destroy() ; here you will get exception since you
will get in the protected area.
....
}

However if you initilize in constructor you l and r with NULLs then
everything will go smoothly.

Moreover you may do something like that (I think most of compilier will
allow you, at least it used to be).

class foo{
int a;
public:
    void WorkInAnyCase() { cout << "I don't work, but I provide you an
immitation"; }
}

void main()
{
((foo*)0)->WorkInAnyCase();
}

This should work. This is actually  a great advice how to get fired:).

Sincerely, Arkady

> ---
> [ comp.std.c++ is moderated.  To submit articles, try just posting with ]
> [ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
> [              --- Please see the FAQ before posting. ---               ]
> [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]
>

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: fjh@cs.mu.OZ.AU (Fergus Henderson)
Date: Wed, 15 May 2002 17:57:52 GMT
Raw View
whatiscpp@yahoo.com (John the newbie) writes:

 >void node::destroy ()  { // member function!
 >    if (!this)
 >      return;
 >
 >    l->destroy();
 >    r->destroy();
 >    delete this;
 >}
 >
 >But since <null pointer>->destroy() is undefined this suddenly reveals
 >me that, again, the C form and the C++ one are not equivalent.
 >In fact, I'm prevented to implement the function recursively.
 >So I wonder, why doesn't the standard explicitly allow a call made
 >through the operator -> with a null left hand, i.e. not considering it
 >of a null pointer deferentiation?
 >
 >I want to stress the fact that when this == NULL destroy() doesn't
 >access data members of its class and the call 0->destroy() doesn't
 >need to access any object (I think).

The C++ committee wanted to permit implementations in which all method
calls were implemented in essentially the same way as virtual method calls,
which do need to access the object (to get the pointer to the virtual
function table).

--
Fergus Henderson <fjh@cs.mu.oz.au>  |  "I have always known that the pursuit
The University of Melbourne         |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- the last words of T. S. Garp.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Robert Klemme <bob.news@gmx.net>
Date: Wed, 15 May 2002 17:58:21 GMT
Raw View
John the newbie schrieb:
[snip]
> But since <null pointer>->destroy() is undefined this suddenly reveals
> me that, again, the C form and the C++ one are not equivalent.
> In fact, I'm prevented to implement the function recursively.

wrong.

class Node {
  int val;
  Node *l;
  Node *r;

public:
  ~Node() { delete l; delete r; }
}

note, that "delete NULL;" is legal and does not crash!

5.3.5 Delete

In either alternative, if the value of the operand of delete is
the null pointer the operation has no effect.

> So I wonder, why doesn't the standard explicitly allow a call made
> through the operator -> with a null left hand, i.e. not considering it
> of a null pointer deferentiation?

the semantic of invoking a method on a NULL instance (i.e. on NO
instance) is at least doubtful.  when there is no instance then
there's nobody that can receive messages and react on them.

> I want to stress the fact that when this == NULL destroy() doesn't
> access data members of its class and the call 0->destroy() doesn't
> need to access any object (I think).

strangly enough i believe that this is legal as long as the
method invoked is not virtual.  has anybody standard references
for this?

regards

 robert

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: whatiscpp@yahoo.com (John the newbie)
Date: Sat, 18 May 2002 19:34:45 GMT
Raw View
Thanks to anyone who replied :) I didn't think of the problem
concerning adjusting 'null' for virtual functions, neither the fact
(thanks Fergus Henderson) that the compiler is free to implement all
calls as if they was polymorphic calls.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: whatiscpp@yahoo.com (John the newbie)
Date: Mon, 13 May 2002 18:24:12 GMT
Raw View
Hi everybody,

this is a strange tale of changing one's mind twice :)
In the first few days of my C++ learning I used to wonder if it wasn't
a trouble that the compiler created a copy of each member function for
every object I instantiated :) Yes, now I know that's an absurdity but
I didn't know about the this pointer yet and, if I remember well, my
book (an old Schildt edition) brought me to such an idea.

Anyhow, when I heard of 'this' I suddenly began to think that given,
let's say,

class foo {
  public:
    void m();
};

then each call of the form

<foo object>.m();

goes exactly as it was

m(&<foo object>), with m() declared as:

void m (foo* ).


The only differences are in name lookup and access checking of course.
And I still think this is the reason why, after doing these two
important things, a compiler can straightly translate C++ into C.
If I'm wrong about this late sentence please correct me.


Anyhow, yesterday I examined the following recursive function to
deallocate a binary tree:


typedef struct node_ {
  int val; // actual data
  struct node_ * l;
  struct node_ * r;

} node;

void destroy(node *p) {
    if (!p)
      return;

    destroy (p->l);
    destroy (p->r);
    free(p);
}



Now, with the logic I presented above the C++ "equivalent" would be:


class node {
 //....

 void destroy();
};

void node::destroy ()  { // member function!

    if (!this)
      return;

    l->destroy();
    r->destroy();
    delete this;

}


But since <null pointer>->destroy() is undefined this suddenly reveals
me that, again, the C form and the C++ one are not equivalent.
In fact, I'm prevented to implement the function recursively.
So I wonder, why doesn't the standard explicitly allow a call made
through the operator -> with a null left hand, i.e. not considering it
of a null pointer deferentiation?

I want to stress the fact that when this == NULL destroy() doesn't
access data members of its class and the call 0->destroy() doesn't
need to access any object (I think).

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: James Kanze <kanze@alex.gabi-soft.de>
Date: Tue, 14 May 2002 17:55:21 GMT
Raw View
whatiscpp@yahoo.com (John the newbie) writes:

|>  Anyhow, when I heard of 'this' I suddenly began to think that
|>  given, let's say,

|>  class foo {
|>    public:
|>      void m();
|>  };

|>  then each call of the form

|>  <foo object>.m();

|>  goes exactly as it was=20

|>  m(&<foo object>), with m() declared as:

|>  void m (foo* ).

The standard makes no requirements in this regard.  This is, however,
a typical way of implementing member functions, and the standard is
designed so that this implementation technique is legal.

|>  The only differences are in name lookup and access checking of
|>  course.  And I still think this is the reason why, after doing
|>  these two important things, a compiler can straightly translate
|>  C++ into C.  If I'm wrong about this late sentence please correct
|>  me.

There are problem areas, like exceptions.  But for the simpler stuff,
member functions et al., yes.  The compiler can translate directly
into C.  The earliest C++ compiler (CFront) used C as its intermediate
language, and even today, the EDG front-end generates C by default
(but the OEM purchasers may modify it to generate their own
intermediate language, presumably with better support for exceptions).

|>  Anyhow, yesterday I examined the following recursive function to
|>  deallocate a binary tree:

|>  typedef struct node_ {
|>    int val; // actual data
|>    struct node_ * l;
|>    struct node_ * r;
|> =20
|>  } node;

|>  void destroy(node *p) {
|>      if (!p)
|>        return;

|>      destroy (p->l);
|>      destroy (p->r);
|>      free(p);
|>  }

Attention.  The compiler can convert C++ into C.  But the generated C
isn't necessarily anything you'd write or want to maintain.  Be leary
of one to one translation at the programmer level.  Despite a certain
degree of compatibility, C and C++ are two different languages.

|>  Now, with the logic I presented above the C++ "equivalent" would be:

|>  class node {
|>   //....

|>   void destroy();
|>  };

|>  void node::destroy ()  { // member function!

|>      if (!this)
|>        return;

|>      l->destroy();
|>      r->destroy();
|>      delete this;

|>  }

|> =20
|>  But since <null pointer>->destroy() is undefined this suddenly
|>  reveals me that, again, the C form and the C++ one are not
|>  equivalent.  In fact, I'm prevented to implement the function
|>  recursively.  So I wonder, why doesn't the standard explicitly
|>  allow a call made through the operator -> with a null left hand,
|>  i.e. not considering it of a null pointer deferentiation?

C and C++ are not the same.  But that doesn't prevent writing the
function recursively; it simply means that you have to write it
slightly differently:

    void
    Node::destroy()
    {
        if ( l !=3D NULL ) {
            l->destroy() ;
        }
        if ( r !=3D NULL ) {
            r->destroy() ;
        }
        delete this ;
    }

|>  I want to stress the fact that when this =3D=3D NULL destroy() doesn'=
t
|>  access data members of its class and the call 0->destroy() doesn't
|>  need to access any object (I think).

As long as destroy isn't virtual, it is probable that the call to
destroy doesn't actually access the pointer.  It is still undefined
behavior, however, and a good compiler might optimize out the if on
the grounds that it can never be true.

--=20
James Kanze                                mailto:kanze@gabi-soft.de
Conseils en informatique orient=E9e objet/
                    Beratung in objektorientierter Datenverarbeitung
Ziegelh=FCttenweg 17a, 60598 Frankfurt, Germany Tel. +49(0)179 2607481

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]