C中的OOP和接口

时间:2011-06-10 09:59:30

标签: c oop

直接击球我明白ANSI C不是面向对象的编程语言。我想学习如何使用c。

应用特定的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实现这种灵活性?

3 个答案:

答案 0 :(得分:27)

在C中有三种不同的方法可以实现多态:

  1. 编码
    在基类函数中,只需switch类类型ID即可调用专用版本。一个不完整的代码示例:

    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 above
    
    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);
    
  4. 您应该使用哪种方法取决于手头的任务。基于switch的方法很容易为两个或三个小类提供,但对于大类和层次结构来说却很难处理。第二种方法扩展得更好,但由于重复的函数指针而导致空间开销很大。 vtable方法需要相当多的额外结构,并引入更多的间接性(这使得代码更难以阅读),但肯定是复杂类层次结构的方法。

答案 1 :(得分:11)

您能否妥协以下内容:

#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;
}

答案 2 :(得分:3)

使用函数指针的结构实现接口。然后,您可以在数据对象结构中嵌入接口结构,并将接口指针作为每个接口成员函数的第一个参数传递。在该函数中,然后使用container_of()宏获取指向容器类的指针(这是特定于您的实现)。搜索&#34; container_of linux内核&#34;实施。这是一个非常有用的宏。