Question about virtual inheritance

A

aap

Hi,
I have the following code.

#include <iostream>
using namespace std;
class Root
{
public:
virtual void Root1() = 0;
virtual void Root2() = 0;
};
class A: public virtual Root
{
public:
virtual void Root1() {cout << "A.Root1" << endl;}
virtual void Root2() {cout << "A.Root1" << endl;}
virtual void A0() {cout << "A.A0" << endl;}

virtual void A1() {cout << "A.A1" << endl;}
virtual void A2() {cout << "A.A2" << endl;}
};
class B: public virtual Root
{
public:
virtual void B1() {cout << "B.B1" << endl;}
};
class Derived : public A, public B
{
virtual void D1() { cout << "Derived:D1() " << endl;}
};

void main()
{
Derived *d = new Derived;
void *f = d;
cout << "d->Root1" << endl;
d->Root1();
cout << "((Root *)f)->Root1()" << endl;
((Root *)f)->Root1();

}
I don't quite understand what happens when we cast d to void * and then
cast to (Root *) and invoke Root1(). But it prints "A.A0()".

Could someone please explain what is happening?

Thanks in advance.
 
V

Victor Bazarov

aap said:
I have the following code.

#include <iostream>
using namespace std;
class Root
{
public:
virtual void Root1() = 0;
virtual void Root2() = 0;
};
class A: public virtual Root
{
public:
virtual void Root1() {cout << "A.Root1" << endl;}
virtual void Root2() {cout << "A.Root1" << endl;}
virtual void A0() {cout << "A.A0" << endl;}

virtual void A1() {cout << "A.A1" << endl;}
virtual void A2() {cout << "A.A2" << endl;}
};
class B: public virtual Root
{
public:
virtual void B1() {cout << "B.B1" << endl;}
};
class Derived : public A, public B
{
virtual void D1() { cout << "Derived:D1() " << endl;}
};

void main()

int main()
{
Derived *d = new Derived;
void *f = d;
cout << "d->Root1" << endl;
d->Root1();
cout << "((Root *)f)->Root1()" << endl;
((Root *)f)->Root1();

}
I don't quite understand what happens when we cast d to void * and then
cast to (Root *) and invoke Root1(). But it prints "A.A0()".

Could someone please explain what is happening?

Undefined behaviour is happening. Casting like that is not defined
in C++.

V
 
R

Ron Wills

At Tue, 21 Jun 2005 17:19:29 -0400,
Victor said:
int main()


Undefined behaviour is happening. Casting like that is not defined
in C++.

This may not be defined in the specs. What you are doing is ok, but
VERY dangerous. Basically casting doesn't change the memory structure
by default (unless an operator is defined). So pointing f to d doesn't
make any changes and then cast f back to Root * doesn't change any thing
either. This is why it is working, but you are putting a lot of trust
in how the compiler is working.

What make this very dangerous is the way the objects are created in
memory. This explaination is not 100%, but gets the idea across. When
an instance is created, it's very much like a c struct populated with
the attributes defined from the class (NOTE: this excludes any class
variables) then followed by the virtual table (virtual methods are
mapped this way and none virtual method are just called
directly). Here's an example:

class A {
public:
A(void) : i(10);

int i;
virtual void fn0() {cout << i << endl;}
};

would look something like:

struct __class_A {
int __class_A_i;
void *virt_tab[] = {
A::fn0
};
}

Now if we create a derived class

class B : public A {
public:
int j;
virtual void fn0(void) { ... }
virtual void fn1(void) { ... }
};

Now the compiler can put this togther is a couple of different ways:

Example 1, appending the derived class properties:
struct __class_B {
int __class_A_i;
void *virt_tab_A[] = {
B::fn0
};
int __class_B_j;
void *virt_tab_B[] = {
B::fn1
};
}

Example 2, merging the derived class properties:
struct __class_B {
int __class_A_i;
int __class_B_j
void *virt_tab[] = {
B::fn0,
B::fn1
};
}

Since you example works, I'd assume it doing something along the lines
of Example 1. If a compiler was using something along the lines of
Example 2, your example would fail misurably. Since how object are put
together is not defined in the language, good luck with this ;) So
basically a void pointer should be VERY last resort when using C++.
 
A

aap

Actually, my code does not work as I expected it to work. I am
accessing method Root1 and the program is invoking A::A0.

The reason I am asking this question is that I want to KNOW how a
typical compiler implements virtual inheritance.

Be assured that I don't want to do it in a real code. I just want to
know WHY I see what I see.

Thanks.

p.s. I am running it on Microsoft's Visual C++ compiler.
 
R

Ron Wills

At 21 Jun 2005 15:06:00 -0700,
aap said:
Actually, my code does not work as I expected it to work. I am
accessing method Root1 and the program is invoking A::A0.

The reason I am asking this question is that I want to KNOW how a
typical compiler implements virtual inheritance.

Be assured that I don't want to do it in a real code. I just want to
know WHY I see what I see.

Thanks.

p.s. I am running it on Microsoft's Visual C++ compiler.

Basically virtual functions a called from a table of function pointers
with the instance of the object. To try to clarify this non virtual
methods are call directly and are hard coded into the binary. Virtual
methods are called the same way you would call a function pointer from
C. Here's the idea in C:

void fn1(int *this) { printf("fn1(%d)\n", *this); }
void fn2(int *this) { printf("fn2(%d)\n", *this); }
void (fnvirt *)(int *this) = fn2;

int main() {
int i = 10;
fn1(&i); // like calling a non virtual method
(*fnvirt)(&i); // like calling a virtual method
};

When you derive a call and redefine it's virtual method it basically
updates the method pointer to its new method. This way if you cast a
derived class to it's base class you are still going to call the
derived classes virtual methods otherwise you just end up calling the
base class methods (non-virtual).

class A {
public:
void fn_novirt(void) { cout << "A::fn_novirt()" << endln; }
virtual void fn_virt(void) { cout << "A::fn_virt()" << endln; }
};

class B : public A {
public:
void fn_novirt(void) { cout << "B::fn_novirt()" << endln; }
virtual void fn_virt(void) { cout << "B::fn_virt()" << endln; }
};

int main() {
A *a1 = new A();
B *b1 = new B();

(A *)a1->fn_virt(); // prints A::fn_novirt()
(A *)a1->fn_virt(); // prints A::fn_virt()

/* Here we can see that virtual functions are resolved at run time
from the objects virtual table */
(A *)a1->fn_novirt(); // prints A::fn_novirt()
(A *)b1->fn_virt(); // prints B::fn_virt()

return 0;
}

I hope this helps ;)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top