Apa sajakah cara untuk menulis kode berorientasi objek dalam C? Terutama berkenaan dengan polimorfisme.
Lihat juga ini Stack Overflow pertanyaan Obyek-orientasi dalam C .
Apa sajakah cara untuk menulis kode berorientasi objek dalam C? Terutama berkenaan dengan polimorfisme.
Lihat juga ini Stack Overflow pertanyaan Obyek-orientasi dalam C .
Jawaban:
Iya. Bahkan Axel Schreiner menyediakan bukunya "Pemrograman Berorientasi Objek dalam ANSI-C" secara gratis yang mencakup subjek cukup menyeluruh.
Karena Anda berbicara tentang polimorfisme maka ya, Anda bisa, kami melakukan hal-hal semacam itu bertahun-tahun sebelum C ++ muncul.
Pada dasarnya Anda menggunakan a struct
untuk menyimpan data dan daftar pointer fungsi untuk menunjuk ke fungsi yang relevan untuk data tersebut.
Jadi, di kelas komunikasi, Anda akan memiliki panggilan terbuka, baca, tulis, dan tutup yang akan dipertahankan sebagai empat fungsi pointer dalam struktur, di samping data untuk objek, seperti:
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;
Tentu saja, segmen kode tersebut di atas sebenarnya akan berada dalam "konstruktor" seperti rs232Init()
.
Ketika Anda 'mewarisi' dari kelas itu, Anda hanya mengubah pointer untuk menunjuk ke fungsi Anda sendiri. Setiap orang yang memanggil fungsi-fungsi itu akan melakukannya melalui pointer fungsi, memberi Anda polimorfisme Anda:
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
Semacam seperti vtable manual.
Anda bahkan bisa memiliki kelas virtual dengan mengatur pointer ke NULL -perilaku akan sedikit berbeda dengan C ++ (core dump pada saat run-time daripada kesalahan pada waktu kompilasi).
Berikut sepotong kode contoh yang menunjukkannya. Pertama struktur kelas tingkat atas:
#include <stdio.h>
// The top-level class.
typedef struct sCommClass {
int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;
Kemudian kita memiliki fungsi untuk 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;
}
Dan HTTP juga:
// 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;
}
Dan akhirnya program uji untuk menunjukkannya dalam aksi:
// 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;
}
Ini menghasilkan output:
Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com
sehingga Anda dapat melihat bahwa fungsi yang berbeda sedang dipanggil, tergantung pada sub-kelas.
tCommClass
akan diubah namanya menjadi tCommVT
, dan tCommClass
struct hanya akan memiliki bidang data dan bidang tunggal yang tCommVT vt
menunjuk ke "satu-satunya" tabel virtual. Membawa semua petunjuk dengan setiap contoh menambahkan overhead yang tidak perlu dan lebih menyerupai bagaimana Anda akan melakukan hal-hal dalam JavaScript daripada C ++, IMHO.
Ruang nama sering dilakukan dengan melakukan:
stack_push(thing *)
dari pada
stack::push(thing *)
Untuk membuat C struct menjadi sesuatu seperti kelas C ++ Anda dapat mengubah:
class stack {
public:
stack();
void push(thing *);
thing * pop();
static int this_is_here_as_an_example_only;
private:
...
};
Ke
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
Dan lakukan:
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
}
Saya tidak melakukan destruktor atau menghapus, tetapi mengikuti pola yang sama.
this_is_here_as_an_example_only seperti variabel kelas statis - dibagikan di antara semua contoh jenis. Semua metode benar-benar statis, kecuali ada yang mengambil ini *
st->my_type->push(st, thing2);
bukanst->my_type.push(st, thing2);
struct stack_type my_type;
bukannyastruct stack_type * my_type;
Class
struct generik ? Itu akan membuat OO C lebih dinamis daripada C ++. Bagaimana tentang itu? Omong-omong, +1.
Saya percaya bahwa selain berguna dalam haknya sendiri, menerapkan OOP dalam C adalah cara terbaik untuk mempelajari OOP dan memahami cara kerja bagian dalamnya. Pengalaman banyak programmer telah menunjukkan bahwa untuk menggunakan teknik secara efisien dan percaya diri, seorang programmer harus memahami bagaimana konsep-konsep yang mendasarinya akhirnya diimplementasikan. Meniru kelas, warisan, dan polimorfisme dalam C mengajarkan hal ini.
Untuk menjawab pertanyaan awal, berikut adalah beberapa sumber yang mengajarkan bagaimana melakukan OOP di C:
Posting blog EmbeddedGurus.com "Pemrograman berbasis objek dalam C" menunjukkan bagaimana mengimplementasikan kelas dan pewarisan tunggal dalam C portabel: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c /
Catatan Aplikasi "" C + "- Pemrograman Berorientasi Objek dalam C" menunjukkan bagaimana mengimplementasikan kelas, pewarisan tunggal, dan pengikatan akhir (polimorfisme) dalam C menggunakan macro preprocessor: http://www.state-machine.com/resources/cplus_3. 0_manual.pdf , kode contoh tersedia dari http://www.state-machine.com/resources/cplus_3.0.zip
Saya sudah melihatnya selesai. Saya tidak akan merekomendasikannya. C ++ awalnya dimulai dengan cara ini sebagai preprosesor yang menghasilkan kode C sebagai langkah perantara.
Pada dasarnya apa yang Anda akhirnya lakukan adalah membuat tabel pengiriman untuk semua metode Anda di mana Anda menyimpan referensi fungsi Anda. Turunnya kelas akan memerlukan menyalin tabel pengiriman ini dan mengganti entri yang ingin Anda timpa, dengan "metode" baru Anda harus memanggil metode asli jika ingin memanggil metode dasar. Akhirnya, Anda akhirnya menulis ulang C ++.
glib
ditulis dalam C dengan cara yang objektif?
Yakin itu mungkin. Inilah yang dilakukan GObject , kerangka kerja yang mendasari semua GTK + dan GNOME .
Sub-pustaka C stdio FILE adalah contoh yang sangat baik tentang cara membuat abstraksi, enkapsulasi, dan modularitas dalam C. yang tidak disadap.
Warisan dan polimorfisme - aspek-aspek lain yang sering dianggap penting untuk OOP - tidak selalu memberikan keuntungan produktivitas yang mereka janjikan dan argumen yang masuk akal telah dibuat bahwa mereka benar-benar dapat menghambat perkembangan dan berpikir tentang domain masalah.
Contoh sepele dengan Hewan dan Anjing: Anda mencerminkan mekanisme vtable C ++ (sebagian besar toh). Anda juga memisahkan alokasi dan instantiasi (Animal_Alloc, Animal_New) sehingga kami tidak memanggil malloc () beberapa kali. Kita juga harus secara eksplisit melewatkan this
pointer.
Jika Anda melakukan fungsi non-virtual, itu trival. Anda hanya tidak menambahkannya ke fungsi vtable dan statis tidak memerlukan this
pointer. Multiple inheritance umumnya membutuhkan banyak vtables untuk menyelesaikan ambiguitas.
Selain itu, Anda harus dapat menggunakan setjmp / longjmp untuk melakukan penanganan pengecualian.
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. Ini diuji pada kompiler C ++, tetapi harus mudah membuatnya bekerja pada kompiler C.
typedef
di dalam struct
tidak dimungkinkan dalam C.
Lihat GObject . Ini dimaksudkan sebagai OO dalam C dan satu implementasi dari apa yang Anda cari. Jika Anda benar-benar menginginkan OO, gunakan C ++ atau bahasa OOP lainnya. GObject terkadang bisa sangat sulit untuk digunakan jika Anda terbiasa berurusan dengan bahasa OO, tetapi seperti apa pun, Anda akan terbiasa dengan konvensi dan aliran.
Ini menarik untuk dibaca. Saya sendiri telah memikirkan pertanyaan yang sama, dan manfaat dari memikirkannya adalah ini:
Mencoba membayangkan bagaimana menerapkan konsep OOP dalam bahasa non-OOP membantu saya memahami kekuatan bahasa OOp (dalam kasus saya, C ++). Ini membantu memberi saya penilaian yang lebih baik tentang apakah akan menggunakan C atau C ++ untuk jenis aplikasi yang diberikan - di mana manfaat dari satu lebih besar dari yang lain.
Dalam menjelajah web saya untuk informasi dan pendapat tentang ini, saya menemukan seorang penulis yang sedang menulis kode untuk prosesor tertanam dan hanya memiliki kompiler C yang tersedia: http://www.eetimes.com/discussion/other/4024626/Object-Oriented -C-Menciptakan-Yayasan-Kelas-Bagian-1
Dalam kasusnya, menganalisis dan mengadaptasi konsep OOP di dataran C adalah pengejaran yang valid. Tampaknya ia terbuka untuk mengorbankan beberapa konsep OOP karena hit overhead kinerja yang dihasilkan dari upaya untuk mengimplementasikannya dalam C.
Pelajaran yang saya ambil adalah, ya itu bisa dilakukan sampai tingkat tertentu, dan ya, ada beberapa alasan bagus untuk mencobanya.
Pada akhirnya, mesin memutar-mutar bit pointer stack, membuat penghitung program melompat-lompat dan menghitung operasi akses memori. Dari sudut pandang efisiensi, semakin sedikit perhitungan yang dilakukan oleh program Anda, semakin baik ... tetapi terkadang kami harus membayar pajak ini hanya agar kami dapat mengatur program kami dengan cara yang membuatnya paling tidak rentan terhadap kesalahan manusia. Kompilator bahasa OOP berusaha untuk mengoptimalkan kedua aspek. Programmer harus lebih berhati-hati mengimplementasikan konsep-konsep ini dalam bahasa seperti C.
Anda mungkin terbantu untuk melihat dokumentasi Apple untuk kumpulan Core Foundation-nya. Ini adalah API C murni, tetapi banyak tipe yang dijembatani dengan objek Objective-C yang setara.
Anda juga mungkin merasa terbantu untuk melihat desain Objective-C itu sendiri. Ini sedikit berbeda dari C ++ karena sistem objek didefinisikan dalam hal fungsi C, misalnya objc_msg_send
untuk memanggil metode pada objek. Kompiler menerjemahkan sintaks braket kuadrat ke dalam panggilan fungsi tersebut, jadi Anda tidak perlu mengetahuinya, tetapi mempertimbangkan pertanyaan Anda, Anda mungkin menemukan manfaatnya untuk mempelajari cara kerjanya di bawah tenda.
Ada beberapa teknik yang bisa digunakan. Yang paling penting adalah bagaimana membagi proyek. Kami menggunakan antarmuka dalam proyek kami yang dideklarasikan dalam file .h dan implementasi objek dalam file .c. Bagian yang penting adalah bahwa semua modul yang menyertakan file .h hanya melihat objek sebagai void *
, dan file .c adalah satu-satunya modul yang mengetahui internal struktur.
Sesuatu seperti ini untuk kelas yang kita beri nama FOO sebagai contoh:
Dalam file .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
File implementasi C akan seperti itu.
#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;
}
Jadi saya memberikan pointer secara eksplisit ke objek ke setiap fungsi modul itu. Kompiler C ++ melakukannya secara implisit, dan dalam C kita menuliskannya secara eksplisit.
Saya sangat menggunakan this
dalam program saya, untuk memastikan bahwa program saya tidak dikompilasi dalam C ++, dan itu memiliki sifat yang baik berada dalam warna lain di editor menyoroti sintaks saya.
Bidang-bidang FOO_struct dapat dimodifikasi dalam satu modul dan modul lainnya bahkan tidak perlu dikompilasi ulang agar tetap dapat digunakan.
Dengan gaya itu saya sudah menangani sebagian besar keunggulan OOP (enkapsulasi data). Dengan menggunakan pointer fungsi, itu bahkan mudah untuk mengimplementasikan sesuatu seperti warisan, tetapi jujur, itu benar-benar jarang berguna.
typedef struct FOO_type FOO_type
alih - alih mengetikkan membatalkan di header Anda mendapatkan manfaat tambahan dari pemeriksaan jenis, sementara masih tidak mengekspos struktur Anda.
Anda dapat memalsunya menggunakan pointer fungsi, dan pada kenyataannya, saya pikir secara teori dimungkinkan untuk mengkompilasi program C ++ ke C.
Namun, jarang masuk akal untuk memaksakan paradigma pada suatu bahasa daripada memilih bahasa yang menggunakan paradigma.
Berorientasi objek C, dapat dilakukan, saya telah melihat jenis kode dalam produksi di Korea, dan itu adalah monster paling mengerikan yang pernah saya lihat selama bertahun-tahun (ini seperti tahun lalu (2007) yang saya lihat kode). Jadi ya itu bisa dilakukan, dan ya orang sudah melakukannya sebelumnya, dan masih melakukannya bahkan di zaman sekarang ini. Tapi saya akan merekomendasikan C ++ atau Objective-C, keduanya bahasa yang lahir dari C, dengan tujuan memberikan orientasi objek dengan paradigma yang berbeda.
Jika Anda yakin bahwa pendekatan OOP lebih unggul untuk masalah yang Anda coba selesaikan, mengapa Anda mencoba menyelesaikannya dengan bahasa non-OOP? Sepertinya Anda menggunakan alat yang salah untuk pekerjaan itu. Gunakan C ++ atau bahasa varian C berorientasi objek lainnya.
Jika Anda bertanya karena Anda mulai membuat kode pada proyek besar yang sudah ada yang ditulis dalam C, maka Anda tidak boleh mencoba memaksakan paradigma OOP Anda sendiri (atau orang lain) ke dalam infrastruktur proyek. Ikuti panduan yang sudah ada dalam proyek. Secara umum, API bersih dan terisolasi perpustakaan dan modul akan pergi jauh ke arah memiliki bersih OOP- ish desain.
Jika, setelah semua ini, Anda benar-benar siap melakukan OOP C, baca ini (PDF).
Ya kamu bisa. Orang-orang menulis berorientasi objek C sebelum C ++ atau Objective-C muncul. Baik C ++ dan Objective-C, sebagian, berupaya untuk mengambil beberapa konsep OO yang digunakan dalam C dan memformalkannya sebagai bagian dari bahasa.
Berikut adalah program yang sangat sederhana yang menunjukkan bagaimana Anda dapat membuat sesuatu yang terlihat seperti / adalah pemanggilan metode (ada cara yang lebih baik untuk melakukan ini. Ini hanya bukti bahwa bahasa mendukung konsep-konsep):
#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;
}
Sedikit kode OOC untuk ditambahkan:
#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;
}
Saya telah menggali ini selama satu tahun:
Karena sistem GObject sulit digunakan dengan C murni, saya mencoba menulis beberapa makro bagus untuk meringankan gaya OO dengan 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;
}
Ini situs proyek saya (saya tidak punya cukup waktu untuk menulis en. Doc, namun doc dalam bahasa Cina jauh lebih baik).
Ada contoh pewarisan menggunakan C dalam ceramah Jim Larson 1996 yang diberikan pada Bagian 312 Seminar Pemrograman Makan Siang di sini: C Tinggi dan Rendah .
Artikel atau buku mana yang baik untuk menggunakan konsep OOP di C?
Dave Hanson C Interfaces dan Implementasi adalah sangat baik pada enkapsulasi dan penamaan dan sangat baik pada penggunaan fungsi pointer. Dave tidak mencoba mensimulasikan pewarisan.
Satu hal yang mungkin ingin Anda lakukan adalah melihat implementasi Xt toolkit untuk X Window . Memang sudah lama di gigi, tetapi banyak struktur yang digunakan dirancang untuk bekerja secara OO dalam C. tradisional. Umumnya ini berarti menambahkan lapisan tipuan tambahan di sana-sini dan merancang struktur untuk saling menempel.
Anda benar-benar dapat melakukan banyak hal di jalan OO yang terletak di C dengan cara ini, meskipun terasa seperti itu beberapa kali, konsep OO tidak muncul sepenuhnya dari pikiran #include<favorite_OO_Guru.h>
. Mereka benar-benar merupakan banyak praktik terbaik yang ada saat itu. Bahasa dan sistem OO hanya menyaring dan memperkuat bagian-bagian dari zeitgeist pemrograman saat itu.
Jawaban untuk pertanyaan itu adalah 'Ya, Anda bisa'.
Object-oriented C (OOC) kit adalah untuk mereka yang ingin memprogram dengan cara berorientasi objek, tetapi tetap menggunakan C lama yang baik juga. OOC mengimplementasikan kelas, pewarisan tunggal dan ganda, penanganan pengecualian.
fitur
• Hanya menggunakan makro C dan fungsi, tidak perlu ekstensi bahasa! (ANSI-C)
• Kode sumber yang mudah dibaca untuk aplikasi Anda. Perawatan diambil untuk membuat hal-hal sesederhana mungkin.
• Warisan tunggal kelas
• Warisan berganda dengan antarmuka dan mixin (sejak versi 1.3)
• Menerapkan pengecualian (dalam C murni!)
• Fungsi virtual untuk kelas
• Alat eksternal untuk implementasi kelas yang mudah
Untuk detail lebih lanjut, kunjungi http://ooc-coding.sourceforge.net/ .
Sepertinya orang mencoba meniru gaya C ++ menggunakan C. Saya kira adalah melakukan pemrograman berorientasi objek C benar-benar melakukan pemrograman berorientasi-struktur. Namun, Anda dapat mencapai hal-hal seperti ikatan yang terlambat, enkapsulasi, dan pewarisan. Untuk warisan Anda secara eksplisit menentukan pointer ke struct dasar di sub struct Anda dan ini jelas merupakan bentuk multiple inheritance. Anda juga harus menentukan apakah Anda
//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);
kompilasi dengan c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj
.
Jadi sarannya adalah tetap berpegang pada gaya C murni dan tidak mencoba memaksakan gaya C ++. Juga cara ini cocok untuk cara yang sangat bersih untuk membangun API.
Lihat http://slkpg.byethost7.com/instance.html untuk twist lain pada OOP di C. Ini menekankan contoh data untuk reentrancy menggunakan hanya asli C. Banyak warisan dilakukan secara manual menggunakan pembungkus fungsi. Keamanan jenis dipertahankan. Ini adalah contoh kecil:
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;
}
Saya agak terlambat ke pesta, tapi saya ingin berbagi pengalaman saya tentang topik: Saya bekerja dengan hal-hal yang disematkan hari ini, dan satu-satunya kompiler (dapat diandalkan) yang saya miliki adalah C, sehingga saya ingin menerapkan berorientasi objek pendekatan dalam proyek tertanam saya ditulis dalam C.
Sebagian besar solusi yang saya lihat sejauh ini banyak menggunakan typecast, jadi kami kehilangan keamanan jenis: kompiler tidak akan membantu Anda jika Anda melakukan kesalahan. Ini benar-benar tidak dapat diterima.
Persyaratan yang saya miliki:
Saya telah menjelaskan pendekatan saya secara rinci dalam artikel ini: Pemrograman berorientasi objek dalam C ; ditambah, ada utilitas untuk autogenerasi kode boilerplate untuk kelas dasar dan turunan.
Saya membangun perpustakaan kecil tempat saya mencobanya dan bagi saya itu berfungsi dengan baik. Jadi saya pikir saya berbagi pengalaman.
https://github.com/thomasfuhringer/oxygen
Warisan tunggal dapat diimplementasikan dengan cukup mudah menggunakan struct dan memperluasnya untuk setiap kelas anak lainnya. Gips sederhana ke struktur induk memungkinkan untuk menggunakan metode induk pada semua keturunan. Selama Anda tahu bahwa sebuah variabel menunjuk ke sebuah struct yang memegang objek semacam ini, Anda selalu dapat melakukan cast ke kelas root dan melakukan introspeksi.
Seperti yang telah disebutkan, metode virtual agak rumit. Tapi itu bisa dilakukan. Untuk menjaga hal-hal sederhana, saya hanya menggunakan array fungsi dalam struktur deskripsi kelas yang setiap kelas anak menyalin dan mengisi kembali slot individu di mana diperlukan.
Berbagai warisan akan agak rumit untuk diterapkan dan disertai dengan dampak kinerja yang signifikan. Jadi saya tinggalkan saja. Saya menganggap itu diinginkan dan berguna dalam beberapa kasus untuk memodelkan keadaan kehidupan nyata dengan bersih, tetapi mungkin 90% dari kasus warisan tunggal mencakup kebutuhan. Dan warisan tunggal itu sederhana dan tidak ada biaya.
Juga saya tidak peduli tentang keamanan tipe. Saya pikir Anda tidak harus bergantung pada kompiler untuk mencegah Anda dari kesalahan pemrograman. Dan itu melindungi Anda hanya dari sebagian kecil kesalahan saja.
Biasanya, dalam lingkungan berorientasi objek Anda juga ingin menerapkan penghitungan referensi untuk mengotomatisasi manajemen memori sejauh mungkin. Jadi saya juga memasukkan jumlah referensi ke kelas root "Object" dan beberapa fungsionalitas untuk merangkum alokasi dan deallokasi memori heap.
Itu semua sangat sederhana dan ramping dan memberi saya esensi OO tanpa memaksa saya untuk berurusan dengan monster yang C ++. Dan saya mempertahankan fleksibilitas untuk tinggal di tanah C, yang antara lain membuatnya lebih mudah untuk mengintegrasikan perpustakaan pihak ketiga.
Saya mengusulkan untuk menggunakan Objective-C, yang merupakan superset dari C.
Sementara Objective-C berusia 30 tahun, memungkinkan untuk menulis kode yang elegan.
Ya, tapi saya belum pernah melihat orang yang mencoba menerapkan polimorfisme apa pun dengan C.