有趣的pdf。
我的第一个问题是,为什么**cp是常数?这是必要的还是只是为了防止代码编写者意外地做出任何有害行为?
是的,这是为了防止编写者意外地做出任何事情,并向代码的读者传达指针及其使用性质的某些信息。
其次,为什么cp是指向指针的指针(双星号)?结构类在pdf的第12页上定义。我不明白为什么它不能是单个指针,因为我们似乎将自指针强制转换为类指针。
看一下new()
的定义(第13页),其中创建指针p
(作为self
传递给delete()
的同一指针):
void * new (const void * _class, ...)
{
const struct Class * class = _class;
void * p = calloc(1, class —> size);
* (const struct Class **) p = class;
因此,'p'被分配空间,然后被取消引用并赋值一个指针值(类中的地址;这就像取消引用和赋值给int指针一样,但我们不是给int赋值,而是给一个地址赋值)。
这意味着p中的第一件事是指向其类定义的指针。然而,p被分配的空间不仅仅是用于此目的(它还将保存对象的实例数据)。现在再考虑一下
delete()
:
const struct Class ** cp = self;
if (self&&*cp&&(*cp)->dtor)
当cp被引用时,由于它是指向指针的指针,所以现在它是一个指针。指针包含什么内容?一个地址。是什么地址?指向p所指向的块开头处的类定义的指针。
这有点聪明,因为p不是真正的指向指针--它分配了更大的内存块,其中包含特定的对象数据。然而,在该块的开头处有一个地址(类定义的地址),因此,如果p通过转换或cp被解除引用成指针,您就可以访问该定义。因此,类定义只存在于一个地方,但该类的每个实例都包含对该定义的引用。听起来有道理吗?如果p被声明为struct,将更清晰:
struct object {
struct class *class;
[...]
};
然后,您可以使用像
p->class->dtor()
这样的东西来代替
delete()
中的现有代码。但是,这会破坏并复杂化更大的图像。
第三个问题是,如何将void指针更改为Class指针(或指向Class指针的指针)?我认为这个问题最能显示出我对C的理解不足。我脑海中想象的是,void指针占用一定量的内存,但它必须比Class指针少,因为Class中有很多“东西”。
指针就像int一样--它具有小的、固定的大小来保存一个值。该值是一个内存地址。当您通过
*
或
->
解除引用指针时,您访问的是该地址处的内存。但是,由于内存地址都具有相同的长度(例如,在64位系统上为8字节),指针本身的大小与类型无关。这就是对象指针“p”的魔法之处。再次强调:指针
所指向的内存块中的第一件事是一个地址,这使它可以作为指向指针的指针,当对其进行解引用时,您会得到包含类定义的内存块,该内存块与p
中的实例数据分开。