凯发app官方网站-凯发k8官网下载客户端中心 | | 凯发app官方网站-凯发k8官网下载客户端中心
  • 博客访问: 134
  • 博文数量: 1
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2024-09-08 12:42
文章分类
文章存档

2024年(1)

我的朋友
最近访客
相关博文
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·

分类: linux

2024-09-08 12:43:34

qemu对象模型——qom实现分析-凯发app官方网站

——lvyilong316

qemu提供了一套面向对象编程的模型——qom,即qemu object module,几乎所有的设备如cpu、内存、总线等都是利用这一面向对象的模型来实现的。qom模型的实现代码位于qom/文件夹下的文件中。

那么qemu为什么要引入qom这个概念呢?首先,qemu作为一个纯软件虚拟化的虚拟机,需要模拟各种硬件,引入qom能够对这些各种各样的硬件进行抽象,提取共性作为基类,特性作为子类;其次我们知道linux设备驱动模型中有设备、驱动、总线的概念,qemu为了模拟这些关系也需要借助于qom

qom模型的数据结构

qom中只要的数据结构有objectclassobjecttypeimplinterfaceclass,知道这些数据结构基本就能清楚qom的实现方式了。首先,我们要暂时抛弃传统高级语言的面向对象实现,因为传统的classobjectqom中的概念稍有不同。

l  typeimpl

正如其名称所示,这个结构表示一种类型(type),每种objectclass都有其对应的type。看到这里有人可能有些迷糊,class本身不就表示一种类型吗?没错,但前面已经说过不要和高级语言中的概念混淆,如果非要类比,可以把这里的typeimpl理解为传统意义的class

点击(此处)折叠或打开

  1. struct typeimpl
  2. {
  3.     const char *name;
  4.  
  5.     size_t class_size; /*该数据类型所代表的类的大小*/
  6.     size_t instance_size; /*该数据类型产生的对象的大小*/
  7.  
  8.     /*类的 constructor & destructor*/
  9.     void (*class_init)(objectclass *klass, void *data);
  10.     void (*class_base_init)(objectclass *klass, void *data);
  11.     void (*class_finalize)(objectclass *klass, void *data);
  12.  
  13.     void *class_data;
  14.  
  15.     /*实例的contructor & destructor*/
  16.     void (*instance_init)(object *obj);
  17.     void (*instance_post_init)(object *obj);
  18.     void (*instance_finalize)(object *obj);
  19.  
  20.     bool abstract; /*表示类是否是抽象类*/
  21.  
  22.     const char *parent; /*父类的名字*/
  23.     typeimpl *parent_type; /*指向父类typeimpl的指针*/
  24.  
  25.     objectclass *class; /*该类型对应的类的指针*/
  26.  
  27.     int num_interfaces; /*所实现的接口的数量*/
  28.     interfaceimpl interfaces[max_interfaces];
  29. };

其中interfaceimpl的定义如下,只是一个类型的名字

点击(此处)折叠或打开

  1. struct interfaceimpl
  2. {
  3.     const char *typename;
  4. };

首先typeimpl本身作为一种类型(class),有其继承关系,这些关系表现在parentparent_type成员,parent_type指向其父typeimpl;当然和传统面向对象意义,typeimpl也可以实现接口,其中interfaces记录其实现的接口名称;

其次我们看到typeimpl主要分为两部分,一部分是classconstructor & destructor,另一部分是objectconstructor & destructor。注意这里的class指的是objectclass或其衍生类,不是传统意义上的class。我们知道传统意义上的class中有数据成员和方法,对应到qom中,objectclass中存放的就是class中的方法,而object中存放的数据成员。

最后,说到typeimpl,不得不提typeinfo这个数据结构。这个结构是用户用来创建一个typeimpl使用的。用户定义了一个typeinfo,然后调用type_register(typeinfo )或者type_register_static(typeinfo )函数,就会生成相应的typeimpl实例,将这个typeinfo注册到全局的typeimplhash表中。

