[continued from Deallocating objects vs. deallocating storage (Part 1)]
A delete-expression applied to a null pointer does nothing. If the pointer is non-null, the delete-expression applies the destructor to the soon-to-be-deleted object, and then deallocates the object's storage by passing the object's address to operator delete. (That first statement inside the body of the if-statement—an explicit destructor call—is something you can actually write in C++ but really shouldn't.)
Only class types have destructors, so if p is a pointer to an object of non-class type, then a delete-expression such as:
delete p;
translates into just:
operator delete(p);
In this case, the compiler need not generate the surrounding if-statement to guard against a null pointer; operator delete accepts null pointer arguments.
An array delete-expression uses a different function with the similar name operator delete [ ], pronounced as "operator delete square bracket" or just "operator delete bracket". The C++ standard refers to any function named either operator delete or operator delete [ ] as a deallocation function.
Each C++ environment provides a default implementation for a global operator delete [ ], declared as:
void operator delete [ ](void *p) throw ();
This function declaration is identical to that for operator delete, except for the function name. Using separate deallocation functions for arrays and non-arrays objects lets you use different memory management policies for each.
As mentioned earlier, the second step of an array delete-expression that deletes an array is a loop which applies the destructor to each array element in descending order by subscript (or address). For example, if widget is a class type and pw is a pointer to a widget, then the array delete-expression:
delete [ ] pw;
translates more-or-less into something like:
if (pw != NULL)
{
widget *p = pw + N;
while (p != pw)
(—p)->~widget();
operator delete [ ](pw);
}
The while-loop applies the destructor to each widget in that array of widgets, from the last element to the first. The subsequent call to operator delete [ ] deallocates the storage for the entire array.
In the code just above, N represents the array dimension (the number of elements in the array). Where, you ask, did that come from? As I explained in an earlier column, operator new [ ] often allocates an extra word or two in addition to the storage required for the array elements. It places the array dimension (or some information from which to compute the array dimension) into that extra storage so that operator delete [ ] can find it. That's where N comes from.
Again, only class types have destructors, so if p is a pointer to an array with elements of non-class type, then an array delete-expression such as:
delete [ ] p;
translates into just:
operator delete [ ](p);
Deallocating objects in C
I once showed that classes with constructors by using structs and functions can be approximated. You can easily extend the model to include destructors. For example, you can implement a C++ widget class as a C struct:
typedef struct widget widget;
struct widget
{
// widget data members go here
};
with a "destructor" implemented as a C function:
void widget_destroy(widget *w);
You can mimic the behavior of a C++ delete-expression by using a single inline function:
inline
void delete_widget(widget *w)
{
if (w != NULL)
{ widget_destroy(w);
free(w);
}
}
Then, if pw points to a dynamically-allocated widget, you can delete it by calling:
delete_widget(pw);
which is a pretty fair approximation for the C++ delete- expression:
delete pw;
Extending the approach to deleting arrays of widgets is a bit harder. You must augment the function that allocates the array to squirrel away the array dimension so that the delete function can find it. I'll show you the details in an upcoming column.
A correction
Alert reader Alan Edmonds noticed that my January column included a call to memset with the second and third arguments in the wrong order. The call as I wrote it was:
memset(&d1, sizeof(d1), 0);
It should be:
memset(&d1, 0, sizeof(d1));
Thanks, Alan.
Endnotes:
1. Saks, Dan. "Destroying everything in every path," Eetasia.com, March, 2011. http://forum.eetasia.com/BLOG_ARTICLE_6801.HTM
2. Meyers, Scott, Effective C++, 3rd Edition. Addison-Wesley, 2005. See Item 29.
文章评论(0条评论)
登录后参与讨论