用C编写面向对象代码有哪些方法?特别是在多态性方面。


另请参阅此堆栈溢出问题C中的面向对象。


当前回答

关于c语言OOP的另一个变化,请参阅http://slkpg.byethost7.com/instance.html。它强调仅使用本机c实现实例数据的可重入性,多重继承使用函数包装器手动完成。保持型号安全。下面是一个小例子:

typedef struct _peeker
{
    log_t     *log;
    symbols_t *sym;
    scanner_t  scan;            // inherited instance
    peek_t     pk;
    int        trace;

    void    (*push) ( SELF *d, symbol_t *symbol );
    short   (*peek) ( SELF *d, int level );
    short   (*get)  ( SELF *d );
    int     (*get_line_number) ( SELF *d );

} peeker_t, SlkToken;

#define push(self,a)            (*self).push(self, a)
#define peek(self,a)            (*self).peek(self, a)
#define get(self)               (*self).get(self)
#define get_line_number(self)   (*self).get_line_number(self)

INSTANCE_METHOD
int
(get_line_number) ( peeker_t *d )
{
    return  d->scan.line_number;
}

PUBLIC
void
InitializePeeker ( peeker_t  *peeker,
                   int        trace,
                   symbols_t *symbols,
                   log_t     *log,
                   list_t    *list )
{
    InitializeScanner ( &peeker->scan, trace, symbols, log, list );
    peeker->log = log;
    peeker->sym = symbols;
    peeker->pk.current = peeker->pk.buffer;
    peeker->pk.count = 0;
    peeker->trace = trace;

    peeker->get_line_number = get_line_number;
    peeker->push = push;
    peeker->get = get;
    peeker->peek = peek;
}

其他回答

我有点晚了,但我想分享我在这个主题上的经验:我这些天在使用嵌入式的东西,我唯一的(可靠的)编译器是C,所以我想在我用C编写的嵌入式项目中应用面向对象的方法。

到目前为止,我看到的大多数解决方案都大量使用类型转换,因此我们失去了类型安全:如果你犯了错误,编译器不会帮助你。这是完全不能接受的。

我的要求是:

尽可能避免类型转换,这样我们就不会失去类型安全; 多态:我们应该能够使用虚方法,类的用户不应该知道某个特定的方法是否是虚的; 多重继承:我不经常使用它,但有时我确实希望某个类实现多个接口(或扩展多个超类)。

我在本文中详细解释了我的方法:C语言中的面向对象编程;另外,还有一个工具可以自动生成基类和派生类的样板代码。

我亲眼见过。我不推荐。c++最初是作为一个生成C代码的中间步骤的预处理器开始的。

本质上,您最终要做的是为所有方法创建一个调度表,其中存储函数引用。派生类需要复制这个分派表并替换您想要重写的条目,如果您的新“方法”想要调用基方法,则必须调用原始方法。最终,你要重写c++。

动物和狗的小例子:你镜像了c++的虚表机制(基本上是这样)。你还分离了分配和实例化(Animal_Alloc, Animal_New),所以我们不会多次调用malloc()。我们还必须显式地传递this指针。

如果你要做非虚函数,那就很简单了。你只是不需要将它们添加到虚函数表中,静态函数也不需要this指针。多重继承通常需要多个虚表来解决歧义。

此外,您应该能够使用setjmp/longjmp来进行异常处理。

struct Animal_Vtable{
    typedef void (*Walk_Fun)(struct Animal *a_This);
    typedef struct Animal * (*Dtor_Fun)(struct Animal *a_This);

    Walk_Fun Walk;
    Dtor_Fun Dtor;
};

struct Animal{
    Animal_Vtable vtable;

    char *Name;
};

struct Dog{
    Animal_Vtable vtable;

    char *Name; // Mirror member variables for easy access
    char *Type;
};

void Animal_Walk(struct Animal *a_This){
    printf("Animal (%s) walking\n", a_This->Name);
}

struct Animal* Animal_Dtor(struct Animal *a_This){
    printf("animal::dtor\n");
    return a_This;
}

Animal *Animal_Alloc(){
    return (Animal*)malloc(sizeof(Animal));
}

Animal *Animal_New(Animal *a_Animal){
    a_Animal->vtable.Walk = Animal_Walk;
    a_Animal->vtable.Dtor = Animal_Dtor;
    a_Animal->Name = "Anonymous";
    return a_Animal;
}

void Animal_Free(Animal *a_This){
    a_This->vtable.Dtor(a_This);

    free(a_This);
}

void Dog_Walk(struct Dog *a_This){
    printf("Dog walking %s (%s)\n", a_This->Type, a_This->Name);
}

Dog* Dog_Dtor(struct Dog *a_This){
    // Explicit call to parent destructor
    Animal_Dtor((Animal*)a_This);

    printf("dog::dtor\n");

    return a_This;
}

Dog *Dog_Alloc(){
    return (Dog*)malloc(sizeof(Dog));
}

Dog *Dog_New(Dog *a_Dog){
    // Explict call to parent constructor
    Animal_New((Animal*)a_Dog);

    a_Dog->Type = "Dog type";
    a_Dog->vtable.Walk = (Animal_Vtable::Walk_Fun) Dog_Walk;
    a_Dog->vtable.Dtor = (Animal_Vtable::Dtor_Fun) Dog_Dtor;

    return a_Dog;
}

int main(int argc, char **argv){
    /*
      Base class:

        Animal *a_Animal = Animal_New(Animal_Alloc());
    */
    Animal *a_Animal = (Animal*)Dog_New(Dog_Alloc());

    a_Animal->vtable.Walk(a_Animal);

    Animal_Free(a_Animal);
}

PS.这是在c++编译器上测试的,但是在C编译器上运行应该很容易。

是的。事实上,Axel Schreiner免费提供了他的书《ANSI-C中的面向对象编程》,这本书相当全面地涵盖了这个主题。

关于c语言OOP的另一个变化,请参阅http://slkpg.byethost7.com/instance.html。它强调仅使用本机c实现实例数据的可重入性,多重继承使用函数包装器手动完成。保持型号安全。下面是一个小例子:

typedef struct _peeker
{
    log_t     *log;
    symbols_t *sym;
    scanner_t  scan;            // inherited instance
    peek_t     pk;
    int        trace;

    void    (*push) ( SELF *d, symbol_t *symbol );
    short   (*peek) ( SELF *d, int level );
    short   (*get)  ( SELF *d );
    int     (*get_line_number) ( SELF *d );

} peeker_t, SlkToken;

#define push(self,a)            (*self).push(self, a)
#define peek(self,a)            (*self).peek(self, a)
#define get(self)               (*self).get(self)
#define get_line_number(self)   (*self).get_line_number(self)

INSTANCE_METHOD
int
(get_line_number) ( peeker_t *d )
{
    return  d->scan.line_number;
}

PUBLIC
void
InitializePeeker ( peeker_t  *peeker,
                   int        trace,
                   symbols_t *symbols,
                   log_t     *log,
                   list_t    *list )
{
    InitializeScanner ( &peeker->scan, trace, symbols, log, list );
    peeker->log = log;
    peeker->sym = symbols;
    peeker->pk.current = peeker->pk.buffer;
    peeker->pk.count = 0;
    peeker->trace = trace;

    peeker->get_line_number = get_line_number;
    peeker->push = push;
    peeker->get = get;
    peeker->peek = peek;
}