Reputation: 2191
Suppose I have the following code:
class a {
public:
virtual void do_a() = 0;
}
class b {
public:
virtual void do_b() = 0;
}
class c: public a, public b {
public:
virtual void do_a() {};
virtual void do_b() {};
}
a *foo = new c();
b *bar = new c();
Will foo->do_a()
and bar->do_b()
work? What's the memory layout here?
Upvotes: 0
Views: 239
Reputation: 109279
As others have mentioned the following will work without any problems
foo->do_a();
bar->do_b();
These, however, will not compile
bar->do_a();
foo->do_b();
Since bar
is of type b*
it has no knowledge of do_a
. The same is true for foo
and do_b
. If you want to make those function calls you must downcast.
static_cast<c *>(foo)->do_b();
static_cast<c *>(bar)->do_a();
The other very important thing that is not shown in your example code is, when inheriting, and referring to the derived class through base class pointer, the base class MUST have a virtual destructor. If it doesn't then the following will produce undefined behavior.
a* foo = new c();
delete a;
The fix is simple
class a {
public:
virtual void do_a() = 0;
virtual ~a() {}
};
Of course, this change needs to be made to b
as well.
Upvotes: 2
Reputation: 19052
foo->do_a(); // will work
bar->do_b(); // will work
bar->do_a(); // compile error (do_a() is not a member of B)
foo->do_b(); // compile error (do_b() is not a member of A)
// If you really know the types are correct:
C* c = static_cast<C*>(foo);
c->do_a(); // will work
c->do_b(); // will work
// If you don't know the types, you can try at runtime:
if(C* c = dynamic_cast<C*>(foo))
{
c->do_a(); // will work
c->do_b(); // will work
}
Upvotes: 1
Reputation: 308520
Yes, of course it will work. The mechanics are a bit tricky though. The object will have two vtables, one for the class a parent and one for the class b parent. The pointers will be adjusted so that they point to the subset of the object that corresponds to the pointer type, leading to this surprising result:
c * baz = new c;
a * foo = baz;
b * bar = baz;
assert((void *)foo == (void *)bar); // assertion fails!
The compiler knows the types at the time of the assignment, and knows exactly how to adjust the pointers.
This is of course completely compiler dependent; nothing in the C++ standard says it has to work this way. Only that it has to work.
Upvotes: 1
Reputation: 9182
They will work. In terms of memory, this is implementation dependent. You have created objects on the heap, and for most systems, it is worth noting that objects on the heap grow upwards (c.f. the stack grows downwards). So possibly, you will have:
Memory:
+foo+
-----
+bar+
Upvotes: 0
Reputation: 385395
Will a->do_a() and b->do_b() work?
No.
Will foo->do_a()
and bar->do_b()
work?
Yes. Your code is the canonical example of virtual function dispatch.
Why didn't you just try it?
What's the memory layout here?
Who cares?
(i.e. this is implementation-defined, and abstracted from you. You should not need to nor want to know.)
Upvotes: 0
Reputation: 154027
Why shouldn't they? The memory layout will typically be something like:
+----------+
| A part |
+----------+
| B part |
+----------+
| C part |
+----------+
If you convert your foo
and bar
to void*
and display them, you'll
get different addresses, but the compiler knows this, and will arrange
for the this
pointer to be correctly fixed up when calling the
function.
Upvotes: 3
Reputation: 361732
Will a->do_a() and b->do_b() work?
Assuming you meant foo->do_a()
and bar->do_b()
, as a
and b
are not object, they're type, yes. They will work. Did you try run that?
What's the memory layout here?
That is implementation-defined, mostly. Fortunately, you don't need to know about that unless you want to write non-portable code.
Upvotes: 4