Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/134.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么在C++;?_C++_Exception_Inheritance - Fatal编程技术网

C++ 为什么在C++;?

C++ 为什么在C++;?,c++,exception,inheritance,C++,Exception,Inheritance,我目前正在处理异常类型,在尝试重新抛出捕获的异常时,我注意到了一些奇怪的事情。 从C++规范中,我知道抛出实际上会产生一个你试图抛出的对象的拷贝,所以你最终会将你捕获的任何派生的类型信息切片。 为了避免这种情况,我看到了重新抛出指向原始异常的指针的建议,因为实际的原始对象将不会删除其派生部分。 然而,我在下面编写的简单示例程序似乎不能以这种方式工作: #包括 #包括 #包括 类派生的_异常:public std::exception{}; 无效重试例外(bool匿名){ 试一试{ 抛出派生的_异

我目前正在处理异常类型,在尝试重新抛出捕获的异常时,我注意到了一些奇怪的事情。
从C++规范中,我知道抛出实际上会产生一个你试图抛出的对象的拷贝,所以你最终会将你捕获的任何派生的类型信息切片。 为了避免这种情况,我看到了重新抛出指向原始异常的指针的建议,因为实际的原始对象将不会删除其派生部分。 然而,我在下面编写的简单示例程序似乎不能以这种方式工作:

#包括
#包括
#包括
类派生的_异常:public std::exception{};
无效重试例外(bool匿名){
试一试{
抛出派生的_异常();
}捕获(const std::exception&e){
std::cout 1.抛出什么?

5.17引发异常

  • 没有操作数的抛出表达式将重新抛出当前处理的异常
  • 15.1引发异常

  • 抛出异常副本会初始化一个临时对象,称为异常对象。临时对象是一个左值,用于初始化匹配处理程序中声明的变量
  • 对于语句
    throw;
    ,它将重新抛出当前异常对象
    派生的\u exception()

    对于语句
    throw&e;
    ,它将创建一个类型为
    std::exception*
    的临时对象,实际上相当于
    throw(std::exception*,除了_obj=&e);

    2.哪个
    catch
    catch?

    15.1引发异常

  • 抛出异常副本会初始化一个临时对象,称为异常对象。临时对象是一个左值,用于初始化匹配处理程序中声明的变量。
  • 15.3处理异常

  • 处理程序与类型为E的异常对象匹配,如果

    • [3.1]处理程序的类型为cv T或cv T&且E和T的类型相同(忽略顶级cv限定符),或
    • [3.2]处理程序的类型为cv T或cv T&且T是明确的公共基类E,或
    • [3.3]处理程序的类型为cv T或const T&其中T为指针类型,E为指针类型,可通过以下任一种或两种方式转换为T:
      • 一种标准指针转换(4.10),不涉及到私有类、受保护类或不明确类指针的转换
      • 资格转换,或
    • [3.4]处理程序的类型为cv T或const T&其中T是一个指针或指向成员类型的指针,E是std::nullptr\T
  • try块的处理程序按外观顺序进行尝试

  • 匿名==true

    执行
    throw;
    ,从而重新抛出异常对象
    派生的\u exception()
    ,然后从以下各项中选择一个入口点:

    • 捕获(常量派生的\u异常&e)
    • catch(const std::exception&e)
    从标准15.3-3-3.2中,我们知道
    derived_exception()
    匹配
    catch(const-derived_exception&e)
    。因此,输出为:

    Re-caught: 17derived_exception (std::exception)
    
    匿名==false时

    执行
    throw&e;
    ,从而创建一个类型为
    std::exception*
    的临时异常对象,然后从以下对象中选择一个入口点:

    • catch(常量派生的\u异常*e)
    • catch(const std::exception*e)
    我们不能选择前者。因为标准15.3-3中的四条规则中没有一条规定
    std::exception*
    const-derived\u exception*
    相匹配

    因此,选择后面的
    catch
    。我们可以看到输出:

    Re-caught: PKSt9exception (std::exception)
    
    (您可能想讨论第三条规则[3.3],但标准指针转换和限定转换都不支持从基类指针到子类指针的转换,必须明确地执行向下转换,就像使用
    动态_cast()

    3.在你的问题中

    从C++规范中,我知道抛出实际上会产生一个你要抛出的对象的拷贝,

    因此,您将对捕获到的任何剩余派生类型信息进行切片

    如果在
    catch
    中使用值类型而不是引用或指针,则正确

    try {
        throw derived_exception();
    } catch (const std::exception e) {
        ...
    }
    
    从标准15.1-3中,我们知道这里
    e
    将用
    derived_exception()
    初始化。实际上,这就像执行
    e=derived_exception();
    。不过,我找不到任何使用此表单的理由

    我看到过一些建议,建议重新抛出指向原始异常的指针,因为实际的原始对象将不会删除其派生部分

    通过将
    typeid(e).name()
    替换为
    typeid(*e).name()
    ,我们可以看到原始对象未被切片:

    catch (const std::exception *e)
    {
        std::cout << "Re-caught: " << typeid(*e).name() << " (std::exception)"
                  << std::endl;
    }
    // Re-caught: 17derived_exception (std::exception)
    
    catch(const std::exception*e)
    {
    
    std::cout
    derived_exception
    是从
    exception
    派生而来的,但是
    derived_exception*
    不是从
    exception*
    派生而来的。因此,不要通过指针捕捉异常,而是通过常量引用捕捉异常。
    匿名化
    是一个错误的名称,因为
    true
    时,您得到了正确的类型,使用false,你有基本类型……抛出/捕获系统不会为你编写代码>动态DistaCase指针。“从C++规范中,我知道投掷实际上会产生一个你试图抛出的对象的拷贝,所以你最终将删除你捕获的任何残余派生类型信息。”-我不认为这是真的。
    catch (const std::exception *e)
    {
        std::cout << "Re-caught: " << typeid(*e).name() << " (std::exception)"
                  << std::endl;
    }
    // Re-caught: 17derived_exception (std::exception)