throw 和 throw with arg of catch Exception 有什么区别?

发布于 2024-08-05 16:41:07 字数 345 浏览 11 评论 0原文

想象两段相似的代码:

try {
  [...]
} catch (myErr &err) {
  err.append("More info added to error...");
  throw err;
}

它们

try {
  [...]
} catch (myErr &err) {
  err.append("More info added to error...");
  throw;
}

实际上是相同的还是有一些微妙的不同?例如,第一个是否会导致运行复制构造函数,而第二个可能会重用同一对象来重新抛出它?

Imagine two similar pieces of code:

try {
  [...]
} catch (myErr &err) {
  err.append("More info added to error...");
  throw err;
}

and

try {
  [...]
} catch (myErr &err) {
  err.append("More info added to error...");
  throw;
}

Are these effectively the same or do they differ in some subtle way? For example, does the first one cause a copy constructor to be run whereas perhaps the second reuses the same object to rethrow it?

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

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

发布评论

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

评论(2

温柔女人霸气范 2024-08-12 16:41:07

根据您安排异常层次结构的方式,通过在 throw 语句中命名异常变量来重新引发异常可能会分割原始异常对象。

无参数的 throw 表达式将抛出当前异常对象,并保留其动态类型,而带参数的 throw 表达式将根据 throw< 的参数的 static 类型抛出新的异常/代码>。

例如,

int main()
{
    try
    {
        try
        {
            throw Derived();
        }
        catch (Base& b)
        {
            std::cout << "Caught a reference to base\n";
            b.print(std::cout);
            throw b;
        }
    }
    catch (Base& b)
    {
        std::cout << "Caught a reference to base\n";
        b.print(std::cout);
    }

    return 0;
}

如上面所写,程序将输出:

Caught a reference to base
Derived
Caught a reference to base
Base

如果将 throw b 替换为 throw,则外层 catch 也会捕获最初抛出的 Derived 异常。如果内部类通过值而不是通过引用捕获 Base 异常,这仍然成立 - 尽管这自然意味着原始异常对象无法修改,因此对 b 的任何更改> 不会反映在外部块捕获的 Derived 异常中。

Depending on how you have arranged your exception hierarchy, re-throwing an exception by naming the exception variable in the throw statement may slice the original exception object.

A no-argument throw expression will throw the current exception object preserving its dynamic type, whereas a throw expression with an argument will throw a new exception based on the static type of the argument to throw.

E.g.

int main()
{
    try
    {
        try
        {
            throw Derived();
        }
        catch (Base& b)
        {
            std::cout << "Caught a reference to base\n";
            b.print(std::cout);
            throw b;
        }
    }
    catch (Base& b)
    {
        std::cout << "Caught a reference to base\n";
        b.print(std::cout);
    }

    return 0;
}

As written above, the program will output:

Caught a reference to base
Derived
Caught a reference to base
Base

If the throw b is replace with a throw, then the outer catch will also catch the originally thrown Derived exception. This still holds if the inner class catches the Base exception by value instead of by reference - although naturally this would mean that the original exception object cannot be modified, so any changes to b would not be reflected in the Derived exception caught by the outer block.

噩梦成真你也成魔 2024-08-12 16:41:07

在第二种情况下,根据 C++ 标准 15.1/6,不使用复制构造函数:

没有操作数的 throw 表达式会重新抛出正在处理的异常。使用现有临时重新激活异常;没有创建新的临时异常对象。 异常不再被认为已被捕获;因此,uncaught_Exception() 的值将再次为 true。

在第一种情况下,将根据 15.1/3 抛出新的异常:

throw 表达式初始化一个临时对象,称为异常对象,其类型是通过从 throw 操作数的静态类型中删除任何顶级 cv 限定符并调整来确定的
从“T 的数组”或“返回 T 的函数”到“指向 T 的指针”或“指向返回 T 的函数的指针”的类型,
分别。 <...>该临时变量用于初始化匹配处理程序 (15.3) 中指定的变量。 throw 表达式的类型不应是
不完整类型,或指向不完整类型的指针或引用,除了 void*、const void*、
易失性 void* 或 const 易失性 void*。除了这些限制和以下限制外
15.3 中提到的类型匹配, throw 的操作数被完全视为调用中的函数参数
(5.2.2) 或 return 语句的操作数。

在这两种情况下,抛出阶段都需要复制构造函数(15.1/5):

当抛出的对象是类对象,并且用于初始化临时副本的复制构造函数不可访问时,程序格式错误(即使可以消除临时对象)。
同样,如果该对象的析构函数不可访问,则程序格式错误(即使可以消除临时对象)。

In the second case according to C++ Standard 15.1/6 copy constructor is not used:

A throw-expression with no operand rethrows the exception being handled. The exception is reactivated with the existing temporary; no new temporary exception object is created. The exception is no longer considered to be caught; therefore, the value of uncaught_exception() will again be true.

In the first case new exception will be thrown according to 15.1/3:

A throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting
the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”,
respectively. <...> The temporary is used to initialize the variable named in the matching handler (15.3). The type of the throw-expression shall not be an
incomplete type, or a pointer or reference to an incomplete type, other than void*, const void*,
volatile void*, or const volatile void*. Except for these restrictions and the restrictions on
type matching mentioned in 15.3, the operand of throw is treated exactly as a function argument in a call
(5.2.2) or the operand of a return statement.

In both cases copy constructor is required at throw stage (15.1/5):

When the thrown object is a class object, and the copy constructor used to initialize the temporary copy is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated).
Similarly, if the destructor for that object is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated).

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