Interesante pdf.
Mi primera pregunta es, ¿por qué ** cp constant? ¿Es necesario o simplemente ser minucioso para que el escritor del código no haga nada dañino por el accidente ?
Es necesario por lo que el escritor no hace nada por accidente, sí, y para comunicar algo acerca de la naturaleza del puntero y su utilidad para el lector del código.
En segundo lugar, ¿por qué cp es un puntero-a-un-puntero (asterisco doble?). La clase de estructura se definió en la página 12 del pdf. No entiendo por qué no puede ser un solo puntero, ya que estamos lanzando el puntero a un puntero de clase, al parecer.
Echa un vistazo a la definición de new()
(pg 13) donde se crea el puntero p
(el mismo puntero que se pasa como self
-delete()
):
void * new (const void * _class, ...)
{
const struct Class * class = _class;
void * p = calloc(1, class —> size);
* (const struct Class **) p = class;
Por lo tanto, se asigna 'p' espacio, luego desreferenciado y asignado un valor de puntero (la dirección en la clase; esto es como desreferenciar y asignar a un puntero int, pero en lugar de un int, estamos asignando una dirección). Esto significa que lo primero en p es un puntero a su definición de clase. Sin embargo, a p se le asignó espacio para algo más que eso (también mantendrá los datos de instancia del objeto). Consideremos ahora de nuevo delete()
:
const struct Class ** cp = self;
if (self&&*cp&&(*cp)->dtor)
Cuando se eliminan las referencias cp, ya que era un puntero a un puntero, que es ahora un puntero. ¿Qué contiene un puntero? Una dirección. ¿Que direccion? El puntero a la definición de clase que está al comienzo del bloque al que apunta p.
Esto es algo inteligente, porque p no es realmente un puntero a un puntero: tiene una gran cantidad de memoria asignada que contiene los datos específicos del objeto. Sin embargo, al comienzo de ese bloque hay una dirección (la dirección de la definición de la clase), de modo que si p se desreferencia en un puntero (a través de casting o cp), tiene acceso a esa definición. Entonces, la definición de clase existe solo en un lugar, pero cada instancia de esa clase contiene una referencia a la definición. ¿Tener sentido?Sería más claro si p se escribe como una estructura como esta:
struct object {
struct class *class;
[...]
};
Entonces usted podría utilizar algo como p->class->dtor()
en lugar del código existente en delete()
. Sin embargo, esto podría estropear y complicar la imagen más grande.
En tercer lugar, ¿cómo es un puntero nulo se cambió a un puntero de clase (o puntero-a-una-clase-puntos)? Creo que esta pregunta muestra mi falta de comprensión de C. Lo que imagino en mi cabeza es un puntero de vacío ocupando una cantidad determinada de memoria, pero debe ser menor que el puntero Clase , porque una Clase tiene una gran cantidad de "cosas" en eso.
Un puntero es como un int: tiene un tamaño pequeño y fijo para contener un valor. Ese valor es una dirección de memoria. Cuando desreferencia un puntero (a través de *
o ->
), lo que está accediendo es la memoria en esa dirección. Pero dado que las direcciones de memoria son todas de la misma longitud (por ejemplo, 8 bytes en un sistema de 64 bits), los punteros son del mismo tamaño independientemente del tipo. Así es como funcionaba la magia del puntero del objeto 'p'. Para volver a iterar: lo primero en el bloque de memoria p
señala a es una dirección, que le permite funcionar como un puntero a un puntero, y cuando se desreferencia, se obtiene el bloque de memoria que contiene la definición de clase, que está separado de los datos de instancia en p
.
@Joe Absolutamente nada de malo en eso. Las buenas habilidades de programación C son tan valiosas y difíciles de encontrar en estos días de escrituras rápidas. – jman
@Joe - Supongo que significa ANSI C89, que el último libro de K & R discutió. el código en el ejemplo no es K & R C IIRC. – Flexo
Estoy leyendo K & R ANSI (creo que desde 1989, como dijiste). El ejemplo del código proviene del OOC pdf –