Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/137.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++_Exception_Exception Handling_Destructor_Exception Specification - Fatal编程技术网

异常规范如何影响虚拟析构函数重写? C++标准对具有异常规范的虚函数陈述如下:

异常规范如何影响虚拟析构函数重写? C++标准对具有异常规范的虚函数陈述如下:,c++,exception,exception-handling,destructor,exception-specification,C++,Exception,Exception Handling,Destructor,Exception Specification,如果虚拟函数具有异常规范,则在任何派生类中重写该虚拟函数的任何函数的所有声明(包括定义)应仅允许基类虚拟函数的异常规范(C++03§15.4/3)允许的异常 因此,以下内容的格式不正确: struct B { virtual void f() throw() { } // allows no exceptions }; struct D : B { virtual void f() { } // allows all exceptions }; (1) 这个规则

如果虚拟函数具有异常规范,则在任何派生类中重写该虚拟函数的任何函数的所有声明(包括定义)应仅允许基类虚拟函数的异常规范(C++03§15.4/3)允许的异常

因此,以下内容的格式不正确:

struct B {
    virtual void f() throw() { } // allows no exceptions
};
struct D : B {
    virtual void f() { }         // allows all exceptions
};
(1) 这个规则适用于析构函数吗?也就是说,下面的格式是否正确

struct B {
    virtual ~B() throw() { }
};
struct D : B {
    virtual ~D() { }
};
(2) 该规则如何应用于隐式声明的析构函数?也就是说,下面的格式是否正确

struct B {
    virtual ~B() throw() { }
};
struct D : B { 
    // ~D() implicitly declared
};
虽然在一般情况下应该这样做,但这个问题具有实际意义,因为
std::exception
析构函数是虚拟的,并且有一个空的异常规范

由于不允许从析构函数抛出异常是一种良好的做法,为了简化任何示例,让我们假设析构函数要么允许所有异常(即,它没有异常规范),要么不允许任何异常(即,它有一个空的异常规范)。

(1)这个规则适用于析构函数吗? 是的,此规则适用于析构函数(析构函数规则没有例外),因此此示例的格式不正确。为了使其格式良好,
~D()
的异常规范必须与
~B()
的异常规范兼容,例如

struct B {
    virtual ~B() throw() { }
};
struct D : B {
    virtual ~D() throw() { }
};
(2) 该规则如何应用于隐式声明的特殊成员函数?

C++标准对隐含声明的特殊成员函数如下:

隐式声明的特殊成员函数应具有异常规范

如果
f
是隐式声明的默认构造函数、复制构造函数、析构函数或复制赋值运算符,则其隐式异常规范指定类型id
T
,当且仅当由
f
的隐式函数直接调用的函数的异常规范允许
T
定义

如果直接调用的任何函数允许所有异常,
f
应允许所有异常,如果直接调用的每个函数不允许任何异常,
f
应不允许任何异常(C++03§15.4/13)

隐式声明的析构函数直接调用哪些函数

在执行析构函数的主体并销毁主体中分配的任何自动对象之后,类
X
的析构函数调用

  • X
    的直接成员的析构函数
  • X
    的直接基类的析构函数和
  • 如果
    X
    是派生最多的类的类型,则其析构函数调用
    X
    的虚拟基类的析构函数
(C++03§12.4/6;重新格式化以便于阅读)

因此,隐式声明的析构函数有一个异常规范,允许这些析构函数允许的任何异常。考虑问题的例子:

struct B {
    virtual ~B() throw() { }
};
struct D : B { 
    // ~D() implicitly declared
};
隐式声明的
~D()
调用的唯一析构函数是
~B()
。由于
~B()
不允许异常,
~D()
不允许异常,就好像它被声明为
virtual~D()throw()

此异常规范显然与
~B()
兼容,因此此示例格式良好


作为一个实际例子,为什么要考虑这一点,请考虑以下内容:

struct my_exception : std::exception {
    std::string message_;
};
~string()
允许所有异常,因此隐式声明的
~my_exception()
允许所有异常。基类析构函数
~exception()
,是虚拟的,不允许任何异常,因此派生类析构函数与基类析构函数不兼容,这是格式错误的

为了使此示例格式良好,我们可以使用空异常规范显式声明析构函数:

struct my_exception : std::exception {
    virtual ~my_exception() throw() { }
    std::string message_;
};

虽然经验法则是永远不要编写异常规范,但至少有一种常见情况需要这样做。

此外,从析构函数中抛出异常也不是一个好主意。@Ben Voigt:同意;我在问题的最后一句说了。我想我已经养成了在答案中寻找细节的习惯,而不是在问题中。只要回答者比提问者聪明,这可能没关系。这导致了一个相当奇怪的推论:你比自己聪明吗?在你发布问题的同时,你是如何得到如此详细的答案的?@Ben:我今天早上本来打算发布这个问题,但最后我自己研究了答案,因为我很感兴趣(我真的不太熟悉异常规范的细节),所以我决定先问,然后自己回答这个问题。我相当肯定,如果有什么不同的话,我比我自己还要笨:-)。我当然有兴趣看到这个问题的其他答案,特别是如果我错过了一些细节或一些密切相关的问题。我在研究GCC4.6中的“松散抛出说明符”错误时偶然发现了这个问题。与您的
struct D:B
示例相反,gcc的隐式析构函数似乎没有throw声明。