I am learning C, mainly by K&R, but now I have found an Object Oriented C pdf tutorial and am fascinated. I’m going through it, but my C skills/knowledge may not be up to the task.
This is the tutorial: http://www.planetpdf.com/codecuts/pdfs/ooc.pdf
My question comes from looking at many different functions in the first couple of chapters of the pdf. Below is one of them. (page 14 of pdf)
void delete(void * self){
const struct Class ** cp = self;
if (self&&*cp&&(*cp)->dtor)
self = (*cp)->dtor(self);
free(self);
}
dtor is a destructor function pointer. But knowledge of this isn’t really necessary for my questions.
- My first question is, why is **cp constant? Is it necessary or just being thorough so the code writer doesn’t do anything damaging by accident?
- Secondly, why is cp a pointer-to-a-pointer (double asterisk?). The struct class was defined on page 12 of the pdf. I don’t understand why it can’t be a single pointer, since we are casting the self pointer to a Class pointer, it seems.
- Thirdly, how is a void pointer being changed to a Class pointer (or pointer-to-a-Class-pointer)? I think this question most shows my lack of understanding of C. What I imagine in my head is a void pointer taking up a set amount of memory, but it must be less than Class pointer, because a Class has a lot of “stuff” in it. I know a void pointer can be “cast” to another type of pointer, but I don’t understand how, since there may not be enough memory to perform this.
Thanks in advance
Interesting pdf.
It’s necessary so the writer doesn’t do anything by accident, yes, and to communicate something about the nature of the pointer and its use to the reader of the code.
Take a look at the definition of
new()(pg 13) where the pointerpis created (the same pointer that’s passed asselftodelete()):So, ‘p’ is allocated space, then dereferenced and assigned a pointer value (the address in class; this is like dereferencing and assigning to an int pointer, but instead of an int, we’re assigning an address). This means the first thing in p is a pointer to its class definition. However, p was allocated space for more than just that (it will also hold the object’s instance data). Now consider
delete()again:When cp is dereferenced, since it was a pointer to a pointer, it’s now a pointer. What does a pointer contain? An address. What address? The pointer to the class definition that’s at the beginning of the block pointed to by p.
This is sort of clever, because p’s not really a pointer to a pointer — it has a larger chunk of memory allocated which contains the specific object data. However, at the very beginning of that block is an address (the address of the class definition), so if p is dereferenced into a pointer (via casting or cp), you have access to that definition. So, the class definition exists only in one place, but each instance of that class contains a reference to the definition. Make sense? It would be clearer if p were typed as a struct like this:
Then you could just use something like
p->class->dtor()instead of the existing code indelete(). However, this would mess up and complicate the larger picture.A pointer is like an int — it has a small, set size for holding a value. That value is a memory address. When you dereference a pointer (via
*or->) what you are accessing is the memory at that address. But since memory addresses are all the same length (eg, 8 bytes on a 64-bit system) pointers themselves are all the same size regardless of type. This is how the magic of the object pointer ‘p’ worked. To re-iterate: the first thing in the block of memoryppoints to is an address, which allows it to function as a pointer to a pointer, and when that is dereferenced, you get the block of memory containing the class definition, which is separate from the instance data inp.