点击(此处)折叠或打开

  1. /*typeinfo的属性与typeimpl的属性对应,实际上qemu就是通过用户提供的typeinfo创建的typeimpl的对象*/
  2. struct typeinfo
  3. {
  4.     const char *name;
  5.     const char *parent;
  6.  
  7.     size_t instance_size;
  8.     void (*instance_init)(object *obj);
  9.     void (*instance_post_init)(object *obj);
  10.     void (*instance_finalize)(object *obj);
  11.  
  12.     bool abstract;
  13.     size_t class_size;
  14.  
  15.     void (*class_init)(objectclass *klass, void *data);
  16.     void (*class_base_init)(objectclass *klass, void *data);
  17.     void (*class_finalize)(objectclass *klass, void *data);
  18.     void *class_data;
  19.  
  20.     interfaceinfo *interfaces;
  21. };

l  objectclass

点击(此处)折叠或打开

  1. typedef struct typeimpl *type;
  2.  
  3. struct objectclass
  4. {
  5.     /*< private >*/
  6.     type type; /*指向该class所属的typeimpl*/
  7.     gslist *interfaces;
  8.  
  9.     const char *object_cast_cache[object_class_cast_cache];
  10.     const char *class_cast_cache[object_class_cast_cache];
  11.  
  12.     objectunparent *unparent;
  13. };

objectclass是所有class的基类(这里说的class不是typeimpl),如果你要实现一个class,就需要继承objectclass(所谓继承,就是将objectclass当做你的class的第一个成员)。如qemu中为为描述设备,定义了一个class——deviceclass

点击(此处)折叠或打开

  1. struct deviceclass {
  2.     /*< private >*/
  3.     objectclass parent_class;
  4.     /*< public >*/
  5.     declare_bitmap(categories, device_category_max);
  6.     const char *fw_name;
  7.     const char *desc;
  8.     property *props;
  9.     bool cannot_instantiate_with_device_add_yet;
  10.     bool hotpluggable;
  11.     /* callbacks */
  12.     void (*reset)(devicestate *dev);
  13.     devicerealize realize;
  14.     deviceunrealize unrealize;
  15.     /* device state */
  16.     const struct vmstatedescription *vmsd;
  17.     /* private to qdev / bus. */
  18.     qdev_initfn init; /* todo remove, once users are converted to realize */
  19.     qdev_event exit; /* todo remove, once users are converted to unrealize */
  20.     const char *bus_type;
  21. };

l  object

如果说objectclass是所有要定义class的基类,那么object就是所有要定义object的基类。

点击(此处)折叠或打开

  1. struct object
  2. {
  3.     /*< private >*/
  4.     objectclass *class;
  5.     objectfree *free; /*当对象的引用为0时,清理垃圾的回调函数*/
  6.     ghashtable *properties; /*hash表记录object的属性*/
  7.     uint32_t ref; /*该对象的引用计数*/
  8.     object *parent;
  9. };

    同样qemu中任何对象都需要继承object,如qemu中为了描述设备定义的object——devicestate

点击(此处)折叠或打开

  1. struct devicestate {
  2.     /*< private >*/
  3.     object parent_obj;
  4.     /*< public >*/
  5.  
  6.     const char *id;
  7.     bool realized; // 指示设备是否实现,然后调用callback
  8.     bool pending_deleted_event;
  9.     qemuopts *opts;
  10.     int hotplugged;
  11.     busstate *parent_bus;
  12.     qlist_head(, namedgpiolist) gpios;
  13.     qlist_head(, busstate) child_bus;
  14.     int num_child_bus;
  15.     int instance_id_alias;
  16.     int alias_required_for_version;
  17. };

下面这张图可以简要说明objectclassobjecttypeimplinterfaceclass之间的关系。通过这些数据结构中的指针,我们可以很方便地找到其他对应的数据结构。

 

用户如何通过qom提供的数据结构表示一个类

qemu中如何创建一个新的类(这里其实就是typeimpl)。用户在创建类时要实现两个数据结构:描述类的数据结构(第一个属性必须是父类数据结构的实例,其祖先一定是objectclass)、描述类产生的实例的数据结构(第一个属性必须是父类对象的数据结构的实例,其祖先一定是object),并且需要创建一个数据结构typeinfo的实例,用来描述这种类的类型。通过创建typeinfo,可以告诉qemu哪些信息呢? 

