หนึ่งจะเขียนรหัสเชิงวัตถุใน C อย่างไร [ปิด]


500

การเขียนโค้ดเชิงวัตถุใน C มีวิธีใดบ้าง โดยเฉพาะในเรื่องของ polymorphism


ดูเพิ่มเติมนี้กองมากเกินคำถามวัตถุปฐมนิเทศใน C


1
<a href=" ldeniau.web.cern.ch/ldeniau/html/oopc.html"> การเขียนโปรแกรมเชิงวัตถุใน C </a> โดย Laurent Deniau


25
@Camilo มาร์ติน: ผมจงใจถามสามารถไม่ควร ฉันไม่ได้สนใจที่จะใช้ OOP ใน C อย่างไรก็ตามโดยการเห็นวิธีแก้ปัญหา OO ใน C ฉัน / เรายืนเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับข้อ จำกัด และ / หรือความยืดหยุ่นของ C และวิธีการสร้างสรรค์เพื่อนำไปใช้และใช้ polymorphism
ไดน่า

5
OO เป็นเพียงรูปแบบ ตรวจสอบที่นี่มันสามารถทำได้ในไฟล์. bat: dirk.rave.org/chap9.txt (รูปแบบใด ๆ ที่สามารถนำไปใช้กับภาษาการเขียนโปรแกรมใด ๆ ถ้าคุณมีความสนใจพอฉันคิดว่า) นี่เป็นอาหารที่ดีสำหรับความคิด และอาจเป็นจำนวนมากที่สามารถเรียนรู้ได้จากการใช้รูปแบบดังกล่าวที่เราให้กับภาษาที่ไม่มี
Camilo Martin

6
GTK - 'scuse me, GObject - จริง ๆ แล้วเป็นตัวอย่างที่ดีของ OOP (sorta) ใน C. ดังนั้นเพื่อตอบ @Camilo สำหรับ C interpoliability
new123456

คำตอบ:


362

ใช่. ในความเป็นจริง Axel Schreiner นำเสนอหนังสือ "การเขียนโปรแกรมเชิงวัตถุใน ANSI-C" ของหนังสือของเขาฟรี


28
ในขณะที่แนวคิดในหนังสือเล่มนี้เป็นของแข็งคุณจะสูญเสียความปลอดภัยประเภท
diapir

22
ก่อนหน้าสิ่งที่เรารู้ว่าเป็นรูปแบบการออกแบบคือรูปแบบการออกแบบที่เรียกว่า เดียวกันกับการเก็บขยะและอื่น ๆ เช่น ตอนนี้พวกเขาฝังแน่นแล้วเรามักจะลืมเมื่อพวกเขาถูกคิดค้นครั้งแรกมันก็เหมือนกับวิธีที่เราคิดว่าเป็นรูปแบบการออกแบบในวันนี้
Dexygen

11
คุณสามารถขอรับได้โดยตรงจากเว็บไซต์ของผู้เขียน: cs.rit.edu/~ats/books/ooc.pdf เอกสารอื่น ๆ จากผู้เขียนคนเดียวกัน: cs.rit.edu/~ats/books/index.html
pakman

10
คอลเล็กชันที่เหมาะสม (ตัวอย่างหนังสือ + ซอร์สโค้ด) มีอยู่ในดัชนี rit.edu นี้การเขียนโปรแกรมเชิงวัตถุด้วย ANSI-C
David C. Rankin

3
หนังสือเล่มนี้ได้รับการตรวจสอบโดยเพื่อนหรือไม่ มีการพิมพ์ผิดในประโยคแรกของย่อหน้าแรกของหน้าแรก
Dagrooms

343

เนื่องจากคุณกำลังพูดถึงพหุสัณฐานแล้วใช่คุณสามารถเรากำลังทำสิ่งที่เรียงลำดับปีก่อนที่ 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

เพื่อให้คุณเห็นว่ามีการเรียกใช้ฟังก์ชันที่แตกต่างกันขึ้นอยู่กับคลาสย่อย


52
Encapsulation นั้นค่อนข้างง่าย polymorphism นั้นทำได้ - แต่การสืบทอดนั้นยาก
Martin Beckett

