5

我看到了以下代码段:

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不能通过编译?

谢谢

4

5 回答 5

9

让我们分解一下。声明:

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

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

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

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

void virtual func() throw(short);

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

如果您像这样声明覆盖:

void virtual func() throw(float);

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

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

如果虚函数具有异常规范,则在任何派生类中覆盖该虚函数的任何函数的所有声明(包括定义)都应仅允许基类虚函数的异常规范允许的异常。

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

于 2010-09-21T19:24:27.077 回答
6

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

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

于 2010-09-21T18:54:46.493 回答
3

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

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

于 2010-09-21T18:56:50.337 回答
2

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

所以这一行:

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

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

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

于 2010-09-21T18:56:00.277 回答
2

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

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

于 2010-09-21T18:58:17.783 回答