Reputation: 3625
I have created a class and I want not to create the object if one member is empty. These are the code lines:
#include "verification/CVerifObj.hpp"
VerifObj::VerifObj(const fs::path& imgNameIn)
{
m_image = cv::imread(imgNameIn.string());
AnalyseCSV csv;
m_plateContour = csv.getPlateRegion(imgNameIn); // search for the csv file and load the values
if (m_plateContour.empty()) // throw exception if empty csv
{
throw EmptyContourException(imgNameIn.string());
}
m_imageName = imgNameIn.string();
}
VerifObj::~VerifObj()
{
// are these enough for destructor?
m_image.release();
m_plateContour.clear();
}
Is this OK, or shall I do something more? How shall I be sure that if the exception is thrown the object is not created?
I have the following lines of code for ensuring it:
for(fs::directory_iterator iter(folderIn); iter != end; ++iter)
{
if(!fs::is_regular_file(iter->status()))
{
continue;
}
std::string extension = iter->path().extension().string();
if(targetExtensions.find(extension) == targetExtensions.end())
{
continue;
}
try
{
VerifObj verifObj(iter->path());
verifObjVecRet.push_back(verifObj);
}
catch (EmptyContourException& ece)
{
continue;
}
catch (CSVException &csve)
{
continue;
}
}
Upvotes: 3
Views: 5973
Reputation: 7881
The short answer is, throwing stuff in the constructor is dangerous.
First, lets define the design problem: you have a class that can fail to initialize. If the class fails to initialize, it cannot be used (will cause additional errors if used), so the class failing is considered a "critical failure", atleast where the class is concerned.
In a nutshell, what we want to avoid is letting a user use a class that failed to initialize.
Imagine the following situation:
class A
{
public:
A()
{
stuff1 = malloc(100);//some dynamic memory allocation
throw "This throw is crazy";
stuff2 = malloc(100);//some dynamic memory allocation
}
~A() {free(stuff1); free(stuff2);}
private: void* stuff2;void* stuff2;
};
int main(int argc, char** argv)
{
A a;
}
Once you throw in the constructor, what happens to stuff? well, it is an instant memory leak. The destructor is never called. Which is baaaad. If you handle the exception:
int main(int argc, char** argv)
{
try
{
A a;
}
catch(...)
{
//can't do much here
}
}
You've lost the reference to A, which is a nightmare. So some people try to get away with this instead (Up front, it is still bad)
int main(int argc, char** argv)
{
A* a;
try { a= new A();}
catch(...){delete a;}
}
But that is just as bad. You may still have a reference to a (and the memory directly pointed to by a will not leak) but a is now in an unknown state...you'll still have to delete it some day, and the free for stuff2 failed.
One way to get around this is to make your constructor and destructor alot smarter. Catch any exceptions that can be thrown in the constructor, and clean up the object, returning a "zombie" object. And have the destructor easily be able to check for the zombie object. I find this method to be more complicated.
A better way to some is to use an initializer:
class A
{
public:
A() {stuff1=null; stuff2=null;}
void init()
{
stuff1 = malloc(100);//some dynamic memory allocation
throw "This throw is crazy";
stuff2 = malloc(100);//some dynamic memory allocation
}
void destroy() {if (stuff1) {delete stuff1; stuff1=NULL;} if (stuff2) {delete stuff2; stuff2=NULL;}}
~A() {destroy();}
};
I don't like this because you still get "zombie" objects, and have all the added upkeep of calling init and destroy. Going back to the original problem, both a smarter constructor and providing initalizers still don't solve the nutshell statement: the end user can still (pretty easily, in fact) use an instance that is in an unknown state.
I like to follow a pseudo RAII (Resource Acquisition Is Initialization) here: what you want to to ensure that, if you have a reference to an object, it is a valid reference. Else, there should be no memory leaks. The best way (IMHO) to achieve this is to use a factory method, and keep all the initializes private.
class A
{
public:
~A() {destroy();}
static A* MakeA()
{
A* ret = new A();
try { ret->init();}
catch(...)
{
delete ret;
return NULL;
}
return ret;
}
private: void* stuff1,*stuff2;
A() {stuff1=null; stuff2=null;}
void init()
{
stuff1 = malloc(100);//some dynamic memory allocation
throw "This throw is crazy";
stuff2 = malloc(100);//some dynamic memory allocation
}
void destroy() {if (stuff1) {delete stuff1; stuff1=NULL;} if (stuff2) {delete stuff2; stuff2=NULL;}}
};
Now the user can never make a valid reference to a failed object, which is nice.
Protips:
Edit: clarified what I'm lazy about
Upvotes: 2
Reputation: 254431
Hopefully, m_image
and m_plateContour
(and any other non-trivial members of your class) are properly designed RAII types, with destructors that clean up any resources they might have.
In that case, your class won't need a destructor at all, and all the members will be properly destroyed automatically if your constructor throws - there's no need to take any action there.
However, the presence of the destructor implies that they might be evil types which do need manually cleaning up before destruction. In that case, fix them.
If you can't fix them for some reason, you'll need m_image.release();
before throwing. You'll also need a large coffee supply, as classes like that will lead to long debugging sessions trying to fix memory leaks.
Upvotes: 8