多重继承和重复函数调用

发布于 2024-10-16 21:04:17 字数 1654 浏览 6 评论 0原文

我遇到类似于多重继承+虚函数混乱的情况。我在一个不太复杂的环境中复制了代码,以演示我感到困惑的地方。

我想知道 C 如何执行(但不访问)B1::method 和 B2::method,然后依次执行继承的方法。

我认为这是正确工作的唯一方法是因为父类是将函数调用传播到子类的类,因此它直接访问 Bx 的 vtable,而不是通过 C。

在任何一种情况下,都是它安全,还是未定义的行为,有哪些陷阱等;以及什么可能是更好的方法来做到这一点。

#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;
}

输出:

B1inhmethod in C
B2inhmethod in C

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

只有影子陪我不离不弃 2024-10-23 21:04:17

我认为原因是 C 继承了 A 的两份副本,一份来自 B1,一份来自 B2。继承图如下所示:

A       A
|       |
B1     B2
  \   /
   \ /
    C

当您创建 C 时,它会初始化 B1 和 B2 基类,这两个基类都会递归地初始化其 A 基类。这会导致两个不同的指针被添加到 A 的主列表中 - 指向 C 的 B1 基础对象的指针和指向 A 的 B2 基础对象的指针。它们都是同一个对象的一部分 - 即 C 实例 - 但因为有逻辑上涉及两个 A 基础对象,您将获得两个指针。然后,当您迭代 A 对象的列表时,您将找到 C 的 B1 组件和 C 的 B2 组件,因此两条消息都会打印出来。

如果这不是您想要的,请考虑研究虚拟继承,这将使 B1 和B2 对象共享一个 A 基础对象。这样,只有该对象的一个​​副本将被添加到主列表中。当然,如果这样做,你必须小心,因为那样你将需要 C 实现 method 以避免歧义;如果您没有定义它,则有两种 method 实现,但显然都不是正确的调用方式。

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.

人│生佛魔见 2024-10-23 21:04:17

我假设这

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

实际上是对 B1method()B2method() 的重写,这只是一个拼写错误。我还假设您的问题是为什么调用这两个函数。

由于类 C 继承自 B1 和 B2,因此它们的构造函数都将被调用,并且它们都将被添加到侦听器向量中。

那么由于应该调用哪个类方法method是没有歧义的,B1和B2仅继承自A并使用简单的多态性,B1Method和B2Method都会被调用。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文