I was fighting with this problem for 2 days. I have a workaround but I want to understand what happens more. So let’s begin.
I have very Primitive exception class that holds an error message as a pointer to array of chars (I know about profit of std::string). I know the “rule of three” so it looks like:
globalexceptions.hpp
class FatalError {
public:
const char* errorMessage;
/* WARNING:
* "Rule of three". You see it below.
*/
FatalError(const char* pErrorMessage);
FatalError(const FatalError& rhs);
FatalError& operator=(const FatalError& rhs);
~FatalError();
};
globalexceptions.cpp
FatalError::FatalError(const char *pErrorMessage):
errorMessage(pErrorMessage)
{}
FatalError::FatalError(const FatalError& rhs){
char* buf = new char[strlen(rhs.errorMessage)+1];
strcpy(buf, rhs.errorMessage);
errorMessage = buf;
}
FatalError& FatalError::operator =(const FatalError& rhs){
if (this == &rhs)
return *this;
delete[] errorMessage;
char* buf = new char[strlen(rhs.errorMessage)+1];
strcpy(buf,rhs.errorMessage);
errorMessage = buf;
return *this;
}
FatalError::~FatalError(){
delete[] errorMessage;
}
But when throwing:
int Config::readConfig(int argc_p, char *argv_p[])
{
if ( argc_p != 2 )
{
throw FatalError ("Sick usage. Try: <file.ini>\n");
}
I get “SIGABRT”.
Some valgrind analysis:
Invalid free() / delete / delete[] / realloc()
in FatalError::~FatalError() in globalexceptions.cpp:26
Address 0x413980 is not stack'd, malloc'd or (recently) free'd 1: operator delete[](void*) in /tmp/buildd/valgrind-3.7.0/coregrind/m_replacemalloc/vg_replace_malloc.c:490
2: FatalError::~FatalError() in <a href="file:///home/gumba/Projects/cpp/backup_helper/backup_helper-build-desktop-Qt_4_8_2_in_PATH__System__Debug/../backup_helper/globalexceptions.cpp:26" >globalexceptions.cpp:26</a>
3: /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17
4: main in <a href="file:///home/gumba/Projects/cpp/backup_helper/backup_helper-build-desktop-Qt_4_8_2_in_PATH__System__Debug/../backup_helper/main.cpp:35" >main.cpp:35</a>
I’ve done some research, and found the following information and advices:
- (OK: Catch by reference) “Technically, even when you catch an exception by reference, the compiler still uses pass by value. This is due to the fact that a catch never returns control to the caller, and is thus responsible for clean-up”
- (OK: Have copy constructor) “Objects that are thrown must have a publicly accessible copy-constructor. The compiler is allowed to generate code that copies the thrown object any number of times, including zero. However even if the compiler never actually copies the thrown object, it must make sure the exception class’s copy constructor exists and is accessible”
According to GDB the copy constructor is not called. SIGABRT happens on delete[] errorMessage. I don’t understand why. errorMessage seems to be properly initialized.
What is the reason of SIGABRT?
Thanks!
The problem is here:
You shouldn’t just store that
const char*, you should allocate enough size in yourerrorMessagemember, and copy it there. Otherwise, in the destructor, you’ll bedeleteing the address of the string literal, which will lead to undefined behaviour.Anyway, you shouldn’t use pointers here. Just use
std::string, which handles memory for you:No need to implement copy assignment operator, copy constructor, nor destructor.
A better solution would be to use
std::runtime_error, which is exactly the same as you implemented, and it’s provided in the standard library: