Reputation: 562
I have a C++ code mentioned below:
#include<iostream>
#include<stdexcept>
const long MAX = 10240000;
class Widget{
public:
Widget(){
ok = new int [1024];
prob = new int [100*MAX];
}
~Widget(){
std::cout<<"\nDtoR of Widget is called\n";
delete ok; ok = NULL;
delete prob; prob = NULL;
}
//keeping below public: Intentionally
int* ok;
int* prob;
};
void func(){
Widget* pw = NULL; // <<<<--------------- Issue in Here
try{
pw = new Widget;
}
catch(...){
delete pw;
std::cout<<"\nIn catch BLOCK\n";
if(pw->ok == NULL){
std::cout<<"\n OK is NULL\n";
}
throw;
}
delete pw;
}
int main(){
try{
func();
}
catch(std::bad_alloc&){
std::cout<<"\nError allocating memory."<<std::endl;
}
std::cout<<std::endl;
system("pause");
return 0;
}
Now in function func(), i am seeing two different behavior depending on if i do not set pointer 'pw' to NULL and if i do set 'pw' pointer to NULL (like code above). I was under the impression that it is 'good' practise to first set pointer to NULL and then initialize it. But when i initilize it to NULL then the output just shows "In catch BLOCK" and later app crashes. but if i do not set pw pointer to NULL, then i see destructor of pw is called and following output is shown w/o any app crash.
DtoR of Widget is called
In catch BLOCK
OK is NULL
Error allocating memory.
Press any key to continue . . .
My question is why such difference in one case when we are not initializing 'pw' pointer to NULL and in other case we are setting it to NULL. Why destructor is called in one case and why it was not called in another.
Note: The intent of this code is to throw bad_alloc exeption.
Upvotes: 2
Views: 226
Reputation: 17428
You are seeing the app crash when you set pw to NULL because of the line
if (pw->ok == NULL)
You are dereferencing NULL which is causing the crash. In the other case you are deleting garbage which will give you undefined behavior.
Also, you should not use a pointer after calling delete on it. That can cause all kinds of weird behavior.
To explain more of what is happening. Your Widget constructor is throwing the allocation exception. In this case most likely the allocation for ok
completed, but the allocation for prob
failed. Your constructor never finishes, leaking the memory that was allocated to the ok
variable. If you want to ensure the memory is cleaned up, you should add a try catch in your constructor.
Widget() : ok(NULL), prob(NULL)
{
try
{
ok = new int [1024];
prob = new int [100*MAX];
}
catch(...)
{
std::cout << "\nCtor of Widget threw exception\n";
delete [] ok;
delete [] prob;
throw;
}
}
Upvotes: 0
Reputation: 166
you are going to intend the bad_alloc exeption.but you have more unhandled exceptions! It is not ok to delete pw first and then using the pointer of it!
delete pw;
if(pw->ok == NULL)
Upvotes: 1
Reputation: 254431
In your catch block, you have:
if(pw->ok == NULL)
At this point, pw
is NULL
(or garbage, in the case that you didn't initialise it). pw-ok
attempts to dereference it, giving undefined behaviour (a crash in this case).
If you didn't initialise it, then the delete pw
will crash before printing the "catch" message; most likely, it will print the "Dtor" message before crashing, but there is no guarantee since you're firmly in the realm of undefined behaviour.
If you did initialise it, then delete pw
is unnecessary but harmless; deleting a null pointer is defined to do nothing. So in that case you won't crash until you dereference it.
In any event, you have an unfixable memory leak - the first allocation ok = new int[1024]
will have succeeded, but you have lost the only pointer to it. This is why you should always manage dynamic memory using smart pointers, containers, and other RAII techniques.
Upvotes: 4
Reputation: 2036
pw->ok
after you delete pw
? It's already gone.
Widget():ok(NULL), prob(NULL) {
...
}
because if Widget() fails, you don't know which member variable is initialized and which is not which can cause problem in your destructor.
int[]
, you need to delete[]
not just delete
in your destructor.Upvotes: 0
Reputation: 59
It's good to initialize pw
to NULL
, but when deleting it, you should first check if pw is not null. Something like:
if (pw) delete pw;
Also, if pw
is NULL
, you can't reference its members.
Upvotes: -1
Reputation:
If you do not set pw
to NULL
then you will leave it uninitialized. Then, when new
operator inside a "try" block throws an exception, it never returns, and you get into catch
block. Since new
never returned, pw
will still be not initialized, and you will pass a random address to delete
. That gives you an undefined behavior.
Upvotes: 6