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
析构函数是虚拟的并且具有空的异常规范。
因为最好不要从析构函数中抛出异常,所以我们假设为了简化析构函数允许所有异常(即,它没有异常规范)或者它不允许的任何示例。异常(即,它有一个空的异常规范)。
答案 0 :(得分:13)
是的,这个规则适用于析构函数(析构函数规则没有例外),所以这个例子是不正确的。为了使其格式良好,~D()
的异常规范必须与~B()
的异常规范兼容,例如,
struct B {
virtual ~B() throw() { }
};
struct D : B {
virtual ~D() throw() { }
};
C ++标准说明了以下关于隐式声明的特殊成员函数:
隐式声明的特殊成员函数应具有异常规范。
如果
如果它直接调用的任何函数允许所有异常,f
是隐式声明的默认构造函数,复制构造函数,析构函数或复制赋值运算符,则其隐式异常规范指定类型标识T
当且仅当T
由f
隐式调用的函数的异常规范允许 定义;
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_;
};
虽然经验法则永远不会编写异常规范,但至少有一个常见的情况是必须这样做。