我们再次查看一下typeinfo数据结构,这个数据结构中包含了这个类型的父类(typeimpl)的名字,insatncesize信息、classsize信息,instance的初始化和垃圾回收函数、class的初始化和垃圾回收函数。有了这些信息我们就可以: 

(1)     instanceclass初始化第一个属性(instance的第一个属性必须是父类对象的数据结构的实例,class的一个属性必须是父类的数据结构的实例) 

(2)     instanceclass分配合适的内存空间,这样我们就可以将一个object的指针动态cast为我们需要的对象类型。 

(3)     类的数据结构一般包含大量的函数指针,用于对对象进行操作。classinit函数可以将这些函数指针初始化。然后所有含有这个类数据结构指针的对象,都可以调用这些函数。 

(4)     对象的数据结构一般包含了大量变量,是对象的一些属性。instanceinit函数可以把这些属性初始化,相当于c 中的构造函数。 

一个类可以实现多个接口,这些接口也是一个类,并且是抽象类,含有虚拟函数指针。

可以说,有了这个数据结构,就有了类的最基本的信息。(代码在include/qom/object.h中)

一个类是如何注册到qom中的

qom提供了type_registertype_register_static方法,用户可以调用这两个方法注册一个type,需要传进去的参数就是typeinfo的指针。(代码在qom/object.c中)

点击(此处)折叠或打开

  1. typeimpl *type_register(const typeinfo *info)
  2. {
  3.     assert(info->parent);
  4.     return type_register_internal(info);
  5. }
  6.  
  7. typeimpl *type_register_static(const typeinfo *info)
  8. {
  9.     return type_register(info);
  10. }

可以看到它们都调用了type_register_internal(typeinfo*)函数。(代码在qom/object.c中)

l  type_register_internal

点击(此处)折叠或打开

  1. static typeimpl *type_register_internal(const typeinfo *info)
  2. {
  3.     typeimpl *ti;
  4.     ti = type_new(info);
  5.  
  6.     type_table_add(ti);
  7.     return ti;
  8. }

type_new(typeinfo*)函数将typeinfo数据结构中的相关信息,赋值给typeimpl数据结构中的属性。而type_table_add(typeimpl *)函数将调用type_new()获得的typeimpl指针保存到静态的hash表中。 

l  type_new

针对typeinfo中的各种信息,给typeimpl赋值:(代码在qom/object.c中)

点击(此处)折叠或打开

  1. static typeimpl *type_new(const typeinfo *info)
  2. {
  3.     typeimpl *ti = g_malloc0(sizeof(*ti));
  4.     int i;
  5.  
  6.     g_assert(info->name != null);
  7.  
  8.     if (type_table_lookup(info->name) != null) {
  9.         fprintf(stderr, "registering `%s' which already exists\n", info->name);
  10.         abort();
  11.     }
  12.  
  13.     ti->name = g_strdup(info->name);
  14.     ti->parent = g_strdup(info->parent);
  15.  
  16.     ti->class_size = info->class_size;
  17.     ti->instance_size = info->instance_size;
  18.  
  19.     ti->class_init = info->class_init;
  20.     ti->class_base_init = info->class_base_init;
  21.     ti->class_finalize = info->class_finalize;
  22.     ti->class_data = info->class_data;
  23.  
  24.     ti->instance_init = info->instance_init;
  25.     ti->instance_post_init = info->instance_post_init;
  26.     ti->instance_finalize = info->instance_finalize;
  27.  
  28.     ti->abstract = info->abstract;
  29.  
  30.     for (i = 0; info->interfaces && info->interfaces[i].type; i) {
  31.         ti->interfaces[i].typename = g_strdup(info->interfaces[i].type);
  32.     }
  33.     ti->num_interfaces = i;
  34.  
  35.     return ti;
  36. }

最后,通过type_table_add()保存到静态的hash表中(代码在qom/object.c中)

l  type_table_add

