尝试除了和继承
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
成为 B
和 C
的虚拟基础,确保只有一个唯一且明确的子对象在 C 中。这就是它起作用的原因。
解决方案 2
虚拟继承不是灵丹妙药。一旦有了非默认构造函数,就必须系统地显式显示虚构造函数。此外,只要 A 是子class,就必须使用虚拟继承。最后但并非最不重要的是,虚拟继承有时只是不适合需要(例如,如果需要两个独立的链)。
在最后一种情况下,您还可以通过引入中介来消除歧义 class:
class AA : public A {};
class C : public AA, public B {};
然后 catch (AA& a)
而不是捕获 A
。这是愚蠢的简单,但它消除了歧义,而仍然有两个不同的 A 子对象(再次:仅当您需要它们时)。 (在线demo)
建议:为了全面了解情况,我建议您尝试更新您的示例并在每个 class 中添加一些您想要的成员使用非默认构造函数进行初始化。是的,通过引用捕获异常。
为什么结果是“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
成为 B
和 C
的虚拟基础,确保只有一个唯一且明确的子对象在 C 中。这就是它起作用的原因。
解决方案 2
虚拟继承不是灵丹妙药。一旦有了非默认构造函数,就必须系统地显式显示虚构造函数。此外,只要 A 是子class,就必须使用虚拟继承。最后但并非最不重要的是,虚拟继承有时只是不适合需要(例如,如果需要两个独立的链)。
在最后一种情况下,您还可以通过引入中介来消除歧义 class:
class AA : public A {};
class C : public AA, public B {};
然后 catch (AA& a)
而不是捕获 A
。这是愚蠢的简单,但它消除了歧义,而仍然有两个不同的 A 子对象(再次:仅当您需要它们时)。 (在线demo)
建议:为了全面了解情况,我建议您尝试更新您的示例并在每个 class 中添加一些您想要的成员使用非默认构造函数进行初始化。是的,通过引用捕获异常。