5
lwn.net เพิ่งเผยแพร่บทความชื่อการออกแบบเชิงวัตถุรูปแบบในเคอร์เนลในเรื่องของ stucts คล้ายกับคำตอบข้างต้น - นั่นคือ struct ที่มีตัวชี้ฟังก์ชั่นหรือตัวชี้ไปยัง struct ที่มีฟังก์ชั่นที่ใช้ตัวชี้ไปที่ struct ด้วยข้อมูลที่เรากำลังทำงานด้วยเป็นพารามิเตอร์
radicalmatt

11
+1 ตัวอย่างที่ดี! แม้ว่าหากใครต้องการลงไปตามถนนสายนี้จริง ๆ มันก็จะเหมาะสมกว่าสำหรับ "อินสแตนซ์" structs เพื่อให้มีฟิลด์เดียวชี้ไปที่อินสแตนซ์ "ตารางเสมือน" ของพวกเขาซึ่งมีฟังก์ชั่นเสมือนทั้งหมดสำหรับประเภทนั้นในที่เดียว นั่นคือการtCommClassเปลี่ยนชื่อของคุณtCommVTและโครงสร้างtCommClassจะมีเพียงเขตข้อมูลและเขตข้อมูลเดียวtCommVT vtชี้ไปที่ตารางเสมือน "หนึ่งเดียวเท่านั้น" การใช้พอยน์เตอร์ทุกตัวในแต่ละอินสแตนซ์จะเพิ่มค่าใช้จ่ายที่ไม่จำเป็นและคล้ายกับวิธีการทำสิ่งต่างๆใน JavaScript มากกว่า C ++, IMHO
Groo

1
ดังนั้นสิ่งนี้แสดงให้เห็นถึงการใช้งานของอินเทอร์เฟซเดียว แต่สิ่งที่เกี่ยวกับการใช้หลายอินเตอร์เฟส? หรือหลายมรดก
weberc2

Weber หากคุณต้องการฟังก์ชั่นการทำงานทั้งหมดของ C ++ คุณน่าจะใช้ C ++ คำถามที่ถามโดยเฉพาะเกี่ยวกับความหลากหลายรูปแบบความสามารถของวัตถุที่จะใช้ "รูปแบบ" ที่แตกต่างกัน แน่นอนคุณสามารถทำอินเทอร์เฟซและการสืบทอดหลายอย่างใน C แต่มันเป็นงานพิเศษที่ค่อนข้างยุติธรรมและคุณต้องจัดการกับ smarts ด้วยตัวคุณเองแทนที่จะใช้ C ++ ในตัว
paxdiablo

86

เนมสเปซมักจะทำโดยทำ:

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 เป็นเหมือนตัวแปรคลาสแบบสแตติก - แบ่งใช้ระหว่างอินสแตนซ์ทั้งหมดของชนิด วิธีการทั้งหมดเป็นแบบคงที่จริงๆยกเว้นบางวิธีใช้ *


1
@networkoose - st->my_type->push(st, thing2);แทนที่จะเป็นst->my_type.push(st, thing2);
Fabricio

@ หมวดหมู่: หรือ struct stack_type my_type;แทนstruct stack_type * my_type;
Fabricio

3
ฉันชอบแนวคิดของการมีโครงสร้างสำหรับชั้นเรียน แต่แล้วโครงสร้างทั่วไปClassล่ะ? นั่นจะทำให้ OO C มีไดนามิกมากกว่า C ++ แล้วมันล่ะ? อย่างไรก็ตาม +1
Linuxios

54

ฉันเชื่อว่านอกเหนือจากการมีประโยชน์ในสิทธิของตนเองการใช้ 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


4
URL ใหม่สำหรับคู่มือ C +: state-machine.com/doc/cplus_3.0_manual.pdf
เหลียง

32

ฉันเห็นมันเสร็จแล้ว ฉันจะไม่แนะนำที่นี่ C ++ เริ่มต้นด้วยวิธีนี้ในฐานะ preprocessor ที่สร้างรหัส C เป็นขั้นตอนกลาง

สิ่งสำคัญที่คุณต้องทำคือสร้างตารางส่งข้อมูลสำหรับวิธีการทั้งหมดที่คุณเก็บการอ้างอิงฟังก์ชันของคุณ การรับคลาสจะทำให้เกิดการคัดลอกตารางการแจกจ่ายนี้และแทนที่รายการที่คุณต้องการแทนที่ด้วย "เมธอด" ใหม่ของคุณที่มีการเรียกเมธอดดั้งเดิมหากต้องการเรียกใช้เมธอดพื้นฐาน ในที่สุดคุณก็สิ้นสุดการเขียน C ++ ใหม่


