oop - 在C OOP和接口中

  显示原文与译文双语对照的内容

我明白,ANSI不是面向对象编程语言。 我想了解如何使用应用特定的oo技术。

例如我想创建几个音频效果类,它们都具有相同的函数名但这些函数的实现不同。

如果我用更高级的语言来做这个,我首先会编写一个接口,然后实现它。


AudioEffectInterface

-(float) processEffect 



DelayClass

-(float) processEffect

{
//do delay code

 return result

}

FlangerClass

-(float) processEffect

{
//do flanger code

 return result

}



-(void) main

{
 effect= new DelayEffect()
 effect.process()

 effect = new FlangerEffect()
 effect.process()


}

如何使用C 实现这种灵活性?

时间:

你可以通过以下方式进行妥协:


#include <stdio.h>

struct effect_ops {
 float (*processEffect)(void *effect);
/* + other operations.. */
};

struct DelayClass {
 unsigned delay;
 struct effect_ops *ops;
};

struct FlangerClass {
 unsigned period;
 struct effect_ops *ops;
};

/* The actual effect functions are here
 * Pointers to the actual structure may be needed for effect-specific parameterization, etc.
 */
float flangerEffect(void *flanger)
{
 struct FlangerClass *this = flanger;
/* mix signal delayed by this->period with original */
 return 0.0f;
}

float delayEffect(void *delay)
{
 struct DelayClass *this = delay;
/* delay signal by this->delay */
 return 0.0f;
}

/* Instantiate and assign a"default" operation, if you want to */
static struct effect_ops flanger_operations = {
. processEffect = flangerEffect,
};

static struct effect_ops delay_operations = {
. processEffect = delayEffect,
};

int main()
{
 struct DelayClass delay = {.delay = 10,. ops = &delay_operations};
 struct FlangerClass flanger = {.period = 1,. ops = &flanger_operations};
/*.. .then for your signal */
 flanger.ops->processEffect(&flanger);
 delay.ops->processEffect(&delay);
 return 0;
}

在C 中,可以通过三种不同的方式实现多态性:

  1. 代码
    在基类函数中,仅在类类型ID上使用 switch 来调用专用版本。 不完整的代码示例:

    
    typedef enum classType {
     CLASS_A,
     CLASS_B
    } classType;
    
    typedef struct base {
     classType type;
    } base;
    
    typedef struct A {
     base super;
    . . .
    } A;
    
    typedef struct B {
     base super;
    . . .
    } B;
    
    void A_construct(A* me) {
     base_construct(&me->super);
     super->type = CLASS_A;
    }
    
    int base_foo(base* me) {
     switch(me->type) {
     case CLASS_A: return A_foo(me);
     case CLASS_B: return B_foo(me);
     default: assert(0), abort();
     }
    }
    
    

    当然,这个的对于大型类来说是的。

  2. 对象中的存储函数指针
    可以通过使用函数指针来避免 switch 语句。 同样,这是不完整的代码:

    
    typedef struct base {
     int (*foo)(base* me);
    } base;
    
    //class definitions for A and B as 上面
    
    int A_foo(base* me);
    
    void A_construct(A* me) {
     base_construct(&me->super);
     me->super.foo = A_foo;
    }
    
    

    现在调用代码可能只是

    
    base* anObject =.. .;
    (*anObject->foo)(anObject);
    
    

    或者,也可以沿着以下行使用预处理器宏:

    
    #define base_foo(me) (*me->foo)(me)
    
    

    请注意,这将计算表达式 me 两次,因此这是一个错误的想法。 这可能是固定的,但这超出了这个答案的范围。

  3. 使用 vtable
    因为类的所有对象都共享相同的成员函数集,所以它们都可以使用相同的函数指针。 这跟 C++ 在引擎盖下的情况非常接近:

    
    typedef struct base_vtable {
     int (*foo)(base* me);
    . . .
    } base_vtable;
    
    typedef struct base {
     base_vtable* vtable;
    . . .
    } base;
    
    typedef struct A_vtable {
     base_vtable super;
    . . .
    } A_vtable;
    
    
    
    //within A.c
    
    int A_foo(base* super);
    static A_vtable gVtable = {
    . foo = A_foo,
    . . .
    };
    
    void A_construct(A* me) {
     base_construct(&me->super);
     me->super.vtable = gVtable;
    };
    
    

    同样,这允许用户代码执行调度( 使用其他间接寻址):

    
    base* anObject =.. .;
    (*anObject->vtable.foo)(anObject);
    
    

你应该使用哪种方法取决于手头的任务。 基于 switch的方法很容易为两个或者三个小类 whip,但对于大型类和层次结构来说是笨拙的。 第二种方法的伸缩性更好,但是由于重复的函数指针,它有很多空间开销。 vtable方法需要相当多的额外结构,并引入更间接的( 这使得代码更难以阅读),但是它确实是用于复杂类层次结构的go的。

使用函数指针的结构实现接口。 然后,你可以将接口结构嵌入到数据对象结构中,并将接口指针作为每个接口成员函数的第一个参数。 在该函数中,你将使用 container_of ( ) 宏获取容器类( 具体取决于你的实现)的指针。 为实现搜索"。container_of内核内核"。 这是一个非常有用的宏。

...