异常规范如何影响虚拟析构函数覆盖?

时间:2010-07-12 23:32:31

标签: c++ exception exception-handling destructor exception-specification

C ++标准规定了以下关于具有异常规范的虚函数:

  

如果虚函数具有异常 - 规范,则在任何派生类中覆盖该虚函数的任何函数的所有声明(包括定义)都只允许基类虚函数的异常规范(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
};

虽然在一般情况下应该never write an exception specification,但这个问题具有实际意义,因为std::exception析构函数是虚拟的并且具有空的异常规范。

因为最好不要从析构函数中抛出异常,所以我们假设为了简化析构函数允许所有异常(即,它没有异常规范)或者它不允许的任何示例。异常(即,它有一个空的异常规范)。

1 个答案:

答案 0 :(得分:13)

(1)此规则是否适用于析构函数?

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

struct B {
    virtual ~B() throw() { }
};
struct D : B {
    virtual ~D() throw() { }
};

(2)此规则如何应用于隐式声明的特殊成员函数?

C ++标准说明了以下关于隐式声明的特殊成员函数:

  

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

     

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

     如果它直接调用的任何函数允许所有异常,

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

虽然经验法则永远不会编写异常规范,但至少有一个常见的情况是必须这样做。