我正在编译一个C ++库,我遇到了析构函数和noexcept
的问题。
据我了解,自C ++ 11以来,析构函数中的默认值noexcept(...)
已发生变化。特别是,现在析构函数默认为noexcept
(即noexcept(true)
)。
现在,我有一个库,由于某些类的析构函数可以抛出,但是它没有标记为noexcept(false)
,因此我收到许多编译器警告。所以我决定改变它,并添加规范。但是,这些类继承自一些公共父Base
,所以这迫使我将它也添加到父类析构函数中,否则我得到编译器错误overriding ‘virtual Base::~Base() noexcept
。
当然,我也可以将noexcept(false)
添加到基类中(实际上,根据我的理解,它足以将其添加到那里),但是有许多类继承自{{1}并且它们都将继承Base
属性(据我所知)。但是,基类析构函数本身将从不抛出,并且只有少数派生类可以实际抛出。因此,仅为少数几个类标记基类析构函数noexcept(false)
似乎是 waste 。
所以,我有两个问题:
1)有什么方法吗?我无法删除抛出的少数派生类中的抛出,因为它们很重要。
2)由于将noexcept(false)
添加到(几乎)库中所有类的基类,我不确定编译器优化会降低多少。什么时候(如果有的话)这涉及到我?
编辑:在旁注中,有没有办法在项目中更改noexcept(false)
的默认值?也许是编译器选项?
编辑:由于有很多反馈,我欠了一个编辑,只有几点评论:
1)我没有编写库,我当然不打算重写许多类的析构函数。必须向某些基类添加noexcept
已经足够痛苦了。并且不能选择使用不同的库(出于不同的原因,超出编程范围)。
2)当我说“我无法删除抛出的少数派生类中的抛出,因为它们很重要”我的意思是我认为代码应该在这种情况下终止,因为发生了一些非常糟糕的事情并且可能会出现未定义的行为无论如何。投掷至少会对发生的事情做一点解释。
3)我理解抛出派生类的析构函数是不好的,因为它可能会泄漏,因为没有调用父类的析构函数(这是正确的吗?)。如果一个人应该终止程序(以避免以后出现模糊的错误)并且仍然让用户知道发生了什么,那么干净的方法是什么?
答案 0 :(得分:2)
如果析构函数是虚拟的,派生的析构函数不能是noexcept(false)
,除非基础析构函数也是noexcept(false)
。
考虑一下:虚函数的关键是即使调用者只知道基类,也可以调用派生类。如果某个函数在delete
上调用Base*
,则如果Base
承诺其析构函数不会抛出异常,则可以编译调用而不使用任何异常处理代码。如果指针指向析构函数实际上 抛出异常的Derived
实例,那就不好了。
尝试更改派生的析构函数,以便它们不会抛出异常。如果那不可能,请将noexcept(false)
添加到基础析构函数。
答案 1 :(得分:2)
由于
noexcept(false)
添加到(几乎)库中所有类的基类,我不确定编译器优化会降低多少。什么时候(如果有的话)这涉及到我?
您不应该基于析构函数的noexcept
声明来关注编译器优化。围绕noexcept
声明的大多数优化都来自检测操作为noexcept
的代码,并根据该代码执行更高效的操作。也就是说,明确的元编程。
为什么析构函数不重要?好吧,考虑一下C ++标准库。如果您手动vector
一个类型,并且所包含对象的一个析构函数抛出,则会得到未定义的行为。这对于标准库中的所有都是如此(除非明确注明)。
这意味着标准库无需检查您的析构函数是否为noexcept
。它可以,并且几乎肯定会假设没有析构函数发出异常。如果一个人......那么,那是你的错。
因此,您的代码可能不会因为在析构函数上使用noexcept(false)
而运行得更慢。您是否在基类的析构函数上使用noexcept(false)
的决定不应受性能问题的影响。