【问题标题】:Throwing exception in derived class constructor. Why is base class destructor called but not derived class destructor?在派生类构造函数中抛出异常。为什么调用基类析构函数而不调用派生类析构函数?
【发布时间】:2015-07-18 18:44:08
【问题描述】:
#include <iostream>
class A
{
    public:
    A() 
    {   
        std::cout << "A()" << std::endl;
    }   

 virtual  ~A()
    {   
        std::cout << "~A()" << std::endl;
    }   
};

class B:public A
{
    public:
    B() 
    {   
        throw std::exception();
        std::cout << "B()" << std::endl;
    }   

   ~B()
    {   
        std::cout << "~B()" << std::endl;
    }   
};


int main()
{
    try 
    {   
        B b;
    }   
    catch(std::exception & e)  
    {   
    }   
    return 0;
}   

以上代码输出,

A()
~A()

当异常被抛出时,B 已经被创建。那么为什么不调用 B 的析构函数呢?

【问题讨论】:

  • 只调用成功构造对象的析构函数。换句话说,你错误地说B 在抛出异常时已经创建。见this

标签: c++ exception inheritance


【解决方案1】:

在构造函数返回之前,对象不会被认为是完全构造的,并且不会对不完整的对象调用析构函数。

【讨论】:

    【解决方案2】:

    卡尔顿给出了正确的答案。这种设计(即不对任何未完全构造的东西调用dector)具有重要的后果。任何 C++ 类只能包含 一个 受析构函数保护的资源。例如,这将不起作用:

    class Ab
    {
    public:
              char *m_buff1;
              char *m_buff2;
    
              Ab() { m_buff1 = malloc(100); Xyz(); m_buff2 = malloc(200); }
             ~Ab() { free(m_buff1); free(m_buff2); }
    };
    

    如果在 2 次分配尝试之间发生异常 - 无法安全地知道哪些字段已初始化,哪些未初始化。我在这里举一个简单的例子来说明这一点。请不要评论说可以先用 NULL 初始化字段。如果 malloc 之间发生异常 - 第一次分配将被简单地泄漏,因为不会调用 desctuctor。

    换句话说,最好避免从构造函数中抛出异常的设计。最好考虑有一个简单的构造函数来初始化数据字段和一个分配资源、打开连接等的单独方法。

    【讨论】:

    • “任何 C++ 类只能包含一个受析构函数保护的资源。”这不是真的。只是,如果在构造函数中分配了多个资源,则构造函数必须确保在发生异常时,所有已分配的资源都被正确释放。确保它的最佳方法是使用处理此问题的 RAII 类。 — “换句话说,最好避免从构造函数中抛出异常的设计。”我不同意。编写一个正确释放资源的构造函数与编写另一个正确释放资源的函数是一样的。
    • RAII 是正确的,它正在做我所说的,即它用一个类保护单个(!)资源。你是对的,可以在构造函数中使用 try 块来保护资源分配,但我不鼓励使用它。它太复杂了,而且通常比 RAII 更糟糕。
    • 您建议不要从构造函数中抛出,并认为您无法正确进行资源管理。我强烈反对。
    • 我建议不要这样做,因为这需要太大的努力,而不是因为这根本不可能。代码应该相当复杂。开发人员应该寻找简洁易懂的解决方案,而不是“现在就这样做”,然后再考虑更大的图景。
    • 使用 RAII 绝对不复杂,而且肯定是可能的。
    猜你喜欢
    • 2011-05-29
    • 2016-07-19
    • 2018-07-21
    • 2022-07-01
    • 2013-01-28
    • 2015-08-18
    • 2018-07-16
    • 2016-12-17
    • 1970-01-01
    相关资源
    最近更新 更多