直接击球我明白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实现这种灵活性?
答案 0 :(得分:27)
在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();
}
}
当然,这个对于大班来说非常繁琐。
在对象中存储函数指针
您可以通过为每个成员函数使用函数指针来避免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
进行两次计算,所以这真是一个坏主意。这可能是固定的,但这超出了这个答案的范围。
使用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
的方法很容易为两个或三个小类提供,但对于大类和层次结构来说却很难处理。第二种方法扩展得更好,但由于重复的函数指针而导致空间开销很大。 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;实施。这是一个非常有用的宏。