2

I have a situation similar to Multiple inheritance + virtual function mess. I've replicated the code in a less complex environment to demonstrate what I'm confused about.

I want to know how C is executing (but not acessing) both B1::method and B2::method, which then in turn executes the inherited methods.

The only way I can see this as working (correctly), is because the parent class is the one that propagates the function call to the sub classes, so it is accessing the vtables of Bx directly instead of through C.

In either case, is it safe, or undefined behavior, what pitfalls etc; and what is maybe a better way to do this.

#include <iostream>
#include <vector>

class A {
    static std::vector<A*> listeners;
public:
    static void propagate();
protected:
    A() {
        listeners.push_back(this);
    }
    ~A() {
        for (std::vector<A*>::iterator it = listeners.begin(); it != listeners.end(); ++it) {
            if (*it == this) {
                listeners.erase(it);
                break;
            }
        }
    }
    virtual void method()=0;
};

std::vector<A*> A::listeners;

void A::propagate() {
    for (unsigned int i=0; i < listeners.size(); ++i) {
        listeners[i]->method();
    }
}

class B1 : public A {
protected:
    B1() {}
    ~B1() {}
    void method() {
        B1inhmethod();
    }
    virtual void B1inhmethod() {}
};
class B2 : public A {
protected:
    B2() {}
    ~B2() {}
    void method() {
        B2inhmethod();
    }
    virtual void B2inhmethod() {}
};

class C : public B1, public B2 {
public:
    C() {}
    ~C() {}
    void B1inhmethod() {
        std::cout << "\nB1method in C";
    }
    void B2inhmethod() {
        std::cout << "\nB2method in C";
    }
};

int main() {
    C myclass;
    A::propagate();
    return 0;
}

output:

B1inhmethod in C
B2inhmethod in C
Community
  • 1
  • 1
hiddensunset4
  • 5,825
  • 3
  • 39
  • 61

2 Answers2

3

I think that the reason for this is that C inherits two copies of A, one from B1 and one from B2. The inheritance diagram looks like this:

A       A
|       |
B1     B2
  \   /
   \ /
    C

When you create C, it initializes both the B1 and B2 base classes, both of which recursively initialize their A base class. This causes two different pointers to be added into A's master list - the pointer to the B1 base object of C and the pointer to the B2 base object of A. These are both part of the same object - namely the C instance - but because there are logically two A base objects involved you'll get two pointers. When you then iterate over the list of A objects, you'll find C's B1 component and C's B2 component, and hence both the messages print out.

If this isn't what you want, consider looking into virtual inheritance, which would let the B1 and B2 objects both share an A base object. That way, only one copy of the object would be added into the master list. Of course, you have to be careful if you do this, because then you would need C to have an implementation of method to avoid ambiguities; if you don't define it, there are two method implementations where neither is clearly the right one to call.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
0

I assuming that

void B1inhmethod() {
    std::cout << "\nB1inhmethod in C";
}
void B2inhmethod() {
    std::cout << "\nB2inhmethod in C";
}

are in fact overrides of B1method() and B2method() and that it's just a typo. And I'm also assuming that your question is why both of these are called.

Since class C inherits from both B1 and B2, both of their constructor will get called and both of them will get added to the vector of listeners.

Then since there is no ambiguity from which class method method should be called, B1 and B2 only inherits from A and use simple polymorphism, Both B1Method and B2Method will get called.

Eric Fortin
  • 7,533
  • 2
  • 25
  • 33
  • Good assumption, my bad on the typo. Thanks for your answer, I just wanted to make sure this was an OK thing to do, no hidden catches etc. – hiddensunset4 Feb 10 '11 at 04:20