I am re-reading some code from a while ago on C++ (I am learning Java in school right now), and I am a little confused as to when I must use delete
.
For example: When declaring two objects:
Fraction* f1;
Fraction* f2;
And creating f1
and f2
like this:
f1 = new Fraction(user_input1, user_input2);
f2 = new Fraction(user_input3, user_input4);
The next time I want to use new
operator to create a new object, do I have to delete
first? I am confused because I am used to having the garbage collector in java take care of objects and their deletion. Do I have to delete
before using new
again?
if (f1) delete f1;
if (f2) delete f2;
// initialize again...
The new and delete operators are designed to support explicitly managed lifetimes. Another common reason is that the size or structure of the "object" is determined at runtime. For simple cases (arrays, etc.)
Example 3: C++ new and delete Operator for Objects In main() , we have created a Student object using the new operator and use the pointer ptr to point to its address. The moment the object is created, the Student() constructor initializes age to 12 . We then call the getAge() function using the code: ptr->getAge();
There is nothing that requires a delete[] in the standard - However, I would say it is a very good guideline to follow. However, it is better practice to use a delete or delete[] with every new or new[] operation, even if the memory will be cleaned up by the program termination.
The main difference between new and delete operator in C++ is that new is used to allocate memory for an object or an array while, delete is used to deallocate the memory allocated using the new operator. There are two types of memory as static and dynamic memory.
Rather then telling you when to use delete
, I'll try to explain why you use pointers anyway. So you can decide when to use dynamic objects, how to use them and so when to call delete
(and not).
Rules of thumb:
delete
call needed.new
also write delete
somehwere at a suitable location (and make sure that is called).new
keyword there needs to be a delete
keyword. Otherwise you are taking all the resources the machine has resulting in applications to crash or just stop. Also it will make the system slower.Static creation of an object:
Fraction f1;
Dynamic creation of an object:
Fraction* f1;
Now you have this address to a memory block on the heap. It is an invalid one since you haven't assigned anything to it. Good practice would be - depending on where you declare it - to assign it a NULL
(windows) or 0
(cross-platform).
Fraction* f1 = 0;
When to use delete
As soon as you create a dynamic object, thus calling the new
operator, you need to call delete
somewhere.
int main()
{
Fraction* f1 = 0; // Good practise to avoid invalid pointers
// An invalid pointer - if( f1 ){ Access violation }
f1 = new Fraction(); // Could have done this at the previous line
/* do whatever you need */
if( f1 )
{
delete f1;
f1 = 0; // not needed since we are leaving the application
}
return 0;
}
In some scenarios it could be useful to have an array of Fraction
, or pointers to it. Using an int
for simplicity here, same as skipping error handling:
int arr[ 10 ];
int cur = -1;
int* Add( int fraction )
{
arr[++cur] = fraction;
return &arr[cur];
}
// Usage:
Add( 1 );
Add( 4 );
One thing happening here, no assignment to any memory through dynamic objects. They are freed automatically. The pointer returned by the function is a pointer to a static memory block.
When making the arr
as pointers to int
:
int* arr[ 10 ];
int cur = -1;
int* Add( int* fraction )
{
arr[++cur] = fraction;
return arr[cur];
}
// Usage:
int* test;
test = Add( new int( 1 ) );
test = Add( new int( 4 ) );
Now you have to memory blocks which are leaking since you have no clean up code.
When you call after each Add(...)
the delete test
, you have cleaned up the memory but you have lost the values you had stored within int* arr[ 10 ]
as they are pointing to the memory holding the value.
You can create another function and call this after you are done with those values:
void CleanUp()
{
for( int a = 0; a < 10; ++a )
delete arr[ a ];
}
Small usage example:
int* test;
int test2;
test = Add( new int( 1 ) );
test2 = *Add( new int( 4 ) ); // dereference the returned value
/* do whatever you need */
CleanUp();
Why do we want to use pointers:
int Add( int val )
{
return val; // indeed very lame
}
When you call a function that needs a parameter (type), you are not passing in the instance but rather a copy of it. In the above function you are returning a copy of that copy. It will amount to a lot of duplication all memory involved and you make your application tremendously slower.
Consider this:
class Test
{
int t;
char str[ 256 ];
}
If a function needs a type Test
, you are copying the int
and 256 chars. So make the function so it needs only a pointer to Test
. Then the memory the pointer is pointing to is used and no copying is needed.
int Add( int val )
{
val++;
return val;
}
Within this last example, we are adding 1 to the copy of val
and then returning a copy of that.
int i = Add( 1 );
result: i = 2;
void Add( int* val )
{
// mind the return type
*val++;
}
In this example you are passing the address to a value and then - after dereferencing - adding one to the value.
int i = 1;
Add( &i );
result: i = 2;
Now you have passed in the address to i
, not making a copy of it. Within the function you directly adding 1 to the value at that memory block. You return nothing since you have altered the memory itself.
Nulling/testing for valid pointers
Sometime you encounter examples such as:
if( p != 0 ) // or if( p )
{
/* do something with p */
}
This is just to check if the pointer p
is valid. However, an invalid address - thus not pointing to a memory you have reserved (the access violation) - will pass through too. For your code, an invalid pointer is a valid address.
Therefore, to use such a check you have to NULL
(or 0
) the pointer.
Fraction* f1 = 0;
When f1 == 0
, it doesn't point to anything otherwise it points to whatever it points to.
This is useful when you have a pointer in a 'main'-class which is or isn't created.
class Fraction
{
public:
int* basicFeature;
int* ExtendedFeature = 0; // NULL this pointer since we don't know if it
// will be used
Fraction( int fraction )
{
// Create a pointer owned by this class
basicFeature = new int( fraction );
}
Fraction( int fraction, int extended ) // mind the static
: Fraction( fraction )
{
// Create a pointer owned by this class
ExtendedFeature = new int( extended );
}
~Fraction()
{
delete basicFeature;
if( ExtendedFeature )
// It is assigned, so delete it
delete ExtendedFeature;
}
}
withing the constructors we are creating the two pointers, so within the destructor we are cleaning up those pointers. Only checking the ExtendedFeature
since this one may or may not be created. basicFeature
is always created.
You could replace the if
statement including its scope within the destructor by calling a new function: removeExtendedFeature()
where that function implementation would be:
Fraction::removeExtendedFeature()
{
if( ExtendedFeature )
{
// It is assigned, so delete it
delete ExtendedFeature;
// Now it is important to NULL the pointer again since you would
// get an access violation on the clean up of the instance of
// the class Fraction
ExtendedFeature = 0;
}
}
And the new destructor:
Fraction::~Fraction()
{
delete basicFeature;
removeExtendedFeature();
}
Another functionality of nulling could be:
int Fraction::getValue()
{
int result = *basicFeature;
if( ExtendedFeature )
result += *ExtendedFeature;
return result;
}
My apologies for the lame class Fraction, with an ever more lame extended feature. But as an example it would serve the purpose.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With