5
"ในที่สุดคุณก็จบเขียนใหม่ C ++" ฉันสงสัยว่า / กลัวว่าจะเป็นเช่นนั้น
Dinah

39
หรือคุณอาจสิ้นสุดการเขียน Objective C ใหม่ซึ่งจะเป็นผลลัพธ์ที่น่าสนใจยิ่งขึ้น
ศ. Falken ผิดสัญญา

3
มีรสชาติแบบ OOP น้อยกว่าระดับเช่นใน Javascriptที่กูรูกล่าวว่า: "เราไม่ต้องการคลาสเพื่อสร้างวัตถุที่คล้ายกันมากมาย" แต่ฉันเกรงว่านี่ไม่ใช่เรื่องง่ายที่จะประสบความสำเร็จใน C. ไม่ (ยัง) ในฐานะที่จะบอกได้ (มีโคลน () ประจำโคลน struct หรือไม่?)
Lumi

1
สมาร์ทอีกคนที่ต้องนำไปใช้จริงและทำให้การติดตั้งนั้นรวดเร็ว (Google, เครื่องมือ V8) ได้ทำทุกอย่างเพื่อเพิ่ม (ซ่อน) คลาสใน JavaScript กลับ
cubuspl42

ไม่glibเขียนใน C ในทางที่เป็นวัตถุประสงค์?
kravemir

26

แน่นอนว่าเป็นไปได้ นี่คือสิ่งที่GObjectกรอบงานของGTK +และGNOMEทั้งหมดตั้งอยู่


อะไรคือข้อดี / ข้อเสียของวิธีการดังกล่าว? กล่าวคือ มันง่ายกว่าที่จะเขียนโดยใช้ C ++
kravemir

@ Kravemir อืม C ++ นั้นไม่สามารถพกพาได้ง่ายเหมือนกับ C และมันค่อนข้างยากที่จะเชื่อมโยง C ++ กับโค้ดที่อาจถูกคอมไพล์โดยคอมไพเลอร์ C ++ ที่แตกต่างกัน แต่ใช่มันง่ายกว่าในการเขียนคลาสใน C ++ แม้ว่า GObject จะไม่ยากอย่างนั้น (สมมติว่าคุณไม่ได้สนใจจานหม้อไอน้ำเล็ก ๆ )
Edwin Buck

17

ไลบรารีย่อย C stdio FILE เป็นตัวอย่างที่ยอดเยี่ยมของวิธีการสร้างสิ่งที่เป็นนามธรรม, การห่อหุ้มและการแยกส่วนในซีที่ไม่บริสุทธิ์

มรดกและ polymorphism - ด้านอื่น ๆ มักจะคิดว่าสิ่งสำคัญที่จะ OOP - ไม่จำเป็นต้องให้กำไรจากการผลิตที่พวกเขาสัญญาและเหมาะสม ข้อโต้แย้งได้รับการทำที่พวกเขาสามารถพัฒนาจริงขัดขวางและความคิดเกี่ยวกับปัญหาโดเมน


stdio ไม่ได้แยกออกจากชั้นเคอร์เนลใช่หรือไม่ ถ้าฉันไม่เข้าใจผิด C-library ถือว่าพวกเขาเป็นไฟล์ตัวอักษร / อุปกรณ์และไดรเวอร์เคอร์เนลทำงาน ...
kravemir

15

ตัวอย่างเล็กน้อยกับสัตว์และสุนัข: คุณสะท้อนกลไก 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.
masoud

13

ตรวจสอบGObject มันหมายถึงการเป็น OO ใน C และการนำสิ่งที่คุณต้องการไปใช้ ถ้าคุณต้องการ OO จริงๆไปด้วย C ++ หรือภาษา OOP อื่น ๆ GObject อาจใช้งานได้ยากในบางครั้งถ้าคุณคุ้นเคยกับการใช้ภาษา OO แต่คุณไม่คุ้นเคยกับการประชุมและการไหล


12

