Topic: backward virtual function call


Author: hitz@sim5.csi.uottawa.ca (Martin Hitz)
Date: 30 Apr 91 02:40:10 GMT
Raw View
I wonder if the following is guaranteed to print 1:

#include <stream.h>
struct X {
 virtual f() { return 1; }
};
struct Y : X {
 f() { return 2; }
};

main()
{
 X x;
 Y * y = (Y *) &x;
 cout << y->f();
}

The ARM explains the usual case, that a call of f() for an object of
class Y invokes Y::f(), even if it is called via a pointer to X,
but I couldn't find anything about the above case. Zortech and g++
both yield 1 for this example. Is this considered to be the standard
behaviour?

Martin Hitz (hitz@csi.uottawa.ca)




Author: wmm@world.std.com (William M Miller)
Date: 30 Apr 91 14:03:13 GMT
Raw View
hitz@sim5.csi.uottawa.ca (Martin Hitz) writes:
> struct X {
>         virtual f() { return 1; }
> };
> struct Y : X {
>         f() { return 2; }
> };
>
> main()
> {
>         X x;
>         Y * y = (Y *) &x;
>         cout << y->f();
> }
>
> The ARM explains the usual case, that a call of f() for an object of
> class Y invokes Y::f(), even if it is called via a pointer to X,
> but I couldn't find anything about the above case.

That's because it's an error.  See section 5.4 of E&S: "Such a cast from a
base to a derived class assumes that the object of the base class is a
sub-object of an object of the derived class; the resulting pointer points
to the enclosing object of the derived class.  If the object of the base
class is not a sub-object of an object of the derived class, the cast may
cause an exception."  In other words, the behavior is undefined, and an
implementation under which the program printed -356, or dumped core, or
erased your disk would be perfectly legitimate.

-- William M. Miller, Glockenspiel, Ltd.
   wmm@world.std.com




Author: ark@alice.att.com (Andrew Koenig)
Date: 30 Apr 91 21:15:21 GMT
Raw View
In article <1991Apr30.024010.4331@csi.uottawa.ca> hitz@sim5.csi.uottawa.ca (Martin Hitz) writes:

> #include <stream.h>

> struct X {
>  virtual f() { return 1; }
> };

> struct Y : X {
>  f() { return 2; }
> };

> main()
> {
>  X x;
>  Y * y = (Y *) &x;
>  cout << y->f();
> }

The effects of things like this are undefined.

In this particular example, I expect that many implementations
will print 2, but the assignment

 Y * y = (Y *) &x;

is well-defined only if &x points at an object of class Y --
which, of course, it does not.
--
    --Andrew Koenig
      ark@europa.att.com




Author: jimad@microsoft.UUCP (Jim ADCOCK)
Date: 6 May 91 19:12:04 GMT
Raw View
In article <1991Apr30.024010.4331@csi.uottawa.ca> hitz@sim5.csi.uottawa.ca (Martin Hitz) writes:
|I wonder if the following is guaranteed to print 1:
|
|#include <stream.h>
|struct X {
| virtual f() { return 1; }
|};
|struct Y : X {
| f() { return 2; }
|};
|
|main()
|{
| X x;
| Y * y = (Y *) &x;
| cout << y->f();
|}
|
|The ARM explains the usual case, that a call of f() for an object of
|class Y invokes Y::f(), even if it is called via a pointer to X,
|but I couldn't find anything about the above case. Zortech and g++
|both yield 1 for this example. Is this considered to be the standard
|behaviour?

What would a C++ compiler then do with the following?
[All that I'm aware of act in an ill-defined manner, which is expected
since the ARM prohibits such erroneous down-casts.  Your example just
happens to work on most C++ compilers]

extern "C" int printf(const char*, ...);

class A
{
 private: virtual void illegalToAccess()
 { printf("erroneous access of A private method\n"); }
 public: virtual void print() { printf("A\n"); }
};

class B
{
 public: virtual void print() { printf("B\n"); }
 private: virtual void illegalToAccess()
 { printf("erroneous access of B private method\n"); }
};

class AB: public A, public B
{
 public: virtual void print() { printf("AB\n"); }
};

main()
{
 A aA;
 B aB;
 A* a = &aA;
 ((AB*)a)->print();
 B* b = &aB;
 ((AB*)b)->print();

 return 0;
}