尝试除了和继承

try except and inheritance

为什么结果是“B”,我觉得应该先命中继承class(“A”)?当我 运行 它与 class B 不从 class A 继承任何东西时,它会遇到第一个 catch 块,但我不知道下面代码中出现这种行为的原因:

#include <iostream>
#include <exception>

using namespace std;

class A {};

class B : public A{};

class C : public A, public B {};

int main() {
    try {
        throw C();
    }
    catch (A a) {
        cout << "A" << endl;
    }
    catch (B b) {
        cout << "B" << endl;
    }
    catch (C c) {
        cout << "C" << endl;
    }
}   

要获得您期望的行为,您应该进行虚拟推导。
还有一个提示作为指南 按值抛出 - 按引用捕获

#include <iostream>
#include <exception>

class A {};

class B : virtual public A {};

class C : public virtual  A, public B {};

int main() {
    try {
        throw C();
    }
    catch (A const& a) {
        std::cout << "A" << std::endl;
    }
    catch (B const& b) {
        std::cout << "B" << std::endl;
    }
    catch (C const& c) {
        std::cout << "C" << std::endl;
    }
}

您的 classes 是 deadly diamond of death 案例 multiple inheritance 的一些变体:基础 class A 是 a 的两倍C 的基础 class:一次直接,一次间接通过 B:

              A
              |\
              | \
              |  B 
              | /
              |/
              C

这种继承图的结果是您的 C 对象有两个不同的 A 子对象。为了方便起见,我在图中命名它们:

            a1:A a2:A
              |   |
              |   |
              |  b:B 
              |  /
              | /
              c:C

如你所见,如果提到A子对象,就会有歧义:是a1还是a2,而B则没有歧义。这会影响异常处理的匹配。因为标准的规则如下:

[except.handle]/3: A handler is a match for an exception object of type E if
— The handler is of type cv T or cv T& and E and T are the same type (ignoring the top-level cv-qualifiers), or
— the handler is of type cv T or cv T& and T is an unambiguous public base class of E, or
— ...

当您 throw C 时,首先测试 catch (A a):A 与 C 不是同一类型。并且 A 也不是明确的 public 基 class .然后测试下一个 catch (B b):B 与 C 不是同一类型。但它是它的一个明确的 public 基 class。比赛!因此 你的结果 .

解决方案 1:

按照 中的建议,使 A 成为 BC 的虚拟基础,确保只有一个唯一且明确的子对象在 C 中。这就是它起作用的原因。

解决方案 2

虚拟继承不是灵丹妙药。一旦有了非默认构造函数,就必须系统地显式显示虚构造函数。此外,只要 A 是子class,就必须使用虚拟继承。最后但并非最不重要的是,虚拟继承有时只是不适合需要(例如,如果需要两个独立的链)。

在最后一种情况下,您还可以通过引入中介来消除歧义 class:

class AA : public A {}; 
class C : public AA, public B {};

然后 catch (AA& a) 而不是捕获 A。这是愚蠢的简单,但它消除了歧义,而仍然有两个不同的 A 子对象(再次:仅当您需要它们时)。 (在线demo

建议:为了全面了解情况,我建议您尝试更新您的示例并在每个 class 中添加一些您想要的成员使用非默认构造函数进行初始化。是的,通过引用捕获异常。