สิ่งนี้น่าสนใจที่จะอ่าน ฉันได้ไตร่ตรองคำถามเดียวกันด้วยตัวเองและประโยชน์ของการคิดเกี่ยวกับมันคือ:

  • การพยายามจินตนาการถึงวิธีการใช้แนวคิด OOP ในภาษาที่ไม่ใช่ OOP ช่วยให้ฉันเข้าใจจุดแข็งของภาษา OOp (ในกรณีของฉัน C ++) สิ่งนี้ช่วยให้ฉันตัดสินใจได้ดียิ่งขึ้นว่าจะใช้ C หรือ C ++ สำหรับแอปพลิเคชันประเภทที่กำหนดหรือไม่

  • ในการเรียกดูเว็บสำหรับข้อมูลและความคิดเห็นเกี่ยวกับเรื่องนี้ฉันพบผู้เขียนที่เขียนโค้ดสำหรับตัวประมวลผลแบบฝังตัวและมีคอมไพเลอร์ C เท่านั้น: http://www.eetimes.com/discussion/other/4024626/Object-Oriented -C-สร้าง-มูลนิธิเรียน-Part-1

ในกรณีของเขาการวิเคราะห์และการปรับแนวคิด OOP ใน C ธรรมดาเป็นการแสวงหาที่ถูกต้อง ดูเหมือนว่าเขาจะเปิดให้เสียสละแนวคิด OOP เนื่องจากผลการดำเนินงานค่าใช้จ่ายที่เกิดจากความพยายามที่จะใช้พวกเขาใน

บทเรียนที่ฉันทำคือใช่สามารถทำได้ในระดับหนึ่งและใช่มีเหตุผลดีๆที่ควรลอง

ในที่สุดเครื่องจะทำงานสองบิตตัวชี้สแต็กทำให้โปรแกรมเคาน์เตอร์กระโดดไปรอบ ๆ และการคำนวณการเข้าถึงหน่วยความจำ จากมุมมองด้านประสิทธิภาพการคำนวณที่น้อยลงของโปรแกรมของคุณดีกว่า ... แต่บางครั้งเราต้องจ่ายภาษีนี้เพียงเพื่อให้เราสามารถจัดระเบียบโปรแกรมของเราในแบบที่ทำให้เกิดความผิดพลาดน้อยที่สุดต่อมนุษย์ คอมไพเลอร์ภาษา OOP พยายามเพิ่มประสิทธิภาพทั้งสองด้าน โปรแกรมเมอร์จะต้องมีความระมัดระวังมากขึ้นในการใช้แนวคิดเหล่านี้ในภาษาเช่น C


10

คุณอาจพบว่ามีประโยชน์ในการดูเอกสารประกอบของ Apple สำหรับชุด Core Foundation ของ API มันเป็น C API บริสุทธิ์ แต่มีหลายชนิดที่เชื่อมต่อกับ Objective-C วัตถุเทียบเท่า

คุณอาจพบว่ามีประโยชน์ในการดูการออกแบบของ Objective-C มันแตกต่างจาก C ++ เล็กน้อยซึ่งระบบวัตถุนั้นถูกกำหนดในแง่ของฟังก์ชั่น C เช่นobjc_msg_sendการเรียกใช้เมธอดบนวัตถุ คอมไพเลอร์แปลไวยากรณ์ของวงเล็บเหลี่ยมเป็นการเรียกใช้ฟังก์ชันเหล่านั้นดังนั้นคุณไม่จำเป็นต้องรู้ แต่เมื่อพิจารณาคำถามของคุณคุณอาจพบว่ามีประโยชน์ในการเรียนรู้วิธีการทำงานภายใต้ประทุน


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 (การห่อหุ้มข้อมูล) ด้วยการใช้ตัวชี้ฟังก์ชันทำให้ง่ายต่อการใช้งานบางอย่างเช่นการสืบทอด แต่จริงๆแล้วมันมีประโยชน์เพียงเล็กน้อยเท่านั้น


6
หากคุณทำtypedef struct FOO_type FOO_typeแทน typedef เป็นโมฆะในส่วนหัวคุณจะได้รับประโยชน์เพิ่มเติมจากการตรวจสอบชนิดในขณะที่ยังไม่เปิดเผยโครงสร้างของคุณ
Scott Wales

8

คุณสามารถปลอมมันโดยใช้ตัวชี้ฟังก์ชั่นและในความเป็นจริงฉันคิดว่ามันเป็นไปได้ในทางทฤษฎีในการรวบรวมโปรแกรม C ++ ลงใน C

