【发布时间】:2016-12-17 14:12:42
【问题描述】:
我正在编译一个 C++ 库,但我遇到了析构函数和noexcept 的问题。
据我了解,从 C++11 开始,析构函数中 noexcept(...) 的默认值发生了变化。特别是,现在析构函数默认为noexcept(即noexcept(true))。
现在,我有一个库,由于某些类的析构函数可以抛出这一事实,我收到了许多编译器警告,但它没有标记为noexcept(false)。所以我决定改变它,并添加规范。然而,这些类继承自一些共同的父类Base,所以这迫使我将它也添加到父类的析构函数中,否则我得到编译器错误overriding ‘virtual Base::~Base() noexcept。
当然,我也可以在基类中添加noexcept(false)(实际上,据我了解,添加它就足够了),但是有很多类继承自Base,它们都会继承noexcept(false) 属性(据我所知)。但是,基类析构函数本身永远不会抛出,只有少数派生类可以真正抛出。因此,只为少数几个类标记基类析构函数noexcept(false) 似乎是一种浪费。
所以,我有两个问题:
1) 有没有办法解决这个问题?我无法删除抛出的少数派生类中的抛出,因为它们很重要。
2) 我不确定由于将noexcept(false) 添加到库中(实际上)所有类的基类中,编译器优化会降低多少。什么时候(如果有的话)这应该让我担心?
编辑:附带说明一下,有没有办法在项目中更改noexcept 的默认值?也许是编译器选项?
编辑:由于有很多反馈,我欠一个编辑,有几点意见:
1) 我没有编写库,当然也不打算重写许多类的析构函数。必须将noexcept(false) 添加到某些基类已经足够痛苦了。并且求助于不同的库不是一种选择(出于编程之外的不同原因)。
2) 当我说“我不能删除抛出的少数派生类中的抛出,因为它们很重要”时,我的意思是我相信代码应该在这种情况下终止,因为发生了非常糟糕的事情并且可能会出现未定义的行为无论如何。投掷至少对发生的事情做了一点解释。
3) 我知道在派生类中加入析构函数是不好的,因为它可能会由于未调用父类的析构函数而泄漏(这是正确的吗?)。如果一个人应该终止程序(以避免以后出现模糊的错误)并且仍然让用户知道发生了什么,那么什么是一种干净的方法?
【问题讨论】:
-
"...我无法删除抛出的少数派生类中的
throws,因为它们很重要。" - 听起来很糟糕而且设计得很糟糕图书馆。 Throwing from the Destructor 很危险。 -
如果可能,尝试更改析构函数,使它们不会抛出。从析构函数中抛出异常是危险的,因为可以在堆栈展开期间调用析构函数,此时另一个异常正在运行(已抛出但尚未捕获)。当另一个正在运行时抛出异常将终止程序。
-
"我不能删除少数派生类中的 throws,因为它们很重要。" 是的,你可以。您只是不想为了实际这样做而做出改变。但事实是你最有能力做到这一点。你可能把自己设计到了一个角落,但你总是可以重新设计自己。
-
"库中(实际上)所有类的基类" 如果您有一种类型,它是库中“几乎”每个类的基础。 . 可能出了大问题。这听起来像是 Java 风格的设计,而不是 C++。
-
一个可以让异常逃逸的析构函数最终会导致程序崩溃。如果您是该库的创建者,那么您应该为您的用户停止从您的析构函数发出异常,这样您最终不会使他们的程序崩溃。如果您是该库的用户获取一个新库。
标签: c++ c++11 inheritance destructor noexcept