点击(此处)折叠或打开

  1. static void type_table_add(typeimpl *ti)
  2. {
  3.     assert(!enumerating_types);
  4.     g_hash_table_insert(type_table_get(), (void *)ti->name, ti);
  5. }
  6. static ghashtable *type_table_get(void)
  7. {
  8.     static ghashtable *type_table; //静态的类型的hash表,保存了全部被注册的类型
  9.  
  10.     if (type_table == null) {
  11.         type_table = g_hash_table_new(g_str_hash, g_str_equal);
  12.     }
  13.  
  14.     return type_table;
  15. }

面向对象特性的实现

封装的实现

在考察qom如何实现封装时,我们需要再次审视object这个数据结构包含了哪些属性:(代码在/include/qom/object.h中)

点击(此处)折叠或打开

  1. struct object
  2. {
  3.     /*< private >*/
  4.     objectclass *class; //指向对应的类的数据结构的指针
  5.     objectfree *free; //当引用计数为0时调用
  6.     ghashtable *properties; //object中的所有属性的hash表
  7.     uint32_t ref; //对象的引用计数
  8.     object *parent; //指向父对象的指针
  9. };

值得注意的是,这个object的数据结构中的一行注释:“private”,它表示object中的所有属性都是私有的,只能被类的内部函数访问、修改。这个数据结构中体现封装特性的私有变量是properties,它是一张hash表,这个变量包含了object中的所有可访问和修改的数据、函数。这个hash表中,每一个entry有对应的keyvaluekey是这个propertyname,而value是一个objectproperty数据结构的指针,objectproperty的实现代码如下:(代码在include/qom/object.h中)

点击(此处)折叠或打开

  1. typedef struct objectproperty
  2. {
  3.     gchar *name;
  4.     gchar *type;
  5.     gchar *description;
  6.     objectpropertyaccessor *get;
  7.     objectpropertyaccessor *set;
  8.     objectpropertyresolve *resolve;
  9.     objectpropertyrelease *release;
  10.     void *opaque;
  11. } objectproperty;

可以看到,objectproperty包含了这个属性的名字、类型、描述、getset的方法,解析(resolve)和释放(release)的方法,以及这个property特有的属性,用void *类型的指针来表示。 

qom使用这样一个数据结构,将对象的每个数据都保存在这样一个单元之中,从而让实现了封装。 

当用户需要向object中增加属性时,就可以直接调用object_property_add函数,这个函数向objectpropertieshash表中插入了一个property。(代码在qom/object.c中)

l  object_property_add

点击(此处)折叠或打开

  1. objectproperty * object_property_add(object *obj, const char *name, const char *type,
  2.                     objectpropertyaccessor *get,
  3.                     objectpropertyaccessor *set,
  4.                     objectpropertyrelease *release,
  5.                     void *opaque, error **errp)
  6. {
  7.     objectproperty *prop;
  8.     size_t name_len = strlen(name);
  9.  
  10.     if (name_len >= 3 && !memcmp(name name_len - 3, "[*]", 4)) {
  11.         int i;
  12.         objectproperty *ret;
  13.         char *name_no_array = g_strdup(name);
  14.  
  15.         name_no_array[name_len - 3] = '\0';
  16.         for (i = 0; ; i) {
  17.             char *full_name = g_strdup_printf("%s[%d]", name_no_array, i);
  18.  
  19.             ret = object_property_add(obj, full_name, type, get, set,
  20.                                       release, opaque, null);
  21.             g_free(full_name);
  22.             if (ret) {
  23.                 break;
  24.             }
  25.         }
  26.         g_free(name_no_array);
  27.         return ret;
  28.     }
  29.  
  30.     if (g_hash_table_lookup(obj->properties, name) != null) {
  31.         error_setg(errp, "attempt to add duplicate property '%s'"
  32.                        " to object (type '%s')", name,
  33.                        object_get_typename(obj));
  34.         return null;
  35.     }
  36.  
  37.     prop = g_malloc0(sizeof(*prop));
  38.  
  39.     prop->name = g_strdup(name);
  40.     prop->type = g_strdup(type);
  41.  
  42.     prop->get = get;
  43.     prop->set = set;
  44.     prop->release = release;
  45.     prop->opaque = opaque;
  46.  
  47.     g_hash_table_insert(obj->properties, prop->name, prop);
  48.     return prop;
  49. }

