lallous said:
I have a function like:
void fnc() {
char *mem1, *mem2, *mem3, *mem4;
// lots of code...
// mem1, 2, 3, 4 got allocated
// lots of code and condition checks
if (condition_failed)
{
// blah blah
// free mem1, mem2, mem3, mem4
return;
}
if (condition2_failed)
{
// blah blah
// free mem1, mem2, ...
return;
}
// here the end of routine (clean exit code):
// free mem1, mem2, mem3, mem4
}
Usually, I would use compiler specific solution by putting most of the code
in __try() and the clean exit code in _finally() block then to reach the
clean exit code I would invoke __leave.
Or use lables and then goto clean_exit
Any better way, other than goto or compiler specific solution?
The problem with code you posted is code duplication (= maintenance
headache, which will no doubt lead to bugs later) and that it is not
exception safe.
If it is just freeing char* that is bothering you, why not use the
std::string class?
#include <string>
void fnc()
{
std::string mem1, mem2, mem3, mem4;
// lots of code...
// lots of code and condition checks
if (condition_failed)
{
// blah blah
// No need to explicitly free mem1, mem2, mem3, mem4!
return;
}
if (condition2_failed)
{
// blah blah
// No need to explicitly free mem1, mem2, mem3, mem4!
return;
}
// No need to explicitly free mem1, mem2, mem3, mem4!
}
The advantage of this is not only that all memory used by the string is
automatically released, no matter how the function exits (return or
exception), but also that string manipulation with the std::string is much
easier to code and understand. Similary the std::vector class is an
excellent replacement for dynamically created arrays; also with this class
there is no need to worry about explicit memory management.
For other tasks that need to be done when a functions exits, consider using
the RAII idiom
(
http://c2.com/cgi/wiki?ResourceAcquisitionIsInitialization). An example of
RAII is the std::auto_ptr class. The basic idea of the RAII idiom is to
create a local object which has a destructor that perfoms the operations
necessary when the function is exited (the destructor essentially replaces
the non-standard _finally clause). Since the destructor of local objects is
always called when the function exists, the destructor can perfom the
things you now do in the non-standard _finally() clause.
#include <iostream>
class AutoCleanup
{
public:
AutoCleanup();
~AutoCleanup()
{
std::cout << "AutoCleanup dtor" << std::endl
}
}
void fnc()
{
AutoCleanup tmp;
// lots of code and condition checks
if (condition_failed)
{
// The return statement causes
// AutoCleanup::~AutoCleanup to be called
return;
}
if (condition2_failed)
{
// The return statement causes
// AutoCleanup::~AutoCleanup to be called
return;
}
// When the function finishes
// AutoCleanup::~AutoCleanup will be called
}
Also when a exception is thrown the AutoCleanup::~AutoCleanup will be
called. The RAII idiom is a very handy technique which usefullness is not
restricted to releasing memory or other resources. For example for UI code
you could make a class the changes the mouse cursor to a hourglass on
construction and restores the previous mouse cursor on destruction:
class HourGlassCursor
{
public:
HourGlassCursor() : prev_(getCursor())
{
setCursor(crHourGlass);
}
~HourGlassCursor()
{
setCursor(prev_);
};
private:
Cursor prev_;
};
void calculate()
{
HourGlassCursor hgc;
// do some lengthy processing...
}
The cursor will always be restored, even if an exception is thrown.