直接蝙蝠我明白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()
}
Run Code Online (Sandbox Code Playgroud)
如何使用C实现这种灵活性?
cma*_*ter 31
在C中有三种不同的方法可以实现多态性:
编码出来
在基类函数中,只需要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();
}
}
Run Code Online (Sandbox Code Playgroud)
当然,这对于大班来说是很乏味的.
在对象中存储函数指针
您可以通过为每个成员函数使用函数指针来避免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;
}
Run Code Online (Sandbox Code Playgroud)
现在,调用代码可能就是这样
base* anObject = ...;
(*anObject->foo)(anObject);
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用预处理器宏:
#define base_foo(me) (*me->foo)(me)
Run Code Online (Sandbox Code Playgroud)
请注意,这会对表达式进行me
两次计算,所以这真是一个坏主意.这可能是固定的,但这超出了这个答案的范围.
使用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;
};
Run Code Online (Sandbox Code Playgroud)
同样,这允许用户代码执行调度(带有一个额外的间接):
base* anObject = ...;
(*anObject->vtable->foo)(anObject);
Run Code Online (Sandbox Code Playgroud)您应该使用哪种方法取决于手头的任务.该switch
基础的方法是很容易掀起了两三个小类,但笨重的大型类和层次结构.第二种方法扩展得更好,但由于重复的函数指针而导致空间开销很大.vtable方法需要相当多的额外结构,并引入更多的间接性(这使代码更难阅读),但肯定是复杂类层次结构的方法.
Mic*_*kis 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;
}
Run Code Online (Sandbox Code Playgroud)