C++虚函数中的异常抛出注释

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;
}

问题1>

什么是

的含义?

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

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

谢谢

5个回答

10

让我们分解一下。声明如下:

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

你所询问的问题涉及到了两种构造方式。 =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异常规范: 如果一个虚函数有一个异常规范,那么任何派生类中覆盖该虚函数的所有声明(包括定义)都只允许基类虚函数的异常规范所允许的异常。请注意,标准明确规定异常规范不是函数类型的一部分(15.4 / 12),因此,例如,函数指针可以指向具有不同异常规范的函数。

6
你正在多次定义相同的函数签名。不同的throw()限定符不能足以区分这些函数。
throw()限定符仅表示指定的函数只会抛出括号中列出的类型。然而,这并不能真正防止函数抛出其他类型的异常。如果函数实际上抛出了未列出的类型,你的程序将终止。

1
这是相反的。如果函数抛出任何列出的类型,异常可以被捕获和处理。导致程序终止的是未列出的类型(通过std::unexpected)。 - Ben Voigt

3
您在基类中定义的函数做出了保证-它只能抛出int或float。您的第一行失败是因为它打破了保证,声明将抛出一个不属于上述两种类型的short。
Q1中的“= 0”声明每个派生类都需要提供自己的声明和实现该函数的方法。基类也可以提供实现,但通常不会这样做。

纯虚函数修饰符= 0并不能防止在基类中具有具体实现。 - Ben Voigt

2

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

因此,以下代码:

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

这段文字的意思是,无论哪个类继承了这个基本类型,它只能抛出 int 或 float。它不能直接或间接地抛出任何其他类型的异常或对象。如果这样做,将调用 unexpected() 函数。默认情况下,这会调用 terminate() 函数。您可以使用 set_unexpected 函数重置它,但仍然存在限制。通过向接口添加这些 throw 语句,您实际上正在限制自己的功能。

2
实际上,如果函数违反了其契约,将使用std::unexpected而不是抛出的值。注释无法做到这一点。 - Ben Voigt
实际上按照我写的方式阅读它非常具有误导性。等一下。 - wheaties
拼写错误:unexcepted 应为 unexpected。 - Ben Voigt

2
当您重写虚函数时,提供的任何异常说明符必须至少与要重写的函数上指定的说明符一样严格。这可以防止违反基类的异常说明。由于基类的异常说明符[throw (int, float)]不允许抛出short,因此派生类不能允许抛出short。最多它可以允许抛出int和/或float; 它只能允许其中一个或两者都不抛出,因为这些可能性中的任何一种都比基类函数的异常说明更为严格。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接