OOC:en:2.1 Constructors and Destructors
来自 ChinaUnix Wiki
Let us implement a simple string data type which we will later include into a set. For a new string we allocate a dynamic buffer to hold the text. When the string is deleted, we will have to reclaim the buffer.
new() is responsible for creating an object and delete() must reclaim the resources it owns. new() knows what kind of object it is creating, because it has the description of the object as a first parameter. Based on the parameter, we could use a chain of if statements to handle each creation individually. The drawback is that new() would explicitly contain code for each data type which we support.
delete(), however, has a bigger problem. It, too, must behave differently based on the type of the object being deleted: for a string the text buffer must be freed; for an object as used in chapter 1 only the object itself has to be reclaimed; and a set may have acquired various chunks of memory to store references to its elements.
We could give delete() another parameter: either our type descriptor or the function to do the cleaning up, but this approach is clumsy and error-prone. There is a much more general and elegant way: each object must know how to destroy its own resources. Part of each and every object will be a pointer with which we can locate a clean-up function. We call such a function a destructor for the object.
Now new() has a problem. It is responsible for creating objects and returning pointers that can be passed to delete(), i.e., new() must install the destructor infor- mation in each object. The obvious approach is to make a pointer to the destructor part of the type descriptor which is passed to new(). So far we need something like the following declarations:
struct type {
size_t size; /* size of an object */
void (* dtor) (void *); /* destructor */
};
struct String {
char * text; /* dynamic string */
const void * destroy; /* locate destructor */
};
struct Set {
... information ...
const void * destroy;
};
It looks like we have another problem: somebody needs to copy the destructor pointer dtor from the type description to destroy in the new object and the copy may have to be placed into a different position in each class of objects.
Initialization is part of the job of new() and different types require different work — new() may even require different arguments for different types:
new(Set); /* make a set */ new(String, "text"); /* make a string */
For initialization we use another type-specific function which we will call a construc- tor. Since constructor and destructor are type-specific and do not change, we pass both to new() as part of the type description.
Note that constructor and destructor are not responsible for acquiring and releasing the memory for an object itself — this is the job of new() and delete(). The constructor is called by new() and is only responsible for initializing the memory area allocated by new(). For a string, this does involve acquiring another piece of memory to store the text, but the space for struct String itself is allocated by new(). This space is later freed by delete(). First, however, delete() calls the des- tructor which essentially reverses the initialization done by the constructor before delete() recycles the memory area allocated by new().
