用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编译器上运行应该很容易。
其他回答
有几种技术可以使用。最重要的是如何分割项目。我们在项目中使用的接口是在.h文件中声明的,对象的实现是在.c文件中。重要的部分是,所有包含.h文件的模块都只将对象视为void *,而.c文件是唯一知道结构内部的模块。
以FOO类为例:
在。h文件中
#ifndef FOO_H_
#define FOO_H_
...
typedef struct FOO_type FOO_type; /* That's all the rest of the program knows about FOO */
/* Declaration of accessors, functions */
FOO_type *FOO_new(void);
void FOO_free(FOO_type *this);
...
void FOO_dosomething(FOO_type *this, param ...):
char *FOO_getName(FOO_type *this, etc);
#endif
C实现文件就像这样。
#include <stdlib.h>
...
#include "FOO.h"
struct FOO_type {
whatever...
};
FOO_type *FOO_new(void)
{
FOO_type *this = calloc(1, sizeof (FOO_type));
...
FOO_dosomething(this, );
return this;
}
因此,我明确地将指针指向该模块的每个函数。c++编译器隐式地完成它,而在C中我们显式地将它写出来。
我真的在我的程序中使用了这个,以确保我的程序不是用c++编译的,而且它在我的语法高亮编辑器中有另一种颜色的良好属性。
FOO_struct的字段可以在一个模块中修改,而另一个模块甚至不需要重新编译就仍然可用。
通过这种风格,我已经处理了OOP(数据封装)的很大一部分优点。通过使用函数指针,甚至可以很容易地实现继承之类的东西,但老实说,它真的很少有用。
是的,但我从未见过有人尝试用C实现任何类型的多态性。
命名空间通常通过以下方式实现:
stack_push(thing *)
而不是
stack::push(thing *)
要将C结构体变成类似c++类的东西,您可以转向:
class stack {
public:
stack();
void push(thing *);
thing * pop();
static int this_is_here_as_an_example_only;
private:
...
};
Into
struct stack {
struct stack_type * my_type;
// Put the stuff that you put after private: here
};
struct stack_type {
void (* construct)(struct stack * this); // This takes uninitialized memory
struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it
void (*push)(struct stack * this, thing * t); // Pushing t onto this stack
thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it
int this_is_here_as_an_example_only;
}Stack = {
.construct = stack_construct,
.operator_new = stack_operator_new,
.push = stack_push,
.pop = stack_pop
};
// All of these functions are assumed to be defined somewhere else
和做的事:
struct stack * st = Stack.operator_new(); // Make a new stack
if (!st) {
// Do something about it
} else {
// You can use the stack
stack_push(st, thing0); // This is a non-virtual call
Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push
st->my_type.push(st, thing2); // This is a virtual call
}
我没有做析构函数或删除,但它遵循相同的模式。
This_is_here_as_an_example_only类似于一个静态类变量——在一个类型的所有实例之间共享。所有的方法都是静态的,除了一些采用this *
哪些文章或书籍适合在C语言中使用面向对象的概念?
Dave Hanson的《C接口与实现》在封装和命名方面非常出色,在函数指针的使用方面也非常出色。Dave没有尝试模拟继承。
关于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;
}