答案 0 :(得分:341)
是。事实上,Axel Schreiner免费提供his book“ANSI-C中的面向对象编程”,它非常全面地介绍了这一主题。
答案 1 :(得分:318)
既然你在谈论多态性,那么是的,你可以,我们在C ++出现之前就已经做了那么多事情。
基本上,您使用struct
来保存数据和函数指针列表,以指向该数据的相关函数。
因此,在通信类中,您将进行开放,读取,写入和关闭调用,该调用将作为结构中的四个函数指针以及对象的数据维护,如:
typedef struct {
int (*open)(void *self, char *fspec);
int (*close)(void *self);
int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
// And data goes here.
} tCommClass;
tCommClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;
tCommClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;
当然,上面的代码段实际上是在rs232Init()
之类的“构造函数”中。
当你从该类“继承”时,只需将指针更改为指向自己的函数即可。每个调用这些函数的人都会通过函数指针来完成它,为你提供多态性:
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
有点像手动vtable。
通过将指针设置为NULL,您甚至可以拥有虚拟类 - 行为与C ++(运行时的核心转储而不是编译时的错误)略有不同。
这是一段演示它的示例代码。首先是顶级类结构:
#include <stdio.h>
// The top-level class.
typedef struct sCommClass {
int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;
然后我们有TCP'子类'的函数:
// Function for the TCP 'class'.
static int tcpOpen (tCommClass *tcp, char *fspec) {
printf ("Opening TCP: %s\n", fspec);
return 0;
}
static int tcpInit (tCommClass *tcp) {
tcp->open = &tcpOpen;
return 0;
}
还有HTTP的那个:
// Function for the HTTP 'class'.
static int httpOpen (tCommClass *http, char *fspec) {
printf ("Opening HTTP: %s\n", fspec);
return 0;
}
static int httpInit (tCommClass *http) {
http->open = &httpOpen;
return 0;
}
最后是一个测试程序,以显示它的实际效果:
// Test program.
int main (void) {
int status;
tCommClass commTcp, commHttp;
// Same 'base' class but initialised to different sub-classes.
tcpInit (&commTcp);
httpInit (&commHttp);
// Called in exactly the same manner.
status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
status = (commHttp.open)(&commHttp, "http://www.microsoft.com");
return 0;
}
这会产生输出:
Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com
所以你可以看到正在调用不同的函数,具体取决于子类。
答案 2 :(得分:81)
命名空间通常通过以下方式完成:
stack_push(thing *)
而不是
stack::push(thing *)
class stack {
public:
stack();
void push(thing *);
thing * pop();
static int this_is_here_as_an_example_only;
private:
...
};
向
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就像一个静态类变量 - 在一个类型的所有实例之间共享。所有方法都是静态的,除了有些方法采用这个*
答案 3 :(得分:53)
我相信除了有用之外,在C中实现OOP是学习 OOP并理解其内部工作的绝佳方式。许多程序员的经验表明,要有效和自信地使用技术,程序员必须了解最终如何实现基础概念。在C中模拟类,继承和多态就是这样教的。
要回答原始问题,这里有一些资源可以教授如何在C中进行OOP:
EmbeddedGurus.com博客文章“基于对象的C语言编程”展示了如何在便携式C中实现类和单继承: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c/
应用程序说明“”C +“ - C语言中的面向对象编程”展示了如何使用预处理器宏在C中实现类,单继承和后期绑定(多态): http://www.state-machine.com/resources/cplus_3.0_manual.pdf,示例代码可从http://www.state-machine.com/resources/cplus_3.0.zip
获取答案 4 :(得分:32)
我已经看到它完成了。我不推荐它。 C ++最初是以这种方式开始的,它将C代码作为中间步骤生成。
基本上,您最终要做的是为存储函数引用的所有方法创建一个调度表。派生一个类需要复制这个调度表并替换你想要覆盖的条目,新的“方法”必须调用原始方法才能调用基本方法。最终,你最终重写了C ++。
答案 5 :(得分:25)
答案 6 :(得分:17)
C stdio FILE子库是如何在纯粹的C中创建抽象,封装和模块化的一个很好的例子。
继承和多态 - 通常被认为对OOP至关重要的其他方面 - 不一定能提供他们所承诺的生产力增益,reasonable arguments been made他们实际上可以阻碍发展和思考关于问题领域。
答案 7 :(得分:15)
动物与狗的简单例子:你反映了C ++的vtable机制(主要是反正)。您还可以分配分配和实例化(Animal_Alloc,Animal_New),因此我们不会多次调用malloc()。我们还必须明确地传递this
指针。
如果你要做非虚拟功能,那就太过分了。您只是不将它们添加到vtable中,静态函数不需要this
指针。多重继承通常需要多个vtable来解决歧义。
此外,您应该能够使用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编译器上运行。
答案 8 :(得分:13)
结帐GObject。它意味着在C中是OO,并且是您正在寻找的一个实现。如果您真的想要OO,请使用C ++或其他一些OOP语言。如果你习惯于处理OO语言,GObject很难与之合作,但就像任何事情一样,你会习惯惯例和流程。
答案 9 :(得分:12)
这很有趣。我自己一直在思考同样的问题,思考它的好处是:
试图想象如何用非OOP语言实现OOP概念有助于我理解OOp语言的优势(在我的例子中,C ++)。这有助于我更好地判断是否在特定类型的应用程序中使用C或C ++ - 其中一个的好处超过另一个应用程序。
在浏览网页以获取有关此信息和意见时,我找到了一位为嵌入式处理器编写代码的作者,并且只提供了一个C编译器: http://www.eetimes.com/discussion/other/4024626/Object-Oriented-C-Creating-Foundation-Classes-Part-1
在他的案例中,分析和调整普通C中的OOP概念是一种有效的追求。由于尝试在C中实现它们而导致的性能开销,他似乎愿意牺牲一些OOP概念。
我所接受的教训是,是的,它可以在一定程度上完成,是的,有一些很好的理由可以尝试。
最后,机器处理堆栈指针位,使程序计数器跳转并计算内存访问操作。从效率的角度来看,你的程序执行的这些计算越少越好......但有时我们必须支付这个税,因此我们可以以最不容易出现人为错误的方式组织我们的程序。 OOP语言编译器努力优化这两个方面。程序员必须更加谨慎地用C语言实现这些概念。
答案 10 :(得分:10)
有几种技术可以使用。最重要的是如何拆分项目。我们在项目中使用一个接口,该接口在.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中我们将它明确地写出来。
我真的在我的程序中使用this
,以确保我的程序不能用C ++编译,并且它具有在我的语法高亮编辑器中使用另一种颜色的优良特性。
可以在一个模块中修改FOO_struct的字段,甚至不需要重新编译另一个模块以使其仍然可用。
使用这种风格,我已经处理了OOP(数据封装)的很大一部分优势。通过使用函数指针,它甚至可以很容易地实现继承,但老实说,它实际上很少有用。
答案 11 :(得分:10)
您可能会发现查看Apple的Core Foundation API集文档很有帮助。它是纯C API,但许多类型都桥接到Objective-C对象等价物。
您可能还会发现查看Objective-C本身的设计会很有帮助。它与C ++有点不同之处在于,对象系统是根据C函数定义的,例如: objc_msg_send
调用对象上的方法。编译器将方括号语法转换为那些函数调用,因此您不必知道它,但考虑到您的问题,您可能会发现它有助于了解它是如何工作的。
答案 12 :(得分:7)
如果您确信OOP方法优于您要解决的问题,为什么要尝试使用非OOP语言解决它?看起来你正在使用错误的工具来完成工作。使用C ++或其他一些面向对象的C变体语言。
如果您因为开始使用C编写的现有大型项目进行编码而要求,那么您不应该尝试将自己(或其他任何人)的OOP范例强制插入到项目的基础架构中。遵循项目中已存在的准则。一般而言,干净的API和隔离的库和模块将大大有助于实现干净的OOP- ish 设计。
在完成所有这些之后,如果你真的开始做OOP C,请阅读this(PDF)。
答案 13 :(得分:7)
面向对象的C,可以完成,我已经看到了韩国生产的那种代码,这是我多年来看到的最恐怖的怪物(就像去年(2007年)我看到的那样码)。 所以是的,它可以做到,是的,人们之前已经做过,甚至在这个时代也做到了。但我推荐使用C ++或Objective-C,这两种语言都是C语言的诞生,其目的是提供不同范式的面向对象。
答案 14 :(得分:7)
你可以使用函数指针伪造它,事实上,我认为理论上可以将C ++程序编译成C语言。
然而,强迫语言范式而不是选择使用范式的语言很少有意义。
答案 15 :(得分:6)
当然,它不会像使用内置支持的语言一样漂亮。我甚至写过“面向对象的汇编程序”。
答案 16 :(得分:6)
是的,你可以。人们在C ++之前编写面向对象的C或Objective-C出现在现场。 C ++和Objective-C在某些方面都试图采用C中使用的一些OO概念,并将它们形式化为语言的一部分。
这是一个非常简单的程序,它展示了如何制作看起来像/是方法调用的东西(有更好的方法来实现这一点。这只是语言支持概念的证据):
#include<stdio.h>
struct foobarbaz{
int one;
int two;
int three;
int (*exampleMethod)(int, int);
};
int addTwoNumbers(int a, int b){
return a+b;
}
int main()
{
// Define the function pointer
int (*pointerToFunction)(int, int) = addTwoNumbers;
// Let's make sure we can call the pointer
int test = (*pointerToFunction)(12,12);
printf ("test: %u \n", test);
// Now, define an instance of our struct
// and add some default values.
struct foobarbaz fbb;
fbb.one = 1;
fbb.two = 2;
fbb.three = 3;
// Now add a "method"
fbb.exampleMethod = addTwoNumbers;
// Try calling the method
int test2 = fbb.exampleMethod(13,36);
printf ("test2: %u \n", test2);
printf("\nDone\n");
return 0;
}
答案 17 :(得分:6)
要添加的小OOC代码:
#include <stdio.h>
struct Node {
int somevar;
};
void print() {
printf("Hello from an object-oriented C method!");
};
struct Tree {
struct Node * NIL;
void (*FPprint)(void);
struct Node *root;
struct Node NIL_t;
} TreeA = {&TreeA.NIL_t,print};
int main()
{
struct Tree TreeB;
TreeB = TreeA;
TreeB.FPprint();
return 0;
}
答案 18 :(得分:5)
我一直在挖这个一年:
由于GObject系统很难与纯C一起使用,我尝试编写一些不错的宏来简化C语言的OO风格。
#include "OOStd.h"
CLASS(Animal) {
char *name;
STATIC(Animal);
vFn talk;
};
static int Animal_load(Animal *THIS,void *name) {
THIS->name = name;
return 0;
}
ASM(Animal, Animal_load, NULL, NULL, NULL)
CLASS_EX(Cat,Animal) {
STATIC_EX(Cat, Animal);
};
static void Meow(Animal *THIS){
printf("Meow!My name is %s!\n", THIS->name);
}
static int Cat_loadSt(StAnimal *THIS, void *PARAM){
THIS->talk = (void *)Meow;
return 0;
}
ASM_EX(Cat,Animal, NULL, NULL, Cat_loadSt, NULL)
CLASS_EX(Dog,Animal){
STATIC_EX(Dog, Animal);
};
static void Woof(Animal *THIS){
printf("Woof!My name is %s!\n", THIS->name);
}
static int Dog_loadSt(StAnimal *THIS, void *PARAM) {
THIS->talk = (void *)Woof;
return 0;
}
ASM_EX(Dog, Animal, NULL, NULL, Dog_loadSt, NULL)
int main(){
Animal *animals[4000];
StAnimal *f;
int i = 0;
for (i=0; i<4000; i++)
{
if(i%2==0)
animals[i] = NEW(Dog,"Jack");
else
animals[i] = NEW(Cat,"Lily");
};
f = ST(animals[0]);
for(i=0; i<4000; ++i) {
f->talk(animals[i]);
}
for (i=0; i<4000; ++i) {
DELETE0(animals[i]);
}
return 0;
}
这是我的项目网站(我没有足够的时间来编写en.doc,但是中文文档要好得多。)
答案 19 :(得分:4)
您可能想要做的一件事是研究Xt X Window工具包的实现。当然它在牙齿上变得越来越长,但是许多使用的结构被设计成在传统的C中以OO方式工作。通常这意味着在这里和那里添加额外的间接层并设计结构以相互叠加。
你可以通过这种方式在OO中做很多事情,尽管感觉有时候,OO概念并没有完全由#include<favorite_OO_Guru.h>
的思想形成。它们确实构成了当时许多既定的最佳实践。面向对象的语言和系统只是提炼和放大了当时编程时代精神的一部分。
答案 20 :(得分:4)
问题的答案是'是的,你可以'。
面向对象的C(OOC)工具包适用于那些希望以面向对象的方式进行编程的人,但也适用于那些优秀的旧C语言。 OOC实现了类,单个和多个继承,异常处理。
功能强>
•仅使用C宏和函数,不需要语言扩展! (ANSI-C)
•易于阅读的应用程序源代码。注意尽可能简化事情。
•类的单继承
•接口和mixins的多重继承(自1.3版本起)
•实施例外(纯粹的C!)
•班级的虚拟功能
•易于实施的外部工具
有关详细信息,请访问http://ooc-coding.sourceforge.net/。
答案 21 :(得分:4)
OOP只是一种范例,它使数据比程序中的代码更重要。 OOP不是一种语言。因此,就像普通C是一种简单的语言一样,普通C中的OOP也很简单。
答案 22 :(得分:4)
似乎人们正在尝试使用C来模拟C ++风格。我的看法是做面向对象的编程C实际上正在进行面向结构的编程。但是,您可以实现后期绑定,封装和继承等功能。对于继承,您明确定义了一个指向子结构中基本结构的指针,这显然是一种多重继承的形式。您还需要确定是否
//private_class.h
struct private_class;
extern struct private_class * new_private_class();
extern int ret_a_value(struct private_class *, int a, int b);
extern void delete_private_class(struct private_class *);
void (*late_bind_function)(struct private_class *p);
//private_class.c
struct inherited_class_1;
struct inherited_class_2;
struct private_class {
int a;
int b;
struct inherited_class_1 *p1;
struct inherited_class_2 *p2;
};
struct inherited_class_1 * new_inherited_class_1();
struct inherited_class_2 * new_inherited_class_2();
struct private_class * new_private_class() {
struct private_class *p;
p = (struct private_class*) malloc(sizeof(struct private_class));
p->a = 0;
p->b = 0;
p->p1 = new_inherited_class_1();
p->p2 = new_inherited_class_2();
return p;
}
int ret_a_value(struct private_class *p, int a, int b) {
return p->a + p->b + a + b;
}
void delete_private_class(struct private_class *p) {
//release any resources
//call delete methods for inherited classes
free(p);
}
//main.c
struct private_class *p;
p = new_private_class();
late_bind_function = &implementation_function;
delete_private_class(p);
使用c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj
进行编译。
所以建议坚持纯C风格,而不是试图强制进入C ++风格。这种方式也适用于构建API的非常简洁的方法。
答案 23 :(得分:4)
哪些文章或书籍在C中使用OOP概念是好的?
Dave Hanson的C Interfaces and Implementations在封装和命名方面非常出色,并且非常善于使用函数指针。戴夫并不试图模拟继承。
答案 24 :(得分:4)
在Jim Section 312 Programming Lunchtime Seminar {Jimis Larson 1996年的演讲中使用C进行继承的一个例子:High and Low-Level C。
答案 25 :(得分:2)
请参阅http://slkpg.byethost7.com/instance.html,了解C中OOP的另一个转折。它强调使用本机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;
}
答案 26 :(得分:2)
我参加派对有点晚了,但是我想分享一下我对这个主题的经验:这些天我使用嵌入式东西,而我唯一的(可靠的)编译器是C,所以我想申请我用C编写的嵌入式项目中的面向对象方法。
到目前为止,我见过的大多数解决方案都使用了类型转换,因此我们失去了类型安全性:如果你犯了错误,编译器将无法帮助你。这是完全不可接受的。
我的要求:
我在本文中详细解释了我的方法:Object-oriented programming in C;另外,还有一个实用程序可以自动生成基类和派生类的样板代码。
答案 27 :(得分:2)
我建立了一个小图书馆,在那里我尝试过,对我来说它很有效。所以我想我分享经验。
https://github.com/thomasfuhringer/oxygen
单继承可以使用结构很容易地实现,并为每个其他子类扩展它。对父结构的简单转换使得可以在所有后代上使用父方法。 只要你知道一个变量指向一个持有这种对象的结构,你总是可以转换为根类并进行内省。
正如已经提到的,虚拟方法有点棘手。但他们是可行的。为了简单起见,我只使用类描述结构中的一系列函数,每个子类都复制并重新填充所需的各个插槽。
实现多重继承会相当复杂,并会对性能产生重大影响。所以我离开了。我认为在很多情况下干净地模拟现实生活环境是可取和有用的,但在90%的情况下,单一继承可以满足需求。单继承很简单,不需要任何费用。
我也不关心类型安全。我认为你不应该依赖编译器来防止编程错误。而且无论如何它只会掩盖一小部分错误。
通常,在面向对象的环境中,您还希望实现引用计数,以尽可能自动化内存管理。所以我还将引用计数放入“对象”根类和一些封装堆内存分配和释放的功能。
这一切都非常简单和精益,并且给了我OO的基本要素而不强迫我处理C ++的怪物。而且我保留了留在C land的灵活性,除此之外,它还可以更容易地集成第三方库。
答案 28 :(得分:1)
答案 29 :(得分:0)
我认为首先要说的是(至少恕我直言)C的函数指针实现真的很难用。我会跳过一大堆箍以避免函数指针...
说,我认为其他人说的很不错。你有结构,你有模块,而不是foo->method(a,b,c)
,你最终得到method(foo,a,b,c)
如果你有多个带有“方法”方法的类型,那么你可以在前面添加类型,所以{ {1}},正如其他人所说的......好好利用.h文件,你可以私密和公开等等。
现在,这项技术有一些东西不会给你。它不会为您提供私人数据字段。我认为,你必须要有意志力和良好的编码卫生......而且,用这种方法进行继承并不容易。
至少这些是容易的部分...其余的,我认为是90/10的情况。 10%的福利需要90%的工作......
答案 30 :(得分:0)
是的,这是可能的。
这是纯C,没有宏预处理。它具有继承,多态,数据封装(包括私有数据)。它没有等效的受保护限定符,这意味着私有数据也是继承链中的私有数据。
#include "triangle.h"
#include "rectangle.h"
#include "polygon.h"
#include <stdio.h>
int main()
{
Triangle tr1= CTriangle->new();
Rectangle rc1= CRectangle->new();
tr1->width= rc1->width= 3.2;
tr1->height= rc1->height= 4.1;
CPolygon->printArea((Polygon)tr1);
printf("\n");
CPolygon->printArea((Polygon)rc1);
}
/*output:
6.56
13.12
*/
答案 31 :(得分:0)
是的,但我从未见过有人试图用C实现任何形式的多态性。