อย่างไรก็ตามมันไม่ค่อยสมเหตุสมผลที่จะบังคับกระบวนทัศน์เกี่ยวกับภาษาแทนที่จะเลือกภาษาที่ใช้กระบวนทัศน์


5
คอมไพเลอร์ C ++ แรกที่ทำอย่างนั้น - มันแปลงรหัส C ++ เป็นรหัส C เทียบเท่า (แต่น่าเกลียดและไม่สามารถอ่านได้) รหัส C ซึ่งถูกรวบรวมโดยคอมไพเลอร์ C
Adam Rosenfield

2
EDG, Cfront และอื่น ๆ ยังสามารถทำสิ่งนี้ได้ ด้วยเหตุผลที่ดีมาก: ไม่ใช่ทุกแพลตฟอร์มที่มีคอมไพเลอร์ C ++
Jasper Bekkers

ด้วยเหตุผลบางอย่างฉันคิดว่า C-front รองรับเฉพาะส่วนขยาย C ++ บางอย่าง (เช่นการอ้างอิง) แต่ไม่ได้จำลองการส่งข้อมูล OOP / ไดนามิกเต็มรูปแบบ
Uri

2
คุณสามารถทำสิ่งเดียวกันกับ LLVM และแบ็กเอนด์ C
Zifre

7

Object oriented C สามารถทำได้ฉันได้เห็นโค้ดประเภทนี้ในการผลิตในเกาหลีและมันเป็นสัตว์ประหลาดที่น่ากลัวที่สุดที่ฉันเคยเห็นในปีที่ผ่านมา ใช่แล้วสามารถทำได้และมีผู้คนเคยทำมาแล้วและยังคงทำได้แม้ในยุคนี้ แต่ฉันขอแนะนำ C ++ หรือ Objective-C ทั้งสองเป็นภาษาที่เกิดจาก C โดยมีวัตถุประสงค์เพื่อให้การวางแนววัตถุที่มีกระบวนทัศน์ที่แตกต่างกัน


3
ถ้าไลนัสเห็นความคิดเห็นของคุณ เขาจะหัวเราะหรือสาปแช่งคุณอย่างแน่นอน
Anders Lind

7

หากคุณมั่นใจว่าวิธีการของ OOP นั้นเหนือกว่าปัญหาที่คุณพยายามแก้ไขทำไมคุณถึงพยายามที่จะแก้ไขด้วยภาษาที่ไม่ใช่แบบ OOP ดูเหมือนว่าคุณกำลังใช้เครื่องมือที่ผิดสำหรับงาน ใช้ C ++ หรือภาษาอื่น ๆ ในภาษา C-object

หากคุณกำลังถามเพราะคุณกำลังเริ่มเขียนโค้ดสำหรับโครงการขนาดใหญ่ที่มีอยู่แล้วที่เขียนไว้ใน C คุณไม่ควรพยายามบังคับกระบวนทัศน์ OOP ของคุณเอง (หรือบุคคลอื่น) เข้าสู่โครงสร้างพื้นฐานของโครงการ ทำตามแนวทางที่มีอยู่แล้วในโครงการ โดยทั่วไป APIs ที่สะอาดและห้องสมุดแยกและโมดูลจะไปทางยาวต่อการมีการทำความสะอาด OOP- ishการออกแบบ

หากหลังจากทั้งหมดนี้คุณพร้อมที่จะทำ OOP C แล้วให้อ่านสิ่งนี้ (PDF)


36
ไม่ตอบคำถามจริงๆ ...
Brian Postow

2
@Brian ลิงก์ไปยัง PDF จะปรากฏขึ้นเพื่อตอบคำถามโดยตรงแม้ว่าฉันจะไม่มีเวลาตรวจสอบตัวเอง
Mark Ransom

5
ลิงก์ไปยัง PDF ดูเหมือนจะเป็นตำราเรียนทั้งหมดในเรื่อง ... หลักฐานที่สวยงาม แต่มันไม่พอดีกับขอบ ...
Brian Postow

5
ใช่ตอบคำถาม มันถูกต้องสมบูรณ์แบบที่จะถามวิธีการใช้ภาษาในลักษณะเฉพาะ ไม่มีการร้องขอความคิดเห็นเกี่ยวกับภาษาอื่น ๆ ....
Tim Ring

