C nesneye yönelik kod yazmanın bazı yolları nelerdir? Özellikle polimorfizm açısından.
Ayrıca bkz. Bu Yığın Taşması sorusu C'deki Nesne yönelimi .
C nesneye yönelik kod yazmanın bazı yolları nelerdir? Özellikle polimorfizm açısından.
Ayrıca bkz. Bu Yığın Taşması sorusu C'deki Nesne yönelimi .
Yanıtlar:
Evet. Aslında Axel Schreiner, “ANSI-C'de Nesneye Yönelik Programlama” kitabını ücretsiz olarak sunuyor ve konuyu oldukça kapsamlı bir şekilde ele alıyor.
Polimorfizmden bahsettiğiniz için evet, yapabilirsiniz, C ++ ortaya çıkmadan yıllar önce bu tür şeyler yapıyorduk.
Temel olarak struct
, verileri tutmak için a öğesini ve bu verilerle ilgili işlevleri işaret etmek için işlev işaretçileri listesini kullanırsınız.
Bu nedenle, bir iletişim sınıfında, bir nesnenin verilerinin yanı sıra, bir nesnenin verilerinin yanında yapıda dört işlev işaretçisi olarak tutulacak bir açık, okuma, yazma ve kapatma çağrınız olur:
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;
Tabii ki, yukarıdaki kod bölümleri aslında gibi bir "yapıcı" olacaktır rs232Init()
.
Bu sınıftan 'miras aldığınızda' sadece işaretçileri kendi işlevlerinizi gösterecek şekilde değiştirirsiniz. Bu işlevleri çağıran herkes bunu işlev işaretçileri aracılığıyla yaparak polimorfizminizi verir:
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
Manuel bir vtable gibi.
İşaretçileri NULL olarak ayarlayarak sanal sınıflara bile sahip olabilirsiniz. Davranış C ++ 'dan biraz farklı olacaktır (derleme zamanında bir hata yerine çalışma zamanında çekirdek dökümü).
İşte bunu gösteren bir örnek kod parçası. İlk olarak üst düzey sınıf yapısı:
#include <stdio.h>
// The top-level class.
typedef struct sCommClass {
int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;
Sonra TCP 'alt sınıfı' için fonksiyonlarımız var:
// 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;
}
Ve HTTP olanı da:
// 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;
}
Ve son olarak bunu eylem halinde göstermek için bir test programı:
// 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;
}
Bu çıktıyı üretir:
Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com
böylece alt sınıfa bağlı olarak farklı işlevlerin çağrıldığını görebilirsiniz.
tCommClass
yeniden adlandırılacak tCommVT
ve bir tCommClass
yapı sadece veri alanlarına ve tCommVT vt
"bir ve tek" sanal tabloya işaret eden tek bir alana sahip olacaktı . Her örnekle tüm işaretçileri taşımak gereksiz ek yükler ekler ve JavaScript'te C ++, IMHO'dan daha fazla şey nasıl yapacağınıza benzer.
Ad alanları genellikle şu şekilde yapılır:
stack_push(thing *)
onun yerine
stack::push(thing *)
Bir yapmak için C bir gibi bir şey haline yapı C ++ Eğer açabilirsiniz sınıfa:
class stack {
public:
stack();
void push(thing *);
thing * pop();
static int this_is_here_as_an_example_only;
private:
...
};
içine
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
Ve yapın:
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
}
Yıkıcı yapmadım ya da sildim, ama aynı kalıbı takip ediyor.
this_is_here_as_an_example_only, bir türün tüm örnekleri arasında paylaşılan statik bir sınıf değişkeni gibidir. Tüm yöntemler gerçekten statiktir, ancak bazıları bunu alır *
st->my_type->push(st, thing2);
yerinest->my_type.push(st, thing2);
struct stack_type my_type;
yerinestruct stack_type * my_type;
Class
yapıya ne dersiniz ? Bu, OO C'yi C ++ 'dan daha dinamik hale getirecektir . Peki ya bu? Bu arada +1.
Kendi başına faydalı olmanın yanı sıra, C'de OOP uygulamanın, OOP öğrenmek ve iç işleyişini anlamak için mükemmel bir yol olduğuna inanıyorum . Birçok programcının deneyimi, bir tekniği verimli ve güvenli bir şekilde kullanmak için, bir programcının temel kavramların nihayetinde nasıl uygulandığını anlaması gerektiğini göstermiştir. C'deki taklit sınıfları, kalıtım ve polimorfizm sadece bunu öğretir.
Orijinal soruyu cevaplamak için, C'de OOP'nin nasıl yapılacağını öğreten birkaç kaynak:
EmbeddedGurus.com blog yazısı "C'de nesne tabanlı programlama" taşınabilir C'de sınıfların ve tek mirasın nasıl uygulanacağını gösterir: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c /
Uygulama Notu "" C + "- C'de Nesneye Yönelik Programlama" önişlemci makroları kullanarak C'de sınıfların, tek mirasın ve geç bağlamanın (polimorfizm) nasıl uygulanacağını gösterir: http://www.state-machine.com/resources/cplus_3. 0_manual.pdf , örnek kodu http://www.state-machine.com/resources/cplus_3.0.zip adresinde bulabilirsiniz.
Yaptığını gördüm. Ben tavsiye etmem. C ++ başlangıçta bu yolu bir ara adım olarak C kodu üreten bir önişlemci olarak başlattı.
Esasen, sonuçta işlev referanslarınızı sakladığınız tüm yöntemleriniz için bir gönderim tablosu oluşturmaktır. Bir sınıfın türetilmesi, bu dağıtım tablosunun kopyalanmasını ve geçersiz kılmak istediğiniz girişlerin, temel yöntemi çağırmak istiyorsa orijinal yöntemi çağırmak zorunda olan yeni "yöntemleriniz" ile değiştirilmesini gerektirir. Sonunda, C ++ yeniden yazmak sonunda.
glib
objektif bir şekilde C ile yazılmış?
C stdio FILE alt kütüphanesi, katkısız C'de soyutlama, kapsülleme ve modülerliğin nasıl oluşturulacağına mükemmel bir örnektir.
Kalıtım ve polimorfizm - genellikle OOP için gerekli görülen diğer yönler - söz verdikleri verimlilik kazanımlarını ve makul argümanların sahip olması şart değildir. yapıldıktan onlar sorun etki alanı hakkında aslında hinder geliştirme ve düşünme bunu başaramaz.
Hayvan ve Köpek ile önemsiz bir örnek: C ++ 'ın vtable mekanizmasını yansıtırsınız (büyük ölçüde her halükarda). Ayrıca ayırmayı ve somutlaştırmayı (Animal_Alloc, Animal_New) ayırırsınız, bu nedenle malloc () işlevini birden çok kez çağırmayız. this
İşaretçiyi de açıkça geçirmeliyiz .
Sanal olmayan işlevler yapacak olsaydınız, bu üç değerdir. Bunları vtable'a eklemezsiniz ve statik işlevler bir this
işaretçi gerektirmez . Çoklu kalıtım, belirsizlikleri çözmek için genellikle birden fazla veable gerektirir.
Ayrıca, kural dışı durum işleme için setjmp / longjmp komutunu kullanabilmeniz gerekir.
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. Bu bir C ++ derleyicisi üzerinde test edilmiştir, ancak bir C derleyicisi üzerinde çalışmasını sağlamak kolay olmalıdır.
typedef
içinde a içinde struct
mümkün değildir
Bunu okumak ilginçti. Ben de aynı soruyu kendim düşünmekteyim ve bunun üzerinde düşünmenin faydaları şunlardır:
OOP kavramlarının OOP olmayan bir dilde nasıl uygulanacağını hayal etmeye çalışmak OOp dilinin (benim durumumda C ++) güçlü yanlarını anlamama yardımcı olur. Bu, belirli bir uygulama türü için C veya C ++ kullanılıp kullanılmayacağına dair birinin daha iyi yargılanmasına yardımcı olur - birinin yararlarının diğerini aştığı.
Bu konuda bilgi ve görüşler için web'e göz attığımda, gömülü bir işlemci için kod yazan ve sadece bir C derleyicisi bulunan bir yazar buldum: http://www.eetimes.com/discussion/other/4024626/Object-Oriented C-oluşturma-Hazırlık-Sınıflar-Bölüm-1
Onun durumunda, düz C'deki OOP kavramlarını analiz etmek ve uyarlamak geçerli bir arayıştı. Görünüşe göre C'de uygulama girişiminden kaynaklanan performans havai vuruşundan dolayı bazı OOP kavramlarını feda etmeye açıktı.
Aldığım ders, evet belli bir dereceye kadar yapılabilir ve evet, bunu denemek için bazı iyi nedenler var.
Sonunda, makine yığın sayacı bitlerini döndürerek program sayacını atlatıyor ve bellek erişim işlemlerini hesaplıyor. Verimlilik açısından, bu hesaplamalar programınız tarafından ne kadar az yapılırsa o kadar iyidir ... ama bazen bu vergiyi ödemek zorundayız, böylece programımızı insan hatasına en az duyarlı hale getirecek şekilde düzenleyebiliriz. OOP dil derleyicisi her iki yönü de optimize etmeye çalışır. Programcı bu kavramların C gibi bir dilde uygulanmasında çok daha dikkatli olmalıdır.
Core Foundation API setleri için Apple'ın belgelerine bakmakta yardımcı olabilirsiniz. Saf bir C API'sıdır, ancak türlerin çoğu Objective-C nesne eşdeğerlerine köprülenir.
Objective-C'nin tasarımına bakmak da faydalı olabilir. Nesne sisteminin C işlevleri açısından tanımlanması, örneğin objc_msg_send
bir nesne üzerindeki bir yöntemi çağırmak için C ++ 'dan biraz farklıdır . Derleyici köşeli parantez sözdizimini bu işlev çağrılarına dönüştürür, bu yüzden bunu bilmek zorunda değilsiniz, ancak sorunuzu göz önünde bulundurarak kaputun altında nasıl çalıştığını öğrenmek yararlı olabilir.
Kullanılabilecek birkaç teknik vardır. En önemlisi, projenin nasıl bölüneceği. Projemizde .h dosyasında bildirilen bir arabirimi ve nesnenin bir .c dosyasında uygulanmasını sağlıyoruz. Önemli olan, .h dosyasını içeren tüm modüllerin yalnızca bir nesneyivoid *
görmesidir ve .c dosyası yapının içlerini bilen tek modüldür.
Bir sınıf için böyle bir şey örnek olarak FOO adını veriyoruz:
.H dosyasında
#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 uygulama dosyası böyle bir şey olacaktır.
#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;
}
Bu yüzden işaretçiyi bir nesneye o modülün her fonksiyonuna açıkça veriyorum. Bir C ++ derleyicisi bunu örtük olarak yapar ve C'de açıkça yazarız.
Gerçekten kullanıyorum this
Programlarımda C ++ ile derlenmediğinden emin olmak için programlarımda ve sözdizimi vurgulama düzenleyicimde başka bir renkte olma özelliğine sahip.
FOO_struct alanları bir modülde değiştirilebilir ve hala kullanılabilir olması için başka bir modülün yeniden derlenmesi gerekmez.
Bu stille zaten OOP (veri kapsülleme) avantajlarının büyük bir kısmını ele alıyorum. İşlev işaretçileri kullanarak, kalıtım gibi bir şeyi uygulamak bile kolaydır, ancak dürüst olmak gerekirse, gerçekten nadiren yararlıdır.
typedef struct FOO_type FOO_type
başlığında geçersiz bir typedef yerine hala yapısını açığa değilken, tür denetleme yararı olsun.
İşlev işaretçileri kullanarak taklit edebilirsiniz ve aslında, C ++ programlarını C'ye derlemek teorik olarak mümkün olduğunu düşünüyorum.
Ancak, paradigma kullanan bir dil seçmek yerine bir paradigmayı bir dile zorlamak nadiren mantıklıdır.
Nesneye yönelik C, yapılabilir, Kore'de üretimde bu tür bir kod gördüm ve yıllar içinde gördüğüm en korkunç canavardı (bu kod geçen yıl (2007) gibiydi). Yani evet yapılabilir ve evet insanlar daha önce yapmışlardı ve hala bu gün ve yaşta bile yapıyorlar. Ancak C ++ veya Objective-C'yi öneririm, her ikisi de farklı paradigmalarla nesne yönelimi sağlamak amacıyla C'den doğan dillerdir.
Bir OOP yaklaşımının çözmeye çalıştığınız sorundan daha üstün olduğuna inanıyorsanız, neden OOP olmayan bir dille çözmeye çalışıyorsunuz? Görünüşe göre iş için yanlış aracı kullanıyorsunuz. C ++ veya başka bir nesne yönelimli C varyant dili kullanın.
C ile yazılmış zaten var olan büyük bir projeyi kodlamaya başladığınızı soruyorsanız, kendi (veya başka birinin) OOP paradigmalarını projenin altyapısına zorlamaya çalışmamalısınız. Projede zaten mevcut olan yönergeleri izleyin. Genel olarak, temiz API'ler ve izole kütüphaneler ve modüller temiz OOP- sahip yolunda uzun bir yol gidecek imsi tasarım.
Tüm bunlardan sonra, gerçekten OOP C yapmayı ayarlanmışsa, okumak bu (PDF).
Evet yapabilirsin. İnsanlar C ++ veya Objective-C'den önce nesne yönelimli C yazıyordu sahneye . Hem C ++ hem de Objective-C, kısmen, C'de kullanılan OO kavramlarından bazılarını almaya ve bunları dilin bir parçası olarak resmileştirmeye çalışıyordu.
İşte benzeyen / bir yöntem çağrısı olan bir şeyi nasıl yapabileceğinizi gösteren gerçekten basit bir program (bunu yapmanın daha iyi yolları var. Bu sadece dilin kavramları desteklediğinin kanıtıdır):
#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;
}
Eklenecek küçük bir OOC kodu:
#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;
}
Bunu bir yıldır kazıyorum:
GObject sisteminin saf C ile kullanımı zor olduğundan, C ile OO stilini hafifletmek için bazı güzel makrolar yazmaya çalıştım.
#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;
}
İşte benim proje sitesi (en. Doc yazmak için yeterli zamanım yok, ancak çince doktor çok daha iyi).
Verilen Jim Larson 1996 konuşma C'yi kullanarak miras örneği vardır Öğle Semineri Programlama Bölüm 312'ye : Burada yüksek ve alçak Seviye C .
Hangi makaleler veya kitaplar C'de OOP kavramlarını kullanmakta iyidir?
Dave Hanson'un Arayüzler ve gerçekleştirmeler olan mükemmel kapsülleme ve adlandırma ve işlev işaretçileri kullanımı konusunda çok iyi. Dave mirası simüle etmeye çalışmaz.
Yapmak isteyebileceğiniz bir şey , X Window için Xt araç setinin uygulanmasına bakmaktır . Elbette dişte uzun sürüyor, ancak kullanılan yapıların çoğu geleneksel C içinde bir OO tarzında çalışacak şekilde tasarlandı.
C'de bu şekilde yer alan OO yolunda gerçekten çok şey yapabilirsiniz, bazı zamanlarda hissettirse de, OO kavramları zihninden tam olarak oluşmadı #include<favorite_OO_Guru.h>
. Gerçekten de zamanın en iyi uygulamalarını oluşturdular. OO dilleri ve sistemleri sadece günün programlama zeitgeist damıtılmış ve güçlendirilmiş bölümleri.
Sorunun cevabı 'Evet, yapabilirsiniz'.
Nesne yönelimli C (OOC) kiti, nesne yönelimli bir şekilde programlamak isteyenler içindir, ancak eski C'ye de yapışır. OOC sınıfları, tekli ve çoklu kalıtım, istisna yönetimi uygular.
Özellikleri
• Yalnızca C makroları ve işlevleri kullanır, dil uzantısı gerekmez! (ANSI-C)
• Uygulamanız için okunması kolay kaynak kodu. İşleri mümkün olduğunca basit hale getirmeye özen gösterildi.
• Sınıfların tek mirası
• Arayüzler ve karışımlar ile çoklu kalıtım (sürüm 1.3'ten beri)
• İstisnaları uygulama (saf C'de!)
• Sınıflar için sanal fonksiyonlar
• Kolay sınıf uygulaması için harici araç
Daha fazla ayrıntı için http://ooc-coding.sourceforge.net/ adresini ziyaret edin .
Görünen o ki insanlar C kullanarak C ++ stilini taklit etmeye çalışıyorlar. Ancak geç bağlama, kapsülleme ve kalıtım gibi şeyleri başarabilirsiniz. Kalıtım için, alt yapınızdaki temel yapılara açıkça bir işaretçi tanımlarsınız ve bu açıkça çoklu kalıtım biçimidir. Ayrıca,
//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);
ile derlemek c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj
.
Bu yüzden tavsiye, saf bir C stiline bağlı kalmak ve bir C ++ stiline zorlamaya çalışmamaktır. Ayrıca bu şekilde bir API oluşturmanın çok temiz bir yoluna sahiptir.
Bkz http://slkpg.byethost7.com/instance.html elle fonksiyon sarmalayıcılarını aracılığıyla kontrol edilir sadece yerli C. Çoklu miras kullanarak reentrancy için örnek verileri vurgulayan C. OOP başka büküm henüz için. Tip güvenliği sağlanır. İşte küçük bir örnek:
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;
}
Partiye biraz geç kaldım, ancak konuyla ilgili deneyimlerimi paylaşmak istiyorum: Bu günlerde gömülü öğelerle çalışıyorum ve sahip olduğum tek (güvenilir) derleyici C, böylece nesne odaklı uygulamak istiyorum dilinde yazılmış gömülü projelerimde yaklaşım
Şimdiye kadar gördüğüm çözümlerin çoğu, tipik olarak daktiloları kullanıyor, bu nedenle tip güvenliğini kaybediyoruz: hata yaparsanız derleyici size yardımcı olmaz. Bu tamamen kabul edilemez.
Sahip olduğum gereksinimler:
Bu makalede yaklaşımımı ayrıntılı olarak açıkladım: C dilinde nesne yönelimli programlama ; ayrıca, temel ve türetilmiş sınıflar için kazan plakası kodunun otomatik üretimi için bir yardımcı program vardır.
Bunu denediğim küçük bir kütüphane inşa ettim ve bana göre çok iyi çalışıyor. Deneyimi paylaştığımı düşündüm.
https://github.com/thomasfuhringer/oxygen
Tek miras, bir yapı kullanılarak ve diğer tüm çocuk sınıfları için genişletilerek kolayca uygulanabilir. Ana yapıya basit bir döküm, tüm torunlarda ana yöntemlerin kullanılmasını mümkün kılar. Bir değişkenin bu tür bir nesneyi tutan bir yapıya işaret ettiğini bildiğiniz sürece, her zaman kök sınıfına atabilir ve içgözlem yapabilirsiniz.
Daha önce de belirtildiği gibi, sanal yöntemler biraz daha zordur. Ama yapılabilirler. İşleri basit tutmak için, sınıf tanımlama yapısında, her alt sınıfın gerektiğinde ayrı yuvaları kopyalayıp yeniden doldurduğu bir dizi işlev kullanıyorum.
Çoklu kalıtımın uygulanması oldukça karmaşıktır ve önemli bir performans etkisi ile birlikte gelir. Bu yüzden bırakıyorum. Birkaç durumda gerçek yaşam koşullarını temiz bir şekilde modellemenin arzu edilir ve yararlı olduğunu düşünüyorum, ancak vakaların% 90'ında tek miras ihtiyaçları karşılamaktadır. Ve tek miras basittir ve hiçbir maliyeti yoktur.
Ayrıca tip güvenliği de umurumda değil. Programlama hatalarını önlemek için derleyiciye güvenmemeniz gerektiğini düşünüyorum. Ve yine de sizi hataların sadece küçük bir kısmından korur.
Genellikle, nesne yönelimli bir ortamda, bellek yönetimini mümkün olduğunca otomatikleştirmek için referans sayımı uygulamak istersiniz. Bu yüzden "Nesne" kök sınıfına bir referans sayımı ve yığın belleğin tahsisini ve yeniden yerleştirilmesini kapsüllemek için bazı işlevler koydum.
Her şey çok basit ve yalın ve beni C ++ olan canavarla uğraşmaya zorlamadan OO'nun temellerini veriyor. Ve diğer şeylerin yanı sıra üçüncü taraf kütüphanelerini entegre etmeyi kolaylaştıran C topraklarında kalma esnekliğini koruyorum.
C'nin bir üst kümesi olan Objective-C'yi kullanmayı öneriyorum.
Objective-C 30 yaşındayken zarif kod yazmanıza izin verir.
Evet, ama hiç kimsenin C ile herhangi bir çeşit polimorfizm uygulamaya çalıştığını görmedim.