继承的实现

继承包括:实现继承、接口继承。实现继承是指使用基类的属性和方法而无需额外编码的能力; 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力。 

l  实现继承 

我们创建一个新类时,需要实现两个数据结构:类的数据结构和对象的数据结构,由于类的数据结构中第一个属性就是父类的数据结构的实例,而对象的数据结构中,第一个属性就是父类对应的对象的实例。这样的实现方式,使得qom天然地支持显现继承。 

l  接口继承 

接口在qom中也有一个对应的类的数据结构:(代码在include/qom/object.h中)

点击(此处)折叠或打开

  1. struct interfaceclass
  2. {
  3.     objectclass parent_class; //它的父类就是objectclass
  4.     /*< private >*/
  5.     objectclass *concrete_class; //实现这个接口的类的指针
  6.     type interface_type; //这个interface的类型(typeimpl*指针),这个属性指示要实现的接口类型。
  7. };

    注意interfaceclass也是一个objectclass。在qom中一个类可以实现多个接口,也就是实现接口继承。在objectclass中与接口继承相关的属性就是interfaces,它在objectclass是一条链表,链表中的每个entry都是一个interfaceclass的指针,最终对应到interfacetypeimpl数据结构的指针。我们可以通过给typeimpl指针对应的类数据结构中的函数指针赋值,达到实现接口的目的。(代码在include/qom/include.h中)

多态的实现

多态是指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。为了实现多态,qom实现了一个非常重要的功能,就是动态cast。我们可以使用相关的函数,将一个object的指针在运行时cast为子类对象的指针,可以将一个objectclass的指针在运行时cast为子类的指针。这样就可以调用子类中定义的函数指针来完成相应的功能。

动态cast检查的主要实现函数是:(代码在qom/object.c中)

点击(此处)折叠或打开

  1. objectclass *object_class_dynamic_cast(objectclass *class,
  2.                                        const char *typename)
  3. {
  4.     objectclass *ret = null;
  5.     typeimpl *target_type;
  6.     typeimpl *type;
  7.  
  8.     if (!class) {
  9.         return null;
  10.     }
  11.  
  12.     type = class->type; //class的type指针总是指向这个类的typeimpl,因此从这里获得这个类的类型
  13.     if (type->name == typename) {
  14.         return class;
  15.     }
  16.  
  17.     target_type = type_get_by_name(typename);
  18.     if (!target_type) {
  19.         /* target class type unknown, so fail the cast */
  20.         return null;
  21.     }
  22.     //检查是否需要动态cast为接口类对象,然后检查每个接口
  23.     //主要检查目标类型是不是当前所指向的类型的祖先。
  24.     if (type->class->interfaces &&
  25.             type_is_ancestor(target_type, type_interface)) {
  26.         int found = 0;
  27.         gslist *i;
  28.  
  29.         for (i = class->interfaces; i; i = i->next) {
  30.             objectclass *target_class = i->data;
  31.  
  32.             if (type_is_ancestor(target_class->type, target_type)) {
  33.                 ret = target_class;
  34.                 found;
  35.             }
  36.          }
  37.  
  38.         /* the match was ambiguous, don't allow a cast */
  39.         if (found > 1) {
  40.             ret = null;
  41.         }
  42.     } else if (type_is_ancestor(type, target_type)) {
  43.         ret = class;
  44.     }
  45.  
  46.     return ret;
  47. }

一般object_class_dynamic_cast是在object_class_dynamic_cast_assert中调用的。

l  object_class_dynamic_cast_assert

点击(此处)折叠或打开

  1. objectclass *object_class_dynamic_cast_assert(objectclass *class,
  2.                                               const char *typename,
  3.                                               const char *file, int line,
  4.                                               const char *func)
  5. {
  6.     objectclass *ret;
  7.  
  8.     if (!class || !class->interfaces) {
  9.         return class;
  10.     }
  11.  
  12.     ret = object_class_dynamic_cast(class, typename);
  13.     if (!ret && class) {
  14.         fprintf(stderr, "%s:%d:%s: object %p is not an instance of type %s\n",
  15.                 file, line, func, class, typename);
  16.         abort();
  17.     }
  18.  
  19.     return ret;
  20. }

    可以看到只有classclass->interfaces都不空时才会调用object_class_dynamic_cast,也就是当前class实现了接口的时候才需要object_class_dynamic_cast,否则直接返回当前class