9
@Brian & Tim Ring: คำถามที่ถามถึงคำแนะนำหนังสือในหัวข้อ; ฉันให้ลิงก์แก่หนังสือที่เน้นหัวข้อนี้โดยเฉพาะ ฉันยังให้ความเห็นเกี่ยวกับสาเหตุที่วิธีการแก้ปัญหาอาจไม่เหมาะสม (ซึ่งฉันคิดว่าหลายคนที่นี่ดูเหมือนจะเห็นด้วยตามคะแนนและความเห็น / คำตอบอื่น ๆ ) คุณมีข้อเสนอแนะสำหรับการปรับปรุงคำตอบของฉันหรือไม่?
RarrRarrRarr

6

ใช่คุณสามารถ. ผู้คนกำลังเขียน 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;
}

6

แน่นอนว่ามันจะไม่สวยเท่าการใช้ภาษาที่มีการสนับสนุนในตัว ฉันยังเขียน "แอสเซมเบลอร์เชิงวัตถุ"


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;
}

5

ฉันขุดมาเป็นเวลาหนึ่งปี:

เนื่องจากระบบ 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 แต่เอกสารภาษาจีนดีกว่ามาก)

OOC-GCC


ระดับคงที่ ASM NEW ลบ ST ...มีแมโครใน OOC-GCC
DaMeng


4

บทความหรือหนังสือใดดีที่ใช้แนวคิด OOP ใน C

การเชื่อมต่อและการนำ C มาใช้ของ Dave Hanson นั้นยอดเยี่ยมในการห่อหุ้มและตั้งชื่อและใช้งานตัวชี้ฟังก์ชั่นได้ดีมาก เดฟไม่พยายามจำลองมรดก


4

OOP เป็นเพียงกระบวนทัศน์ที่ทำให้ข้อมูลมีความสำคัญมากกว่ารหัสในโปรแกรม OOP ไม่ใช่ภาษา ดังนั้นเช่นธรรมดา C เป็นภาษาง่าย OOP ในธรรมดา C ก็ง่ายเกินไป


3
พูดได้ดี แต่นี่ควรเป็นความคิดเห็น
pqsk

4

สิ่งหนึ่งที่คุณอาจต้องการที่จะทำคือการมองเข้าไปในการดำเนินงานของXtเครื่องมือสำหรับวินโดว์ แน่ใจว่ามันใช้เวลานานในการฟัน แต่โครงสร้างที่ใช้จำนวนมากถูกออกแบบมาเพื่อทำงานในแบบ OO ในแบบดั้งเดิม C. โดยทั่วไปหมายถึงการเพิ่มชั้นพิเศษของทางอ้อมที่นี่และที่นั่นและการออกแบบโครงสร้างเพื่อวางซ้อนกัน

จริงๆคุณสามารถทำจำนวนมากในทางของ OO อยู่ใน C วิธีนี้แม้ว่ามันจะรู้สึกเหมือนมันบางครั้งแนวคิด OO #include<favorite_OO_Guru.h>ไม่ได้ฤดูใบไม้ผลิที่เกิดขึ้นอย่างเต็มที่จากความคิดของ พวกเขาประกอบด้วยการฝึกฝนที่ดีที่สุดหลายครั้ง ภาษาและระบบ OO จะกลั่นและขยายส่วนของโปรแกรม zeitgeist ประจำวันเท่านั้น


4

คำตอบของคำถามคือ 'ใช่คุณทำได้'

ชุด Object-oriented C (OOC) สำหรับผู้ที่ต้องการเขียนโปรแกรมในลักษณะเชิงวัตถุ แต่ยึดติดกับ C เก่าที่ดีเช่นกัน OOC ใช้คลาสการสืบทอดเดี่ยวและหลายการจัดการข้อยกเว้น

คุณสมบัติ

•ใช้มาโคร C เท่านั้นและฟังก์ชั่นไม่จำเป็นต้องใช้นามสกุลภาษา! (ANSI-C)

•ซอร์สโค้ดที่อ่านง่ายสำหรับแอปพลิเคชันของคุณ การดูแลก็ทำสิ่งที่ง่ายที่สุดเท่าที่จะทำได้

•การสืบทอดคลาสเดียว

•การสืบทอดหลายรายการโดยอินเทอร์เฟซและมิกซ์อิน (ตั้งแต่เวอร์ชั่น 1.3)

•ใช้ข้อยกเว้น (ใน pure C!)

•ฟังก์ชั่นเสมือนจริงสำหรับชั้นเรียน

