尝试例外和继承

Nik*_*lic 7 c++ inheritance try-catch multiple-inheritance

为什么结果是“B”,我认为它应该命中第一个继承类(“A”)?当我使用不从 A 类继承任何内容的 B 类运行它时,它会遇到第一个 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;
    }
}   
Run Code Online (Sandbox Code Playgroud)

Chr*_*phe 1

你的类是多重继承致命钻石死亡案例的一些变体:基类 A 是 C 的基类的两倍:一次是直接的,一次是通过 B 间接的:

\n
              A\n              |\\\n              | \\\n              |  B \n              | /\n              |/\n              C\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
            a1:A a2:A\n              |   |\n              |   |\n              |  b:B \n              |  /\n              | /\n              c:C\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
\n

[except.handle]/3
:如果\n\xe2\x80\x94 处理程序的类型为 cv T 或 cv T& 并且 E 和 T类型相同,则处理程序与 E 类型的异常对象匹配(忽略顶级 cv 限定符),或
\n\xe2\x80\x94 处理程序的类型为 cv T 或 cv T& 并且 T 是E 的明确公共基类,或
\n\xe2\x80\x94 ...

\n
\n

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

\n

解决方案一:

\n

按照另一个答案中的建议,创建 和A的虚拟基,确保 C 中只有一个唯一且明确的 A 子对象。这就是它起作用的原因。BC

\n

解决方案2

\n

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

\n

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

\n
class AA : public A {}; \nclass C : public AA, public B {};\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n