การเขียนโค้ดเชิงวัตถุใน C มีวิธีใดบ้าง โดยเฉพาะในเรื่องของ polymorphism
ดูเพิ่มเติมนี้กองมากเกินคำถามวัตถุปฐมนิเทศใน C
การเขียนโค้ดเชิงวัตถุใน C มีวิธีใดบ้าง โดยเฉพาะในเรื่องของ polymorphism
ดูเพิ่มเติมนี้กองมากเกินคำถามวัตถุปฐมนิเทศใน C
คำตอบ:
ใช่. ในความเป็นจริง Axel Schreiner นำเสนอหนังสือ "การเขียนโปรแกรมเชิงวัตถุใน ANSI-C" ของหนังสือของเขาฟรี
เนื่องจากคุณกำลังพูดถึงพหุสัณฐานแล้วใช่คุณสามารถเรากำลังทำสิ่งที่เรียงลำดับปีก่อนที่ C ++ มา
โดยทั่วไปคุณใช้ a 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 'subclass':
// 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
เพื่อให้คุณเห็นว่ามีการเรียกใช้ฟังก์ชันที่แตกต่างกันขึ้นอยู่กับคลาสย่อย
tCommClass
เปลี่ยนชื่อของคุณtCommVT
และโครงสร้างtCommClass
จะมีเพียงเขตข้อมูลและเขตข้อมูลเดียวtCommVT vt
ชี้ไปที่ตารางเสมือน "หนึ่งเดียวเท่านั้น" การใช้พอยน์เตอร์ทุกตัวในแต่ละอินสแตนซ์จะเพิ่มค่าใช้จ่ายที่ไม่จำเป็นและคล้ายกับวิธีการทำสิ่งต่างๆใน JavaScript มากกว่า C ++, IMHO
เนมสเปซมักจะทำโดยทำ:
stack_push(thing *)
แทน
stack::push(thing *)
ที่จะทำให้C struct เป็นสิ่งที่เหมือนC ++ระดับคุณสามารถเปิด:
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 เป็นเหมือนตัวแปรคลาสแบบสแตติก - แบ่งใช้ระหว่างอินสแตนซ์ทั้งหมดของชนิด วิธีการทั้งหมดเป็นแบบคงที่จริงๆยกเว้นบางวิธีใช้ *
st->my_type->push(st, thing2);
แทนที่จะเป็นst->my_type.push(st, thing2);
struct stack_type my_type;
แทนstruct stack_type * my_type;
Class
ล่ะ? นั่นจะทำให้ OO C มีไดนามิกมากกว่า C ++ แล้วมันล่ะ? อย่างไรก็ตาม +1
ฉันเชื่อว่านอกเหนือจากการมีประโยชน์ในสิทธิของตนเองการใช้ OOP ใน C เป็นวิธีที่ยอดเยี่ยมในการเรียนรู้ OOP และเข้าใจการทำงานภายใน ประสบการณ์ของโปรแกรมเมอร์หลายคนแสดงให้เห็นว่าการใช้เทคนิคอย่างมีประสิทธิภาพและมั่นใจผู้เขียนโปรแกรมจะต้องเข้าใจว่าแนวคิดพื้นฐานถูกนำไปใช้อย่างไรในท้ายที่สุด การเลียนแบบคลาสการสืบทอดและความหลากหลายใน C สอนแค่นี้
เพื่อตอบคำถามเดิมต่อไปนี้เป็นแหล่งข้อมูลคู่ที่สอนวิธีการทำ OOP ใน C:
EmbeddedGurus.com บล็อกโพสต์ "การเขียนโปรแกรมเชิงวัตถุใน C" แสดงวิธีใช้คลาสและการสืบทอดเดี่ยวในพกพา C: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c /
หมายเหตุของแอปพลิเคชัน "" C + "- การเขียนโปรแกรมเชิงวัตถุใน C" แสดงวิธีการใช้งานคลาสการสืบทอดเดี่ยวและการรวมภายหลัง (polymorphism) ใน C โดยใช้มาโครตัวประมวลผลล่วงหน้า: http://www.state-machine.com/resources/cplus_3 0_manual.pdfโค้ดตัวอย่างสามารถดูได้จากhttp://www.state-machine.com/resources/cplus_3.0.zip
ฉันเห็นมันเสร็จแล้ว ฉันจะไม่แนะนำที่นี่ C ++ เริ่มต้นด้วยวิธีนี้ในฐานะ preprocessor ที่สร้างรหัส C เป็นขั้นตอนกลาง
สิ่งสำคัญที่คุณต้องทำคือสร้างตารางส่งข้อมูลสำหรับวิธีการทั้งหมดที่คุณเก็บการอ้างอิงฟังก์ชันของคุณ การรับคลาสจะทำให้เกิดการคัดลอกตารางการแจกจ่ายนี้และแทนที่รายการที่คุณต้องการแทนที่ด้วย "เมธอด" ใหม่ของคุณที่มีการเรียกเมธอดดั้งเดิมหากต้องการเรียกใช้เมธอดพื้นฐาน ในที่สุดคุณก็สิ้นสุดการเขียน C ++ ใหม่
glib
เขียนใน C ในทางที่เป็นวัตถุประสงค์?
ไลบรารีย่อย C stdio FILE เป็นตัวอย่างที่ยอดเยี่ยมของวิธีการสร้างสิ่งที่เป็นนามธรรม, การห่อหุ้มและการแยกส่วนในซีที่ไม่บริสุทธิ์
มรดกและ polymorphism - ด้านอื่น ๆ มักจะคิดว่าสิ่งสำคัญที่จะ OOP - ไม่จำเป็นต้องให้กำไรจากการผลิตที่พวกเขาสัญญาและเหมาะสม ข้อโต้แย้งได้รับการทำที่พวกเขาสามารถพัฒนาจริงขัดขวางและความคิดเกี่ยวกับปัญหาโดเมน
ตัวอย่างเล็กน้อยกับสัตว์และสุนัข: คุณสะท้อนกลไก vtable ของ C ++ (ส่วนใหญ่แล้ว) คุณยังแยกการจัดสรรและอินสแตนซ์ (Animal_Alloc, Animal_New) ดังนั้นเราจึงไม่เรียก malloc () หลายครั้ง เราต้องผ่านthis
ตัวชี้อย่างชัดเจนด้วย
หากคุณต้องทำฟังก์ชั่นที่ไม่ใช่เสมือน คุณเพียงแค่ไม่เพิ่มลงใน vtable และฟังก์ชั่นคงที่ไม่จำเป็นต้องมีthis
ตัวชี้ โดยทั่วไปการสืบทอดหลายรายการต้องใช้หลาย vtables เพื่อแก้ไขความกำกวม
นอกจากนี้คุณควรใช้ 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
typedef
ภายใน a struct
เป็นไปไม่ได้ใน C.
สิ่งนี้น่าสนใจที่จะอ่าน ฉันได้ไตร่ตรองคำถามเดียวกันด้วยตัวเองและประโยชน์ของการคิดเกี่ยวกับมันคือ:
การพยายามจินตนาการถึงวิธีการใช้แนวคิด OOP ในภาษาที่ไม่ใช่ OOP ช่วยให้ฉันเข้าใจจุดแข็งของภาษา OOp (ในกรณีของฉัน C ++) สิ่งนี้ช่วยให้ฉันตัดสินใจได้ดียิ่งขึ้นว่าจะใช้ C หรือ C ++ สำหรับแอปพลิเคชันประเภทที่กำหนดหรือไม่
ในการเรียกดูเว็บสำหรับข้อมูลและความคิดเห็นเกี่ยวกับเรื่องนี้ฉันพบผู้เขียนที่เขียนโค้ดสำหรับตัวประมวลผลแบบฝังตัวและมีคอมไพเลอร์ C เท่านั้น: http://www.eetimes.com/discussion/other/4024626/Object-Oriented -C-สร้าง-มูลนิธิเรียน-Part-1
ในกรณีของเขาการวิเคราะห์และการปรับแนวคิด OOP ใน C ธรรมดาเป็นการแสวงหาที่ถูกต้อง ดูเหมือนว่าเขาจะเปิดให้เสียสละแนวคิด OOP เนื่องจากผลการดำเนินงานค่าใช้จ่ายที่เกิดจากความพยายามที่จะใช้พวกเขาใน
บทเรียนที่ฉันทำคือใช่สามารถทำได้ในระดับหนึ่งและใช่มีเหตุผลดีๆที่ควรลอง
ในที่สุดเครื่องจะทำงานสองบิตตัวชี้สแต็กทำให้โปรแกรมเคาน์เตอร์กระโดดไปรอบ ๆ และการคำนวณการเข้าถึงหน่วยความจำ จากมุมมองด้านประสิทธิภาพการคำนวณที่น้อยลงของโปรแกรมของคุณดีกว่า ... แต่บางครั้งเราต้องจ่ายภาษีนี้เพียงเพื่อให้เราสามารถจัดระเบียบโปรแกรมของเราในแบบที่ทำให้เกิดความผิดพลาดน้อยที่สุดต่อมนุษย์ คอมไพเลอร์ภาษา OOP พยายามเพิ่มประสิทธิภาพทั้งสองด้าน โปรแกรมเมอร์จะต้องมีความระมัดระวังมากขึ้นในการใช้แนวคิดเหล่านี้ในภาษาเช่น C
คุณอาจพบว่ามีประโยชน์ในการดูเอกสารประกอบของ Apple สำหรับชุด Core Foundation ของ API มันเป็น C API บริสุทธิ์ แต่มีหลายชนิดที่เชื่อมต่อกับ Objective-C วัตถุเทียบเท่า
คุณอาจพบว่ามีประโยชน์ในการดูการออกแบบของ Objective-C มันแตกต่างจาก C ++ เล็กน้อยซึ่งระบบวัตถุนั้นถูกกำหนดในแง่ของฟังก์ชั่น C เช่นobjc_msg_send
การเรียกใช้เมธอดบนวัตถุ คอมไพเลอร์แปลไวยากรณ์ของวงเล็บเหลี่ยมเป็นการเรียกใช้ฟังก์ชันเหล่านั้นดังนั้นคุณไม่จำเป็นต้องรู้ แต่เมื่อพิจารณาคำถามของคุณคุณอาจพบว่ามีประโยชน์ในการเรียนรู้วิธีการทำงานภายใต้ประทุน
มีหลายเทคนิคที่สามารถใช้ได้ สิ่งที่สำคัญที่สุดคือวิธีแยกโครงการ เราใช้อินเทอร์เฟซในโครงการของเราซึ่งประกาศในไฟล์. 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 (การห่อหุ้มข้อมูล) ด้วยการใช้ตัวชี้ฟังก์ชันทำให้ง่ายต่อการใช้งานบางอย่างเช่นการสืบทอด แต่จริงๆแล้วมันมีประโยชน์เพียงเล็กน้อยเท่านั้น
typedef struct FOO_type FOO_type
แทน typedef เป็นโมฆะในส่วนหัวคุณจะได้รับประโยชน์เพิ่มเติมจากการตรวจสอบชนิดในขณะที่ยังไม่เปิดเผยโครงสร้างของคุณ
คุณสามารถปลอมมันโดยใช้ตัวชี้ฟังก์ชั่นและในความเป็นจริงฉันคิดว่ามันเป็นไปได้ในทางทฤษฎีในการรวบรวมโปรแกรม C ++ ลงใน C
อย่างไรก็ตามมันไม่ค่อยสมเหตุสมผลที่จะบังคับกระบวนทัศน์เกี่ยวกับภาษาแทนที่จะเลือกภาษาที่ใช้กระบวนทัศน์
Object oriented C สามารถทำได้ฉันได้เห็นโค้ดประเภทนี้ในการผลิตในเกาหลีและมันเป็นสัตว์ประหลาดที่น่ากลัวที่สุดที่ฉันเคยเห็นในปีที่ผ่านมา ใช่แล้วสามารถทำได้และมีผู้คนเคยทำมาแล้วและยังคงทำได้แม้ในยุคนี้ แต่ฉันขอแนะนำ C ++ หรือ Objective-C ทั้งสองเป็นภาษาที่เกิดจาก C โดยมีวัตถุประสงค์เพื่อให้การวางแนววัตถุที่มีกระบวนทัศน์ที่แตกต่างกัน
หากคุณมั่นใจว่าวิธีการของ OOP นั้นเหนือกว่าปัญหาที่คุณพยายามแก้ไขทำไมคุณถึงพยายามที่จะแก้ไขด้วยภาษาที่ไม่ใช่แบบ OOP ดูเหมือนว่าคุณกำลังใช้เครื่องมือที่ผิดสำหรับงาน ใช้ C ++ หรือภาษาอื่น ๆ ในภาษา C-object
หากคุณกำลังถามเพราะคุณกำลังเริ่มเขียนโค้ดสำหรับโครงการขนาดใหญ่ที่มีอยู่แล้วที่เขียนไว้ใน C คุณไม่ควรพยายามบังคับกระบวนทัศน์ OOP ของคุณเอง (หรือบุคคลอื่น) เข้าสู่โครงสร้างพื้นฐานของโครงการ ทำตามแนวทางที่มีอยู่แล้วในโครงการ โดยทั่วไป APIs ที่สะอาดและห้องสมุดแยกและโมดูลจะไปทางยาวต่อการมีการทำความสะอาด OOP- ishการออกแบบ
หากหลังจากทั้งหมดนี้คุณพร้อมที่จะทำ OOP C แล้วให้อ่านสิ่งนี้ (PDF)
ใช่คุณสามารถ. ผู้คนกำลังเขียน Object-oriented C ก่อนที่ C ++ หรือObjective-C จะมาในที่เกิดเหตุ ทั้ง C ++ และ Objective-C เป็นบางส่วนพยายามที่จะใช้แนวคิด OO บางอย่างที่ใช้ใน C และทำให้เป็นทางการเป็นส่วนหนึ่งของภาษา
ต่อไปนี้เป็นโปรแกรมที่ใช้งานง่ายซึ่งแสดงให้เห็นว่าคุณสามารถทำสิ่งที่มีลักษณะเหมือน / เป็นการเรียกใช้เมธอด (มีวิธีที่ดีกว่าในการทำสิ่งนี้เป็นเพียงการพิสูจน์ว่าภาษารองรับแนวคิด):
#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;
}
แน่นอนว่ามันจะไม่สวยเท่าการใช้ภาษาที่มีการสนับสนุนในตัว ฉันยังเขียน "แอสเซมเบลอร์เชิงวัตถุ"
รหัส 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;
}
ฉันขุดมาเป็นเวลาหนึ่งปี:
เนื่องจากระบบ GObject นั้นใช้งานยากกับ C บริสุทธิ์ฉันจึงพยายามเขียนมาโครที่ดีเพื่อบรรเทาสไตล์ OO กับ C
#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 แต่เอกสารภาษาจีนดีกว่ามาก)
มีตัวอย่างของการถ่ายทอดทางพันธุกรรมโดยใช้ C ในจิม Larson 1996 พูดคุยให้ที่เป็นมาตรา 312 Programming อาหารกลางวันสัมมนา : ที่นี่สูงและต่ำระดับ C
บทความหรือหนังสือใดดีที่ใช้แนวคิด OOP ใน C
การเชื่อมต่อและการนำ C มาใช้ของ Dave Hanson นั้นยอดเยี่ยมในการห่อหุ้มและตั้งชื่อและใช้งานตัวชี้ฟังก์ชั่นได้ดีมาก เดฟไม่พยายามจำลองมรดก
OOP เป็นเพียงกระบวนทัศน์ที่ทำให้ข้อมูลมีความสำคัญมากกว่ารหัสในโปรแกรม OOP ไม่ใช่ภาษา ดังนั้นเช่นธรรมดา C เป็นภาษาง่าย OOP ในธรรมดา C ก็ง่ายเกินไป
สิ่งหนึ่งที่คุณอาจต้องการที่จะทำคือการมองเข้าไปในการดำเนินงานของXtเครื่องมือสำหรับวินโดว์ แน่ใจว่ามันใช้เวลานานในการฟัน แต่โครงสร้างที่ใช้จำนวนมากถูกออกแบบมาเพื่อทำงานในแบบ OO ในแบบดั้งเดิม C. โดยทั่วไปหมายถึงการเพิ่มชั้นพิเศษของทางอ้อมที่นี่และที่นั่นและการออกแบบโครงสร้างเพื่อวางซ้อนกัน
จริงๆคุณสามารถทำจำนวนมากในทางของ OO อยู่ใน C วิธีนี้แม้ว่ามันจะรู้สึกเหมือนมันบางครั้งแนวคิด OO #include<favorite_OO_Guru.h>
ไม่ได้ฤดูใบไม้ผลิที่เกิดขึ้นอย่างเต็มที่จากความคิดของ พวกเขาประกอบด้วยการฝึกฝนที่ดีที่สุดหลายครั้ง ภาษาและระบบ OO จะกลั่นและขยายส่วนของโปรแกรม zeitgeist ประจำวันเท่านั้น
คำตอบของคำถามคือ 'ใช่คุณทำได้'
ชุด Object-oriented C (OOC) สำหรับผู้ที่ต้องการเขียนโปรแกรมในลักษณะเชิงวัตถุ แต่ยึดติดกับ C เก่าที่ดีเช่นกัน OOC ใช้คลาสการสืบทอดเดี่ยวและหลายการจัดการข้อยกเว้น
คุณสมบัติ
•ใช้มาโคร C เท่านั้นและฟังก์ชั่นไม่จำเป็นต้องใช้นามสกุลภาษา! (ANSI-C)
•ซอร์สโค้ดที่อ่านง่ายสำหรับแอปพลิเคชันของคุณ การดูแลก็ทำสิ่งที่ง่ายที่สุดเท่าที่จะทำได้
•การสืบทอดคลาสเดียว
•การสืบทอดหลายรายการโดยอินเทอร์เฟซและมิกซ์อิน (ตั้งแต่เวอร์ชั่น 1.3)
•ใช้ข้อยกเว้น (ใน pure C!)
•ฟังก์ชั่นเสมือนจริงสำหรับชั้นเรียน
•เครื่องมือภายนอกสำหรับการใช้งานในชั้นเรียนได้ง่าย
สำหรับรายละเอียดเพิ่มเติมเยี่ยมชมhttp://ooc-coding.sourceforge.net/
ดูเหมือนว่าผู้คนพยายามเลียนแบบสไตล์ 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
ดูที่http://slkpg.byethost7.com/instance.htmlอีกครั้งสำหรับ OOP ใน C. มันจะเน้นข้อมูลตัวอย่างสำหรับการแสดงความเคารพโดยใช้เพียงแค่ดั้งเดิม 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.
วิธีแก้ปัญหาส่วนใหญ่ที่ฉันเคยเห็นใช้ typecasts อย่างหนักดังนั้นเราจึงขาดความปลอดภัยประเภท: คอมไพเลอร์จะไม่ช่วยคุณถ้าคุณทำผิดพลาด สิ่งนี้ไม่สามารถยอมรับได้อย่างสมบูรณ์
ข้อกำหนดที่ฉันมี:
ฉันได้อธิบายวิธีการอย่างละเอียดในบทความนี้แล้ว: การเขียนโปรแกรมเชิงวัตถุใน C ; นอกจากนี้ยังมีโปรแกรมอรรถประโยชน์สำหรับการสร้างรหัสสำเร็จรูปสำหรับฐานและคลาสที่ได้รับ
ฉันสร้างห้องสมุดเล็ก ๆ ที่ฉันลองและมันก็ใช้งานได้จริงสำหรับฉัน ดังนั้นฉันคิดว่าฉันแบ่งปันประสบการณ์
https://github.com/thomasfuhringer/oxygen
การสืบทอดเดี่ยวสามารถดำเนินการได้ค่อนข้างง่ายโดยใช้โครงสร้างและขยายสำหรับคลาสย่อยอื่น ๆ การส่งแบบง่ายไปยังโครงสร้างพาเรนต์ทำให้สามารถใช้เมธอดพาเรนต์ในการสืบทอดทั้งหมด ตราบใดที่คุณรู้ว่าตัวแปรชี้ไปที่โครงสร้างที่เก็บวัตถุชนิดนี้คุณสามารถส่งไปยังคลาสรูทและทำการวิปัสสนา
ดังที่ได้กล่าวมาแล้ววิธีการเสมือนนั้นค่อนข้างซับซ้อนกว่า แต่พวกเขาทำได้ เพื่อให้ง่ายขึ้นฉันแค่ใช้ฟังก์ชั่นมากมายในโครงสร้างคำอธิบายคลาสที่ทุกคลาสเด็กคัดลอกและ repopulate แต่ละสล็อตเมื่อจำเป็น
การรับมรดกหลายรายการค่อนข้างซับซ้อนในการดำเนินการและมาพร้อมกับผลกระทบด้านประสิทธิภาพที่สำคัญ ดังนั้นฉันจะทิ้งมันไว้ ฉันคิดว่ามันเป็นที่น่าพอใจและมีประโยชน์ในหลาย ๆ กรณีเพื่อทำแบบจำลองสถานการณ์ในชีวิตจริงให้เรียบร้อย แต่ใน 90% ของกรณีการสืบทอดเดี่ยวอาจครอบคลุมความต้องการ และการสืบทอดเดี่ยวนั้นง่ายและไม่เสียค่าใช้จ่ายใด ๆ
นอกจากนี้ฉันไม่สนใจเกี่ยวกับความปลอดภัยของประเภท ฉันคิดว่าคุณไม่ควรพึ่งพาคอมไพเลอร์เพื่อป้องกันคุณจากการเขียนโปรแกรมผิดพลาด และจะป้องกันคุณจากข้อผิดพลาดเพียงเล็กน้อยเท่านั้น
โดยทั่วไปในสภาพแวดล้อมเชิงวัตถุคุณต้องการใช้การนับการอ้างอิงเพื่อทำให้การจัดการหน่วยความจำโดยอัตโนมัติในขอบเขตที่เป็นไปได้ ดังนั้นฉันจึงใส่การอ้างอิงลงในคลาสรูท“ Object” และฟังก์ชันบางอย่างเพื่อห่อหุ้มการจัดสรรและการจัดสรรคืนหน่วยความจำฮีป
มันง่ายมากและไม่ติดมันและมอบสิ่งจำเป็นของ OO ให้ฉันโดยไม่บังคับให้ฉันจัดการกับสัตว์ประหลาดนั่นคือ C ++ และฉันยังคงความยืดหยุ่นในการพักอาศัยในที่ดิน C ซึ่งเหนือสิ่งอื่นใดทำให้การรวมห้องสมุดบุคคลที่สามเป็นเรื่องง่ายขึ้น
ฉันเสนอให้ใช้ Objective-C ซึ่งเป็น superset ของ C
ในขณะที่ Objective-C มีอายุ 30 ปีจะอนุญาตให้เขียนโค้ดที่หรูหรา
ใช่ แต่ฉันไม่เคยเห็นใครพยายามใช้ polymorphism ชนิดใดกับ C