•เครื่องมือภายนอกสำหรับการใช้งานในชั้นเรียนได้ง่าย

สำหรับรายละเอียดเพิ่มเติมเยี่ยมชมhttp://ooc-coding.sourceforge.net/


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


สำหรับการสืบทอดโดยทั่วไปคลาสฐานหรือโครงสร้างอินสแตนซ์จะถูกฝังในสิ่งที่ได้รับไม่ใช่การจัดสรรแยกต่างหากและอ้างอิงโดยใช้พอยน์เตอร์ ด้วยวิธีนี้ฐานที่สูงที่สุดจะอยู่ที่จุดเริ่มต้นของโครงสร้างประเภทที่ได้รับมาเสมอดังนั้นพวกมันจึงสามารถทำการร่ายซึ่งกันและกันได้อย่างง่ายดายซึ่งคุณไม่สามารถทำได้ด้วยพอยน์เตอร์ที่อาจชดเชยได้
underscore_d

2

ดูที่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;
}

2

ฉันมาช้าไปงานปาร์ตี้ แต่ฉันต้องการแบ่งปันประสบการณ์ของฉันในหัวข้อ: ฉันทำงานกับสิ่งที่ฝังตัวในวันนี้และคอมไพเลอร์เท่านั้น (ที่เชื่อถือได้) ที่ฉันมีคือ C ดังนั้นฉันจึงต้องการใช้งานเชิงวัตถุ วิธีการในโครงการฝังตัวของฉันเขียนใน C.

วิธีแก้ปัญหาส่วนใหญ่ที่ฉันเคยเห็นใช้ typecasts อย่างหนักดังนั้นเราจึงขาดความปลอดภัยประเภท: คอมไพเลอร์จะไม่ช่วยคุณถ้าคุณทำผิดพลาด สิ่งนี้ไม่สามารถยอมรับได้อย่างสมบูรณ์

ข้อกำหนดที่ฉันมี:

  • หลีกเลี่ยง typecasts มากที่สุดดังนั้นเราจะไม่สูญเสียความปลอดภัยของประเภท;
  • ความแตกต่าง: เราควรจะสามารถใช้วิธีการเสมือนจริงและผู้ใช้ชั้นไม่ควรทราบว่าวิธีการบางอย่างเป็นเสมือนจริงหรือไม่
  • การสืบทอดหลายรายการ: ฉันไม่ได้ใช้บ่อย แต่บางครั้งฉันต้องการให้คลาสบางส่วนใช้อินเทอร์เฟซหลายตัว (หรือขยายหลายคลาสซูเปอร์คลาส)

ฉันได้อธิบายวิธีการอย่างละเอียดในบทความนี้แล้ว: การเขียนโปรแกรมเชิงวัตถุใน C ; นอกจากนี้ยังมีโปรแกรมอรรถประโยชน์สำหรับการสร้างรหัสสำเร็จรูปสำหรับฐานและคลาสที่ได้รับ


2

ฉันสร้างห้องสมุดเล็ก ๆ ที่ฉันลองและมันก็ใช้งานได้จริงสำหรับฉัน ดังนั้นฉันคิดว่าฉันแบ่งปันประสบการณ์

https://github.com/thomasfuhringer/oxygen

การสืบทอดเดี่ยวสามารถดำเนินการได้ค่อนข้างง่ายโดยใช้โครงสร้างและขยายสำหรับคลาสย่อยอื่น ๆ การส่งแบบง่ายไปยังโครงสร้างพาเรนต์ทำให้สามารถใช้เมธอดพาเรนต์ในการสืบทอดทั้งหมด ตราบใดที่คุณรู้ว่าตัวแปรชี้ไปที่โครงสร้างที่เก็บวัตถุชนิดนี้คุณสามารถส่งไปยังคลาสรูทและทำการวิปัสสนา

ดังที่ได้กล่าวมาแล้ววิธีการเสมือนนั้นค่อนข้างซับซ้อนกว่า แต่พวกเขาทำได้ เพื่อให้ง่ายขึ้นฉันแค่ใช้ฟังก์ชั่นมากมายในโครงสร้างคำอธิบายคลาสที่ทุกคลาสเด็กคัดลอกและ repopulate แต่ละสล็อตเมื่อจำเป็น