垃圾回收

object的数据结构中有一个变量:ref,这个变量用于对object引用的计数,如果ref的值变为0,就意味着系统不会继续使用这个对象了,那么就可以对这个变量的内存空间等进行回收操作。 

(1)     typeinfo中,我们可以定义instance_finalize,对于引用计数为0object进行垃圾回收操作。 

(2)     object数据结构中有一个objectfree *类型的函数指针free,当object的引用计数为0时,就会调用这个函数进行垃圾回收。 

(3)     qom自己实现了默认的垃圾回收操作:(代码在qom/object.c中)

点击(此处)折叠或打开

  1. //减少obj的引用计数,如果引用计数为0,将进行垃圾回收
  2. void object_unref(object *obj)
  3. {
  4.     if (!obj) {
  5.         return;
  6.     }
  7.     g_assert_cmpint(obj->ref, >, 0);
  8.  
  9.     /* parent always holds a reference to its children */
  10.     if (atomic_fetch_dec(&obj->ref) == 1) {
  11.         object_finalize(obj);
  12.     }
  13. }
  14. //obj的默认的析构函数
  15. static void object_finalize(void *data)
  16. {
  17.     object *obj = data;
  18.     typeimpl *ti = obj->class->type;
  19.  
  20.     object_property_del_all(obj); //删除obj中的所有属性
  21.     object_deinit(obj, ti); //调用typeimpl中finalize函数进行析构(请看后面)
  22.  
  23.     g_assert_cmpint(obj->ref, ==, 0); //确定引用计数为0
  24.     if (obj->free) {
  25.         obj->free(obj); //如果obj有free函数指针,那么就会调用该函数
  26.     }
  27. }
  28. // 调用typeimpl中的实例析构函数。如果存在父类,需要继续调用父类的实例析构函数
  29. // 调用父类实例析构函数是因为一个对象数据结构中,第一个属性就是父类对象的实例
  30. // 当我们需要对对象析构时,不仅要调用当前类的析构方法,也需要调用父类的析构方法
  31. // 将对象中的第一个属性进行析构。
  32. static void object_deinit(object *obj, typeimpl *type)
  33. {
  34.     if (type->instance_finalize) {
  35.         type->instance_finalize(obj);
  36.     }
  37.  
  38.     if (type_has_parent(type)) {
  39.         object_deinit(obj, type_get_parent(type));
  40.     }
  41. }

怎样使用qom模型创建新类型

使用qom模型创建新类型时,需要用到以上的ojectclassobjecttypeinfo。关于qom的用法,在include/qom/object.h一开始就有一长串的注释,这一长串的注释说明了创建新类型时的各种用法。我们下面是对这些用法的简要说明。 

1. 从最简单的开始,创建一个最小的type:

点击(此处)折叠或打开

  1. #include "qdev.h"
  2. #define type_my_device "my-device"
  3.  
  4.   // 用户需要定义新类型的类和对象的数据结构
  5.   // 由于不实现父类的虚拟函数,所以直接使用父类的数据结构作为子类的数据结构
  6.   // no new virtual functions: we can reuse the typedef for the
  7.   // superclass.
  8.   typedef deviceclass mydeviceclass; //定义class,直接就使用qemu本身的deviceclass
  9.   typedef struct mydevice //定义instance
  10.   {
  11.       devicestate parent; //父对象必须是该对象数据结构的第一个属性,以便实现父对象向子对象的cast (devicestate又是继承object)
  12.  
  13.       int reg0, reg1, reg2;
  14.   } mydevice;
  15.  
  16.   static const typeinfo my_device_info = {
  17.       .name = type_my_device,
  18.       .parent = type_device,
  19.       .instance_size = sizeof(mydevice), //必须向系统说明对象的大小,以便系统为对象的实例分配内存
  20.   };
  21.  
  22.   //向系统中注册这个新类型
  23.   static void my_device_register_types(void)
  24.   {
  25.       type_register_static(&my_device_info);
  26.   }
  27.   type_init(my_device_register_types)

