Try / Catch block replaces method block in destructor

Recently, I was instructed to detect a memory leak in part of our code. The leak was in the destructor for a particular object ... and I found something really strange. A former colleague wrote this:

File::~File() try { Clear(); } catch (...) { Log("caught exception"); } 

A file class inherits from some base classes. My first question is: is this strictly legal C ++? It compiles in Visual Studio 2008, but I showed it to a few friends / co-workers, and they were pretty terrified that it worked.

Actually, this does not work as intended: the base class inherited by this object has a destructor that is now never called (unlike the fact that you simply wrapped the destructor in a regular block of the method, having try / catch as part of this method )

Can someone take a hit explaining why this is allowed and why the base class destructor was not called? The destructor did not rush here.

+6
source share
2 answers

This is a try block function, and it is completely legal.

See, for example, here .

The only time you can do something in a function, try to block what you cannot do in a regular try block in a function, this is a catch exception expressed by an expression in the list of constructor initializers (and even then you should end up throwing something- then), but this is not applicable here.

This GOTW # 66 is especially interesting, although it focuses more on designers. It contains this "moral":

Since destructors should never throw an exception, the destructor-function-try-blocks has no practical use at all.

To add clarification, the code as written will throw any exception due to ISO / IEC 14882: 2003 15.3 [except.handle] / 16:

Exception An exception is thrown if the control reaches the end of the handler function of the constructor or destructor block function. [...]

However, it is legal to have a parameterizable return in the handler of the try block function for the destructor - this is forbidden only in the try block function for the constructor - and this will suppress the repeated exception. Thus, any of these alternatives will prevent the exception from being thrown from the destructor.

 File::~File() try { Clear(); } catch (...) { Log("caught exception"); return; } 

 File::~File() { try { Clear(); } catch (...) { Log("caught exception"); } } 
+10
source

To answer the second part, β€œwhy the base class destructor was not called?”, 12.4 / 6:

After executing the body, the destructor and destruction of any automatic objects allocated within the body, the destructor for class X calls, destructors for Xs direct participants, destructors for Xs direct base classes ... The operator of returning (6.6.3) to the destructor cannot directly return to the caller ; before transferring caller management, destructors for members and grounds.

This does not mean that member destructors and bases are called if the destructor throws. However, 15.2 / 2 states:

An object that is partially built or partially destroyed will be destroyed by destructors for all of its fully constructed subobjects,

I think this should be true whether the object is "partially destroyed" due to an exception thrown from the body of the destructor, or because of an exception thrown from the try block of the try-destructor. I am sure that "after the body of the destructor" should also mean after the function tries to block.

Obviously, Microsoft does not agree with this, and because of the try block function, it did not generate a "destructor body" and did not do what happens after the "destructor body" is executed.

It doesn’t sound like that. GCC 4.3.4 executes the base class destructor, regardless of whether the dtor function of the derived class makes a lock attempt or not. In the case when it is thrown, the base is destroyed before the catch clause is executed.

+4
source

Source: https://habr.com/ru/post/887739/


All Articles