C ++ Exception在虚函数上抛出注释

时间:2010-09-21 18:52:15

标签: c++ virtual throw

我看到了以下代码段:

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无法通过编译器?

谢谢

5 个答案:

答案 0 :(得分:9)

让我们打破这个。声明:

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

有两个你要问的结构。 =0构造告诉编译器声明的函数是'abstract',告诉编译器函数不需要在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异常规范:

  

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

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

答案 1 :(得分:6)

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

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

答案 2 :(得分:3)

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

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

答案 3 :(得分:2)

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

所以这一行:

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

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

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

答案 4 :(得分:2)

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

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