2 . 为了方便编程,对于每个新类型,都会定义由objectclass动态castmydeviceclass的方法,也会定义由object动态castmydevice的方法。以下涉及的函数object_get_classobject_class_checkobject_check都在include/qemu/object.h中定义。

点击(此处)折叠或打开

  1. #define my_device_get_class(obj) \
  2.         object_get_class(mydeviceclass, obj, type_my_device)
  3.  #define my_device_class(klass) \
  4.         object_class_check(mydeviceclass, klass, type_my_device)
  5.  #define my_device(obj) \
  6.         object_check(mydevice, obj, type_my_device)


3 . 如果我们在定义新类型中,实现了父类的虚拟方法,那么需要定义新的class的初始化函数,并且在typeinfo数据结构中,给typeinfoclass_init字段赋予该初始化函数的函数指针。


点击(此处)折叠或打开

  1. #include "qdev.h"
  2.  
  3.   void my_device_class_init(objectclass *klass, void *class_data)
  4.   {
  5.       deviceclass *dc = device_class(klass);
  6.       dc->reset = my_device_reset;
  7.   }
  8.  
  9.   static const typeinfo my_device_info = {
  10.       .name = type_my_device,
  11.       .parent = type_device,
  12.       .instance_size = sizeof(mydevice),
  13.       .class_init = my_device_class_init, /*在类初始化时就会调用这个函数,将虚拟函数赋值*/
  14.   };


4 . 当我们需要从一个类创建一个派生类时,如果需要覆盖 类原有的虚拟方法,派生类中,可以增加相关的属性将类原有的虚拟函数指针保存,然后给虚拟函数赋予新的函数指针,保证父类原有的虚拟函数指针不会丢失。

点击(此处)折叠或打开

  1. typedef struct mystate mystate;
  2. typedef void (*mydosomething)(mystate *obj);
  3.  
  4.   typedef struct myclass {
  5.       objectclass parent_class;
  6.  
  7.       mydosomething do_something;
  8.   } myclass;
  9.  
  10.   static void my_do_something(mystate *obj)
  11.   {
  12.       // do something
  13.   }
  14.  
  15.   static void my_class_init(objectclass *oc, void *data)
  16.   {
  17.       myclass *mc = my_class(oc);
  18.  
  19.       mc->do_something = my_do_something;
  20.   }
  21.  
  22.   static const typeinfo my_type_info = {
  23.       .name = type_my,
  24.       .parent = type_object,
  25.       .instance_size = sizeof(mystate),
  26.       .class_size = sizeof(myclass),
  27.       .class_init = my_class_init,
  28.   };
  29.  
  30.   typedef struct derivedclass {
  31.       myclass parent_class;
  32.  
  33.       mydosomething parent_do_something;
  34.   } derivedclass;
  35.  
  36.   static void derived_do_something(mystate *obj)
  37.   {
  38.       derivedclass *dc = derived_get_class(obj);
  39.  
  40.       // do something here
  41.       dc->parent_do_something(obj);
  42.       // do something else here
  43.   }
  44.  
  45.   static void derived_class_init(objectclass *oc, void *data)
  46.   {
  47.       myclass *mc = my_class(oc);
  48.       derivedclass *dc = derived_class(oc);
  49.  
  50.       dc->parent_do_something = mc->do_something;
  51.       mc->do_something = derived_do_something;
  52.   }
  53.  
  54.   static const typeinfo derived_type_info = {
  55.       .name = type_derived,
  56.       .parent = type_my,
  57.       .class_size = sizeof(derivedclass),
  58.       .class_init = derived_class_init,
  59.   };


阅读(19) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:没有了

给主人留下些什么吧!~~
")); function link(t){ var href= $(t).attr('href'); href ="?url=" encodeuricomponent(location.href); $(t).attr('href',href); //setcookie("returnouturl", location.href, 60, "/"); }
网站地图