การรับมรดกหลายรายการค่อนข้างซับซ้อนในการดำเนินการและมาพร้อมกับผลกระทบด้านประสิทธิภาพที่สำคัญ ดังนั้นฉันจะทิ้งมันไว้ ฉันคิดว่ามันเป็นที่น่าพอใจและมีประโยชน์ในหลาย ๆ กรณีเพื่อทำแบบจำลองสถานการณ์ในชีวิตจริงให้เรียบร้อย แต่ใน 90% ของกรณีการสืบทอดเดี่ยวอาจครอบคลุมความต้องการ และการสืบทอดเดี่ยวนั้นง่ายและไม่เสียค่าใช้จ่ายใด ๆ

นอกจากนี้ฉันไม่สนใจเกี่ยวกับความปลอดภัยของประเภท ฉันคิดว่าคุณไม่ควรพึ่งพาคอมไพเลอร์เพื่อป้องกันคุณจากการเขียนโปรแกรมผิดพลาด และจะป้องกันคุณจากข้อผิดพลาดเพียงเล็กน้อยเท่านั้น

โดยทั่วไปในสภาพแวดล้อมเชิงวัตถุคุณต้องการใช้การนับการอ้างอิงเพื่อทำให้การจัดการหน่วยความจำโดยอัตโนมัติในขอบเขตที่เป็นไปได้ ดังนั้นฉันจึงใส่การอ้างอิงลงในคลาสรูท“ Object” และฟังก์ชันบางอย่างเพื่อห่อหุ้มการจัดสรรและการจัดสรรคืนหน่วยความจำฮีป

มันง่ายมากและไม่ติดมันและมอบสิ่งจำเป็นของ OO ให้ฉันโดยไม่บังคับให้ฉันจัดการกับสัตว์ประหลาดนั่นคือ C ++ และฉันยังคงความยืดหยุ่นในการพักอาศัยในที่ดิน C ซึ่งเหนือสิ่งอื่นใดทำให้การรวมห้องสมุดบุคคลที่สามเป็นเรื่องง่ายขึ้น


1

ฉันเสนอให้ใช้ Objective-C ซึ่งเป็น superset ของ C

ในขณะที่ Objective-C มีอายุ 30 ปีจะอนุญาตให้เขียนโค้ดที่หรูหรา

http://en.wikipedia.org/wiki/Objective-C


ในกรณีที่ว่าฉันจะแนะนำ C ++ ตั้งแต่แทนของจริงเชิงวัตถุ ...
yyny

นี่ไม่ใช่คำตอบ แต่อย่างไรก็ตาม @YoYoYonnY: ฉันไม่ได้ใช้ Objective-C และใช้ C ++ แต่ความคิดเห็นแบบนี้ไม่มีประโยชน์โดยไม่มีพื้นฐานและคุณไม่ได้ให้อะไรเลย ทำไมคุณถึงอ้างว่า Objective-C ขาด "ความเป็นจริงเชิงวัตถุ ... "? และเหตุใด C ++ จึงประสบความสำเร็จในกรณีที่ Objective-C ล้มเหลว สิ่งที่ตลกก็คือ Objective-C, ดี, แท้จริงมีคำว่าObjectในชื่อในขณะที่ C + + ตลาดตัวเองเป็นภาษากระบวนทัศน์หลายไม่ใช่ OOP หนึ่ง เลย) ... คุณแน่ใจเหรอว่าคุณไม่ได้ใช้ชื่อที่ผิด ๆ ?
underscore_d

0

ใช่ แต่ฉันไม่เคยเห็นใครพยายามใช้ polymorphism ชนิดใดกับ C


6
คุณต้องมองไปรอบ ๆ :) ตัวอย่างเช่น Direct X ของ Microsoft มีส่วนต่อประสาน polymorphic C
AShelly

8
ดูที่การใช้เคอร์เนล linux เป็นตัวอย่าง มันเป็นเรื่องธรรมดาและใช้กันอย่างแพร่หลายใน C.
Ilya

3
ยัง glib เป็น polymorphic หรือสามารถใช้ในวิธีที่ช่วยให้ polymorphism (มันเหมือน C ++ คุณต้องพูดอย่างชัดเจนว่าสายใดเป็นเสมือน)
Spudd86

1
ความแตกต่างไม่ได้เป็นสิ่งที่หายากใน C ในขณะที่หลายมรดกเป็น
Johan Bjäreholt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.