构造函数抛出异常结束?是否存在内存泄漏?

3

我正在阅读这篇关于it技术的文章,其中提到:

注意:如果构造函数以抛出异常结束,则与对象本身相关联的内存将被清理 - 没有内存泄漏。例如:

void f()
{
X x; // If X::X() throws, the memory for x itself will not leak
Y* p = new Y(); // If Y::Y() throws, the memory for *p itself will not leak
}

我很难理解这个问题,希望有人能够澄清一下。我尝试了以下示例,它表明如果构造函数内发生异常,则析构函数不会被调用。

struct someObject
{
    someObject()
    {
      f = new foo();    
      throw 12;
    }
    ~someObject()
    {
        std::cout << "Destructor of someobject called";
    }

    foo* f;
};

class foo
{
public:
    foo()
    {
            g = new glue();
            someObject a;
    }
    ~foo()
    {
        std::cout << "Destructor of foo";
    }
 private:
  glue* g;
};


int main()
{
    try
    {
        foo a;
    }
    catch(int a)
    {
        //Exception caught. foo destructor not called and someobject destrucotr not called.
       //Memory leak of glue and foo objects
    }
}

我该如何解决这个问题?

很抱歉更新可能带来的不便。


析构函数没有被调用,因为对象从来没有正确地构造过。 - Some programmer dude
2个回答

3

由于对象在构造函数抛出异常后仍未被视为已构造,因此析构函数不会被调用。

对象的分配和构造(仅仅是销毁)是不同的事情。

在抛出异常之前使用 new()分配的任何对象都将泄漏。

除非您确实需要并且对自己所做的事情非常,非常,非常确定,否则不应自行管理这些资源。

最好使用标准动态内存管理库中适当的智能指针来管理类成员。


但是难道不会有胶水和foo对象的内存泄漏吗? - Rajeshwar
感谢您澄清这一点。再次抱歉更新可能带来的不便。 - Rajeshwar

3

当构造函数抛出异常时,将不会调用析构函数。当构造函数内部抛出异常时,需要注意以下几点,以便正确处理在中止对象的构造过程中可能发生的资源分配:

  • 正在构建的对象的析构函数将不会被调用。
  • 该对象类中包含的成员对象的析构函数将被调用。
  • 正在构建的对象的内存将被释放。

欢迎来到社区!您在答案中使用引用标记和链接到当前问题。可能链接和引用都不是必需的。您可以通过答案下方的“编辑”链接编辑您的答案。 - Valentin P.
1
需要澄清的是,对于在抛出异常之前完全构造的类成员,只有调用它们的析构函数。在构造函数体内使用new分配的成员必须手动进行delete操作,因为它们的析构函数不会自动被调用。这意味着捕获异常、释放内存,然后重新抛出异常。或者使用智能封装器来持有分配的内存,直到构造函数准备优雅地退出。 - Remy Lebeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接