每一个 GType 都有两个结构体 :instance struct 和 class struct ,二者作用和异同 见 [GLib][GStreamer] Glib 对象模型中的 instance struct 和 class struct_ykun089的博客-CSDN博客
继承:GLib 中的继承是通过在 instance struct 和 class struct 开始部分分别定义 parent instance struct 成结构体成员 和 parent class struct 结构体成员来实现的 (这也是OO语言的底层实现)。
函数覆写:那么函数覆写是如何实现的?下面以 GstbaseSrcClass 为例:
类的成员函数基本上都定义在 class struct中,因此以 GstbaseSrcClass 为例
struct _GstbaseSrcClass { GstElementClass parent_class; GstCaps* (*get_caps) (GstbaseSrc *src, GstCaps *filter); gboolean (*negotiate) (GstbaseSrc *src); GstCaps * (*fixate) (GstbaseSrc *src, GstCaps *caps); gboolean (*set_caps) (GstbaseSrc *src, GstCaps *caps); gboolean (*decide_allocation) (GstbaseSrc *src, GstQuery *query); gboolean (*start) (GstbaseSrc *src); gboolean (*stop) (GstbaseSrc *src); void (*get_times) (GstbaseSrc *src, GstBuffer *buffer, GstClockTime *start, GstClockTime *end); gboolean (*get_size) (GstbaseSrc *src, guint64 *size); gboolean (*is_seekable) (GstbaseSrc *src); gboolean (*prepare_seek_segment) (GstbaseSrc *src, GstEvent *seek, GstSegment *segment); gboolean (*do_seek) (GstbaseSrc *src, GstSegment *segment); gboolean (*unlock) (GstbaseSrc *src); gboolean (*unlock_stop) (GstbaseSrc *src); gboolean (*query) (GstbaseSrc *src, GstQuery *query); gboolean (*event) (GstbaseSrc *src, GstEvent *event); GstFlowReturn (*create) (GstbaseSrc *src, guint64 offset, guint size, GstBuffer **buf); GstFlowReturn (*alloc) (GstbaseSrc *src, guint64 offset, guint size, GstBuffer **buf); GstFlowReturn (*fill) (GstbaseSrc *src, guint64 offset, guint size, GstBuffer *buf); gpointer _gst_reserved[GST_PADDING_LARGE]; };
可以看到在 class struct 中有很多函数指针,那么这些指针是怎么用的呢?再随便找一个 GstbaseSrc 的内部实现:
static gboolean gst_base_src_prepare_seek_segment (GstbaseSrc * src, GstEvent * event, GstSegment * seeksegment) { GstbaseSrcClass *bclass; gboolean result = FALSE; bclass = GST_base_SRC_GET_CLASS (src); //这里进行函数指针的调用 if (bclass->prepare_seek_segment) result = bclass->prepare_seek_segment (src, event, seeksegment); return result; }
因此,如果向覆盖 GstbaseSrc 的 prepare_seek_segment 这个函数的默认实现,只要在自定义的 MyGstbaseSrcClass 中为 GstbaseSrcClass 成员的 prepare_seek_segment 指定自定义的函数即可:
static gboolean gst_my_prepare_seek_segment (GstbaseSrc * src, GstEvent * event, GstSegment * segment); static void gst_my_gst_src_init_class(GstbaseSrc* src,gpointer g_class) { ... parent_class->prepare_seek_segment = gst_my_prepare_seek_segment; ... }
其实,上面的思路也是 c++ 语言的实现思路,只不过 c++ 编译器帮我们完成了语法到实现的转换。
一般情况下,某些类都会有一些默认的实现,比如 GstbaseSrc 的 class_init 函数中就用下面这些函数指定:
static void gst_base_src_class_init (GstbaseSrcClass * klass) { //... //同样地, GstbaseSrc 也会给它自己的 parent class GstElementClass 指定一下自定义动作用来 //覆盖 parent class 的默认动作 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_base_src_change_state); gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_src_send_event); //指定默认动作,GST_DEBUG_FUNCPTR 里面包裹的函数都是定义在 GstbaseSrc.c 文件中的 static 函数 klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_src_default_get_caps); klass->negotiate = GST_DEBUG_FUNCPTR (gst_base_src_default_negotiate); klass->fixate = GST_DEBUG_FUNCPTR (gst_base_src_default_fixate); klass->prepare_seek_segment = GST_DEBUG_FUNCPTR (gst_base_src_default_prepare_seek_segment); klass->do_seek = GST_DEBUG_FUNCPTR (gst_base_src_default_do_seek); klass->query = GST_DEBUG_FUNCPTR (gst_base_src_default_query); klass->event = GST_DEBUG_FUNCPTR (gst_base_src_default_event); klass->create = GST_DEBUG_FUNCPTR (gst_base_src_default_create); klass->alloc = GST_DEBUG_FUNCPTR (gst_base_src_default_alloc); klass->decide_allocation = GST_DEBUG_FUNCPTR (gst_base_src_decide_allocation_default); //... }虚函数:
结合上面的描述,如果 基类不提供默认动作,而继承类又不进行函数覆写,那么函数指针就是空指针,这个时候可以认为某个函数是一个纯虚函数。不过通常情况下都会进行二次包装,不会直接暴露成员函数给外部使用,比如GstbaseSrc就对外暴露了下面这些接口,可以认为这些接口是public接口:
GST_base_API GType gst_base_src_get_type (void); GST_base_API GstFlowReturn gst_base_src_wait_playing (GstbaseSrc *src); GST_base_API void gst_base_src_set_live (GstbaseSrc *src, gboolean live); GST_base_API gboolean gst_base_src_is_live (GstbaseSrc *src); GST_base_API void gst_base_src_set_format (GstbaseSrc *src, GstFormat format); GST_base_API void gst_base_src_set_dynamic_size (GstbaseSrc * src, gboolean dynamic); GST_base_API void gst_base_src_set_automatic_eos (GstbaseSrc * src, gboolean automatic_eos); GST_base_API void gst_base_src_set_async (GstbaseSrc *src, gboolean async); GST_base_API gboolean gst_base_src_is_async (GstbaseSrc *src); GST_base_API gboolean gst_base_src_negotiate (GstbaseSrc *src); GST_base_API void gst_base_src_start_complete (GstbaseSrc * basesrc, GstFlowReturn ret); GST_base_API GstFlowReturn gst_base_src_start_wait (GstbaseSrc * basesrc); GST_base_API gboolean gst_base_src_query_latency (GstbaseSrc *src, gboolean * live, GstClockTime * min_latency, GstClockTime * max_latency); GST_base_API void gst_base_src_set_blocksize (GstbaseSrc *src, guint blocksize); GST_base_API guint gst_base_src_get_blocksize (GstbaseSrc *src); GST_base_API void gst_base_src_set_do_timestamp (GstbaseSrc *src, gboolean timestamp); GST_base_API gboolean gst_base_src_get_do_timestamp (GstbaseSrc *src); GST_base_API gboolean gst_base_src_new_seamless_segment (GstbaseSrc *src, gint64 start, gint64 stop, gint64 time); GST_base_API gboolean gst_base_src_new_segment (GstbaseSrc *src, const GstSegment * segment); GST_base_API gboolean gst_base_src_set_caps (GstbaseSrc *src, GstCaps *caps); GST_base_API GstBufferPool * gst_base_src_get_buffer_pool (GstbaseSrc *src); GST_base_API void gst_base_src_get_allocator (GstbaseSrc *src, GstAllocator **allocator, GstAllocationParams *params); GST_base_API void gst_base_src_submit_buffer_list (GstbaseSrc * src, GstBufferList * buffer_list);
而GstbaseSrcClass 中的那些指针则是 protected 的接口,因为只有继承类能够访问,同理可以通过在 GstbaseSrc.c 中定义Priv结构体来实现 private 接口,因为按照 c 语言的变成习惯,大家不会include c文件,这样就没法知道 Priv结构体 的内部细节。
如果想要更强制一点的制止不规范程序员试图 include c文件的话,那么可以约定一个规则,要求所有c文件都定义某个同名符号,这样在链接是就会报错重复符号,进而阻止 include c文件的行为。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)