为什么要在c中将指针转换为双指针?

问题描述

我正在用C读取OOP,并且有以下标头:

#ifndef _NEW_H
#define _NEW_H

#include <stdarg.h>
#include <stddef.h>
#include <assert.h>

void *new (const void *type,...);
void delete (void *item);

typedef struct
{
    size_t size;
    void *(*ctor)(void *self,va_list *app);
    void *(*dtor)(void *self);
    void *(*clone)(const void *self);
    int (*differ)(const void *self,const void *x);
} class_t;

inline void *new (const void *_class,...)
{
    const class_t *class = _class;
    void *p = calloc(1,class->size);

    assert(p);
    *(const class_t **)p = class; //why would you cast to double pointer,when you immediately dereference it?

    if (class->ctor)
    {
        va_list args;
        va_start(args,_class);
        p = class->ctor(p,&args);
        va_end(args);
    }
    return p;
}

#endif //_NEW_H

现在我不明白这个表达:

*(const class_t **)p = class;

const class_t **类型是什么意思?它就像一个数组的数组?但是,如果我要使用自定义类(即,不仅是指向结构class_t的指针,而是更多的“公共”方法),则整个类不是类型为class_t的数组。那么,为什么要将空指针转换为双指针并立即取消引用呢?我应该怎么理解?

书中关于该声明的

* (const struct Class **) p = class;

p指向对象的新存储区的开头。我们 强制进行p转换,将对象的开头视为a 指向结构class_t的指针,并将参数类设置为的值 这个指针。

解决方法

calloc()的调用用于分配一个数组,该数组包含1个未指定“类”类型的元素,其中该类型的第一个成员应为class_t*指针(因此,{ {1}}必须至少为class->size,但可以更高。可能会使用sizeof(class_t*)而不是calloc(),只是将malloc()表示的任何其他数据成员都初始化为零,否则将需要显式的class->size。>

奇怪的强制转换+取消引用只是为了使代码可以将输入的memset()指针直接存储到该分配对象的第一个class成员中。

可以使用双指针访问数组。取消引用此类指针可为您提供数组中第一个元素的地址。在这种情况下,该地址也恰好与class_t*成员相同。

用OOP术语来说,对象在内存中的布局通常从指向对象类的vtable的指针开始,该指针包含指向该类的“虚拟”方法的函数指针的列表。当“派生”一个类时,后代只需将对象的vtable指针设置为新的函数指针列表,即可“覆盖”虚拟方法。 OOP的概念在C中并不真正存在,但对C ++来说却是基础。在C语言中,必须手动实现,这是此代码的作用。

基本上,代码正在为分配的对象分配此内存布局:

           ------------    --------------------
void *p -> | class_t* | -> | size_t size       |
           ------------    --------------------
           | ...      |    | void (*ctor)()   |
           ------------    --------------------
                           | void (*dtor)()   |
                           --------------------
                           | void (*clone)()  |
                           --------------------
                           | void (*differ)() |
                           --------------------

完成相同分配的另一种方法是对“类”类型使用typedef以便于访问,例如原始代码与此等效:

class_t*

完全不使用任何typedef或强制转换,typedef struct { class_t *vtable; // other data members,if class->size > sizeof(class_t*) ... } class_info_t; inline void *new (const void *_class,...) { const class_t *class = _class; class_info_t *p = (class_info_t*) calloc(1,class->size); assert(p); p->vtable = class; // other data members are implicitly zeroed by calloc() ... ... } 可用于完成相同的任务,例如:

memcpy()
,

const class_t ** type?

是什么意思

它是指向指针的指针。它表示所指向的实际上是另一个指针。第二个指向类型的具体对象,在这种情况下为class_t

[type] ---> [2nd pointer] ---> [class_t object]

为什么要*(const class_t **)p = class;

这种奇怪的构造是将class放在“ class_t对象”所在的位置。现在,看看应该如何使用此new函数并不是那么奇怪。

new是任何自定义类型(结构)的通用构造函数。但是,对类型的要求是,结构必须包括指向class_t的指针作为第一成员。这是为了启用多态性,基本上是C ++在后台通过v-table指针进行的操作。

现在,如果要定义自定义类型,我会做:

struct Foo {
    void *class_t;
    // whatever members I need for my type
};

如何使用它?

现在,定义类型后,还有另一件事要做。请注意,new函数作为参数使用指针inline void *new (const void *_class,...),该指针用作正在创建的指针的基础const class_t *class = _class;。这就意味着您在创建内容时需要传递一些内容-那么意义何在?

嗯,有一种技巧可以在其标头中定义指向 type 的const指针,该指针可用于构造此类型的对象。

在foo标头中:

struct Foo {
    void *class_t;
    // whatever members I need for my type
};

static const struct class_t _Foo = { 
sizeof (struct Foo),// and other functions such ad ctor,dtro,etc...
}; 

const void *Foo = &_Foo;

上面定义了一个自定义类型以及一个矩阵,以创建此类型的所有对象。重要的是要注意,它的类型为class_t,并且记录了Foo的大小。

最后,Foo的新对象创建如下:

void *f = new(Foo);

Foo的类型为class_t,但我们希望它为FooFoo结构的第一个元素是指向class_t的指针,因此,为了创建这种关系,需要在new

内部使用双指针。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...