每一个 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;
/*< public >*/
/* virtual methods for subclasses */
/**
* GstBaseSrcClass::get_caps:
* @filter: (in) (nullable):
*
* Called to get the caps to report.
*/
GstCaps* (*get_caps) (GstBaseSrc *src, GstCaps *filter);
/* decide on caps */
gboolean (*negotiate) (GstBaseSrc *src);
/* called if, in negotiation, caps need fixating */
GstCaps * (*fixate) (GstBaseSrc *src, GstCaps *caps);
/* notify the subclass of new caps */
gboolean (*set_caps) (GstBaseSrc *src, GstCaps *caps);
/* setup allocation query */
gboolean (*decide_allocation) (GstBaseSrc *src, GstQuery *query);
/* start and stop processing, ideal for opening/closing the resource */
gboolean (*start) (GstBaseSrc *src);
gboolean (*stop) (GstBaseSrc *src);
/**
* GstBaseSrcClass::get_times:
* @start: (out):
* @end: (out):
*
* Given @buffer, return @start and @end time when it should be pushed
* out. The base class will sync on the clock using these times.
*/
void (*get_times) (GstBaseSrc *src, GstBuffer *buffer,
GstClockTime *start, GstClockTime *end);
/* get the total size of the resource in the format set by
* gst_base_src_set_format() */
gboolean (*get_size) (GstBaseSrc *src, guint64 *size);
/* check if the resource is seekable */
gboolean (*is_seekable) (GstBaseSrc *src);
/* Prepare the segment on which to perform do_seek(), converting to the
* current basesrc format. */
gboolean (*prepare_seek_segment) (GstBaseSrc *src, GstEvent *seek,
GstSegment *segment);
/* notify subclasses of a seek */
gboolean (*do_seek) (GstBaseSrc *src, GstSegment *segment);
/* unlock any pending access to the resource. subclasses should unlock
* any function ASAP. */
gboolean (*unlock) (GstBaseSrc *src);
/* Clear any pending unlock request, as we succeeded in unlocking */
gboolean (*unlock_stop) (GstBaseSrc *src);
/* notify subclasses of a query */
gboolean (*query) (GstBaseSrc *src, GstQuery *query);
/* notify subclasses of an event */
gboolean (*event) (GstBaseSrc *src, GstEvent *event);
/**
* GstBaseSrcClass::create:
* @buf: (out):
*
* Ask the subclass to create a buffer with @offset and @size, the default
* implementation will call alloc and fill.
*/
GstFlowReturn (*create) (GstBaseSrc *src, guint64 offset, guint size,
GstBuffer **buf);
/* ask the subclass to allocate an output buffer. The default implementation
* will use the negotiated allocator. */
GstFlowReturn (*alloc) (GstBaseSrc *src, guint64 offset, guint size,
GstBuffer **buf);
/* ask the subclass to fill the buffer with data from offset and size */
GstFlowReturn (*fill) (GstBaseSrc *src, guint64 offset, guint size,
GstBuffer *buf);
/*< private >*/
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文件的行为。