c - OOP 和 C 中的接口(interface)

标签 c oop

直截了当,我明白 ANSI C 不是一种面向对象的编程语言。我想学习如何使用 c 应用特定的 oo 技术。

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

如果我用更高级的语言制作它,我会先编写一个接口(interface),然后再实现它。

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 实现这种灵 active ?

最佳答案

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

  1. 编码出来
    在基类函数中,只需在类类型 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. 使用虚表
    由于一个类的所有对象共享同一组成员函数,它们都可以使用相同的函数指针。这非常接近 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;
    };
    

    同样,这允许用户代码进行分派(dispatch)(使用一个额外的间接寻址):

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

您应该使用哪种方法取决于手头的任务。基于 switch 的方法很容易为两个或三个小类快速启动,但对于大类和层次结构来说却很笨拙。第二种方法的扩展性更好,但由于函数指针重复,因此会产生大量空间开销。 vtable 方法需要相当多的额外结构并引入更多间接(这使得代码更难阅读),但肯定是复杂类层次结构的方法。

关于c - OOP 和 C 中的接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6304794/

相关文章:

c - 使用mpi_send发送地址内容

c - 解析字符串以在 Xlib 中为文本着色

c++ - 如何在堆上初始化线程? (使用 “new”关键字)

php - 如何以OOP方式处理文件?

c - 我什么时候应该使用地址为 '&' & 符号键的 scanf ?

c - K&R 中的位计数函数

java - 如果您使用基于界面的设计方法,您如何命名更多行为界面?

oop - 如何将对象从其基类转换为子类

java - 与类(Class)斗争,延伸和实现

c - (x || !x) 什么时候为假?