好坏:在构造函数中调用析构函数

时间:2018-06-30 21:24:20

标签: c++ windows constructor destructor


中断:我实际上并不认为这是同一个问题,另一个问题是关于手动调用析构函数的一般性问题。这是在类本身内部的创建过程中。仍然想知道执行此操作时会发生什么,如下面的问题所述。


起初,我认为这很糟糕,真的很糟糕。只需分析这两个人编写的构造函数的这段代码(请参见下文),并需要将其转换为Delphi对象Pascal。它的行为必须与C版本相同。我不喜欢这种样式,非常丑陋,但是没关系。

另一件事,在代码的两个阶段,失败时会调用析构函数(我想关闭连接,但是删除后会自动调用析构函数,为什么还是要这样做?)。我认为这不是做到这一点或错过某些东西的方法吗?

此外,在调用析构函数之后,他们想抛出一个异常(是吗?),但是我认为这将永远不会执行,并且在您手动访问或删除它时会导致另一个异常。


  Cat Time(Secs)
1 36  21
2 26R 18733 # (=1+18732), 18732 secs to be added = total Sec from midnight till 05:12:12
3 99  18750 # (=12+18738), 18738 secs to be added = total Sec from midnight till 05:12:18
4 ..  ..

下一个问题,执行此代码并调用析构函数时,内存中将实际发生什么?我无法执行和调试它。

3 个答案:

答案 0 :(得分:3)

此代码难看但合法。当从构造函数引发异常时,永远不会调用相应的析构函数。因此,需要在抛出之前手动调用它,以防止资源泄漏。真正的错误是 not 在其他情况下在引发异常之前手动调用析构函数。

当然,更好的方法是使用一个单独的RAII对象来封装commHandle。具有自定义删除程序的unique_ptr可以担任此角色。

在低级库之外的任何析构函数都是现代C ++中的代码味道。

答案 1 :(得分:3)

好吧,让我们从一个显而易见的观点开始:不要以这种方式编写代码。我可以看到他们为什么这样做-手动调用析构函数是在引发该异常之前进行清理的便捷方法,但是为什么这样的想法不好?

好吧,析构函数通常仅在构造函数运行完成时才调用(因此,按正常的方式,如果构造函数抛出异常,则不会运行),这是故意的它允许析构函数假定对象已完全初始化。任何试图破坏尚未完全初始化的对象的复杂性的析构函数都可能会遇到麻烦。

现在,在编写的代码 中,这都不重要,因为我们这里只有一个锡罐析构函数,它只是关闭了句柄,因此在这里,代码确实正确扔前清理(有时,谢谢尤金),我们都可以坐下来放松。但是,作为一种编程模式,它会发臭,并且,既然您知道它的实际作用,那么在将其移至Delphi时应该整理一下。

因此,请讲讲一些细节(不分先后顺序):

  • 当您手动调用析构函数时,就像调用任何其他函数一样,它会被执行并返回并继续存在。具体来说,对象本身不会被释放。在使用展示位置new时,这样做很有价值。
  • 从上面可以得出,对throw 的调用将在析构函数返回之后执行(无论如何,还是如此)。
  • 仅重复一次,当构造函数抛出时,不调用析构函数。我相信,该对象将随后被释放,在异常被捕获之前(如果有的话)。

如果您要转换的其余代码都是用这样的短促的方式编写的,那么我不会羡慕您。构造函数无论如何应该不会失败,在一般情况下,一旦对象启动并运行,只需使用单独的方法打开端口即可。

答案 2 :(得分:1)

从构造函数中抛出时,它将调用到目前为止构造的任何对象的析构函数:成员变量和继承的类(第15.2 / 2节)。

  
      
  1. 任何存储期限的对象(其初始化或破坏都被异常终止)将对其所有完全构造的子对象执行析构函数
  2.   

如果您手动调用析构函数,它们的析构函数也将被称为(第12.4 / 8节)。

  
      
  1. 执行析构函数的主体并销毁主体中分配的所有自动对象后,   X类的析构函数调用X的直接非变量非静态数据成员的析构函数,这些析构函数   X的直接基类...
  2.   

因此,成员变量的析构函数将被调用两次。形式上,两次调用析构函数都是未定义的行为。 (如果它们都具有空的析构函数,则可以摆脱它。)

如果您确实需要一个干净的解决方案,请将需要清理的部分包装到一个类中,并将其作为成员变量。从中调用初始化,如果抛出,则可以保证将其清除。 应用RAII甚至会获得荣誉点。