Asu
Asu

Reputation: 75

How polymorphism works involving multiple inheritance?

I'm studying topics related to multiple inheritance now. I came up with the following code, and couldn't totally figure out the mechanism behind it:

struct root
{
    virtual void vfunction(){ /* root version */ }
};

struct mid1:public root
{
    virtual void vfunction(){ /* mid1 version */ }
};

struct mid2:public root
{
    virtual void vfunction(){ /* mid2 version */ }
};

struct inheritMulti:public mid1, public mid2
{
    void ambiguityMethod(){
        vfunction();    // error: ambiguous
    }
    void method1(){
        mid1& t = *this;
        t.vfunction();
    }
    void method2(){
        mid2& t = *this;
        t.vfunction();
    }
};

The ambiguityMethod is an error, obviously. However, the function calls in both method1 and method2 confused me. They are virtual function call as well, and t is actually of type inheritMulti. So they should call the inheritMulti version of vfunction, but since inheritMulti doesn't have its own version, I don't know what will happen. And it turns out the call in method1 calls the mid1 version, while the call in method2 calls the mid2 version. Is it something undefined and only happened on my compiler? If not, why does it works like this? How does the vtable handle this kind of situation? Thanks!


Thanks for helping in the first place. I have searched about relating topics myself, so yes, I know "diamond problem". But I think my question is different from that. My main concern is whether the behavior of "virtual function call" in method1 and method2 well-defined or undefined by the standard. In compiler, it behaved like what I mentioned above, but is the behavior promised by the standard? If it is well-defined, why does it call the mid1 and 'mid2' version respectively? (The intuitive thought would be calling the inheritMulti version since the type of t is actually inheritMulti) And also, how does most of the compilers handle this situation? It is weird that virtual function call in method1 and method2 call different functions. Thanks again!

Upvotes: 0

Views: 168

Answers (3)

Nishant
Nishant

Reputation: 1665

void method1(){
    mid1& t = *this;
    t.vfunction();
}

Here you are calling vfunction which is not defined in inheritMultihence it will search for the nearest definition of vfunction, and that is present in mid1. See the hierarchy.

Root->Mid1->inheritMulti

Root->Mid2->inheritMulti

same in the case of

`void method2(){
    mid2& t = *this;
    t.vfunction();
}

Here also the nearest definition was found in mid2 and hence the output. These calls are not ambiguous because both structures have created there own copy of vfunction.

inheritMulti will have two Subobjects named mid1 and mid2, and each of these subobjects maintain their own vtable pointer. Though you may think that when you do

mid1& t1 = *this;

and mid2& t2 = *this;

both t1 and t2 are same, but try doing this...

bool same = ((void*)t1) == ((void*)t2);  // Result false!

Because compiler generates some code behind to adjust the pointer to point the proper object.

Upvotes: 1

Elvis Oric
Elvis Oric

Reputation: 1364

Your code is describing "The diamond problem" well known problem in multiple inheritance (for vfunction). For method1 and method2 everything is ok, because you are making mid1 and mid2 objects from this and call vfunction from their namespaces.

Upvotes: 1

David Haim
David Haim

Reputation: 26486

The virtual keyword does not play here at all.

when two classes has functions with the same name, and a third class inherits from them both, you need to manually specify on which base class are you refereing when you invoke that function with the scope operator :: .

 void ambiguityMethod(){
        mid1::vfunction();
    }

or

 void ambiguityMethod(){
        mid2::vfunction();
    }

or

 void ambiguityMethod(){
        root::vfunction();
    }

with t : (warning: weird syntax ahead!)
t.mid1::vfunction();

you can also override this function and call some base class function, thus specifing just once which function are you invoking:

void inheritMulti::vfunction() override {
  return mid1::vfunction();
}

the compiler will search for the nearest function with the same signature when calling vfunction, and will call this function which in its turn - will call mid1 function

Upvotes: 1

Related Questions