首页 > 解决方案 > 抛出数据成员的析构函数

问题描述

我有一个类,它包含一个析构函数可以抛出的对象(它实际上是一个 tbb::task_group,但为了简单起见,我在这里将其命名为 MyObject)。

代码是这样的:

#include <stdexcept>

class MyObject {
public:
    MyObject() {}
    ~MyObject() noexcept(false) {}
};

class A {
public:
    A() {}
    virtual ~A() {}
};

class B : public A {
public:
    B() : A() {}
    ~B() {}

private:
    MyObject _object;
};

并且编译器会引发以下错误:

覆盖函数的异常规范比基础版本更宽松

我不喜欢在整个代码中传播 noexcept(false) 的想法,所以我正在考虑使用指向 MyObject 的原始指针,并在析构函数之外删除它们(例如在 Close 函数中)。

处理这种情况的最佳方法是什么?

标签: c++

解决方案


noexpect(true)默认情况下,除非明确指定,否则析构函数是默认的,或者除非基类或成员的析构函数可以抛出。后者是你的情况。之后,它是函数签名之间的简单不匹配。

virtual ~A() {}因而实际上是virtual ~A() noexcept {}哪一个不匹配virtual ~B() noexcept(false) {}

你有两个解决方案:

  1. 显式标记~Bnoexcept(true),但如果~MyObject抛出,程序将在~B' 边界处终止。
  2. 马克~Anoexcept(false)

从析构函数中抛出是一个非常糟糕的主意。抛出对象不能被破坏的信号,这真的是你的代码中发生的事情吗?仅当正确的响应是立即终止程序时才抛出,因为如果将析构函数作为堆栈展开的一部分调用,就会发生这种情况。在这种情况下,不会调用其他析构函数,这可能会比不死对象造成更大的伤害。

如果您真的想要安全并且不关心抛出的异常,您可以unique_ptr使用吸收析构函数将成员包装在 a 中:

class B : public A {
public:
    B() : A(), _object{new MyObject,deleter} {}
     ~B()  noexcept(true) {}

private:
    constexpr static auto deleter = [](MyObject* obj){ try { delete obj;}catch(...){};};
    std::unique_ptr<MyObject,decltype(deleter)> _object;
};

推荐阅读