【问题标题】:C++ Exception throw annotations on virtual functions虚函数上的 C++ 异常抛出注释
【发布时间】:2010-09-21 18:52:15
【问题描述】:

我看到了下面的sn-p代码:

class Foo
{
public:
        void virtual func() throw (int, float) = 0;
};

class Bar : public Foo
{
public:
        void virtual func() throw(short);      // line 1: compile error "
                                                                      // looser throw specifier"
        void virtual func() throw();                // line 2: can compile
        void virtual func() throw(float, int); // line 3: can compile
        void virtual func() throw(float);        // line 4: can compile
        void virtual func() throw(int);           // line 5: can compile

};

int main(void)
{
        return 1;
}

Q1>是什么意思

void virtual func() throw (int, float) = 0;

Q2>为什么line1不能通过编译?

谢谢

【问题讨论】:

    标签: c++ virtual throw


    【解决方案1】:

    让我们分解一下。声明:

    void virtual func() throw (int, float) = 0;
    

    有 2 个您要询问的构造。 =0 构造告诉编译器声明的函数是“抽象的”,它告诉编译器该函数不需要在 class Foo 中定义(尽管可以 - 但通常不是)并且一个对象的 class Foo 不能直接创建 - 作为本地、全局或通过 new。但是,您可以拥有指向 class Foo 对象的指针或引用。某些派生类需要将该函数重写为非抽象函数 - 可以直接创建该类的对象(只要没有其他尚未“具体化”的抽象函数)。

    throw (int, float) 构造是一个异常说明。这告诉编译器该函数的约定是,如果它抛出异常,它只会抛出 intfloat 类型的异常。如果函数抛出某种其他类型的异常,编译器有义务专门处理该异常(通过调用std::unexpected())。

    现在,如果您尝试使用以下声明覆盖派生类中的该函数:

    void virtual func() throw(short);
    

    您是说函数的约定是,如果抛出异常,它将抛出 short 类型的异常。但是,抛出 short 不是被覆盖函数的合同的一部分,因此编译器不允许这样做。

    如果你像这样声明覆盖:

    void virtual func() throw(float);
    

    您是说覆盖可以抛出float,这是原始声明合同的一部分(如果它从不抛出不会破坏合同的int - 原始合同只说该函数被允许抛出一个int,而不是它必须)。

    标准的相关部分是15.4/3 Exception规范:

    如果一个虚函数有一个 异常规范,全部 声明,包括 定义,任何功能 覆盖任何虚函数 派生类只允许 允许的例外情况 基础的异常规范 类虚函数。

    请注意,标准明确指出异常规范不是函数类型 (15.4/12) 的一部分,因此函数指针可以指向具有不同异常规范的函数。

    【讨论】:

      【解决方案2】:

      您多次定义相同的函数签名。不同的throw() 限定符不足以消除功能的歧义。

      throw() 限定符仅仅意味着指定的函数只期望抛出限定符后面括号中列出的类型。但是,这实际上并不能阻止函数抛出。相反,如果函数确实确实抛出任何未列出的类型,您的程序将终止。

      【讨论】:

      • 那是倒退。如果函数抛出任何列出的类型,则可以捕获和处理异常。未列出的类型会导致程序终止(通过std::unexpected)。
      【解决方案3】:

      您在基类中定义的函数做出了保证——它只能抛出intfloat。你的第 1 行失败了,因为它违反了保证,说它会抛出一个 short,这不是上述任何一个。

      Q1 中的= 0 声明您尝试创建实例的每个派生类都需要提供自己的声明和此函数的实现。基类也可以提供实现,但通常不提供。

      【讨论】:

      • 纯修饰符= 0 不会阻止在基类中有具体实现。
      【解决方案4】:

      命名的 throw 语句的含义是声明一个函数只能直接或间接地抛出那些命名的异常。

      所以行:

      void virtual func() throw(int, float) =0;
      

      表示无论继承此基类型的任何类都只允许抛出 int 或 float。它不能直接或间接抛出任何其他类型的异常或对象。如果是这样,它将调用unexcepted() 函数。默认情况下,这会调用terminate() 函数。您可以使用 set_unexpected 函数重置它,但仍然可以。

      通过选择将这些 throw 语句添加到您的界面中,您实际上是在限制自己。

      【讨论】:

      • 实际上如果函数违反了它的约定,std::unexpected 将被使用而不是抛出的值。评论不会这样做。
      • 实际上以我写的方式阅读它非常具有误导性。等一下。
      • 错字:未例外与意外
      【解决方案5】:

      当您覆盖virtual 函数时,您提供的任何异常说明符必须至少与您要覆盖的函数上指定的一样严格。这可以防止违反基类的异常规范。

      由于基类的异常说明符 [throw (int, float)] 不允许抛出 short,派生类也不允许抛出 short。最多允许int 和/或float;它可能只允许抛出一个或一个都不抛出,因为这些可能性中的任何一种都比基类函数的异常规范更具限制性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-08-05
        • 1970-01-01
        • 1970-01-01
        • 2014-11-03
        • 2019-03-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多