Jak napisać kod obiektowy w C? [zamknięte]
chcesz poprawić to pytanie? Update the question so it edytując ten post.
Zamknięte 4 lata temu .
Popraw to pytanieJakie są sposoby zapisu kodu obiektowego w C? Szczególnie w odniesieniu do polimorfizmu.
Zobacz też to pytanie o przepełnienie stosu orientacja obiektu w C.
10 answers
Tak. W rzeczywistości Axel Schreiner dostarcza swoją książkę "Programowanie obiektowe w ANSI-C" za darmo, która obejmuje ten temat dość dokładnie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 15:52:13
Skoro mówisz o polimorfizmie to tak, możesz, robiliśmy takie rzeczy lata przed powstaniem C++.
Zasadniczo używasz struct
do przechowywania zarówno danych, jak i listy wskaźników funkcji, aby wskazać odpowiednie funkcje dla tych danych.
Więc, w klasie komunikacyjnej, masz otwarte, Odczyt, Zapis i zamknięcie wywołania, które byłyby utrzymywane jako cztery wskaźniki funkcji w strukturze, obok danych dla obiektu, coś like:
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;
Oczywiście powyższe segmenty kodu faktycznie byłyby w "konstruktorze" takim jak rs232Init()
.
Kiedy 'dziedziczysz' z tej klasy, po prostu zmieniasz wskaźniki, aby wskazywały na twoje własne funkcje. Każdy, kto wywołał te funkcje, zrobiłby to za pomocą wskaźników funkcji, dając Twój polimorfizm:
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
Coś w rodzaju ręcznego vtable.
Możesz mieć nawet wirtualne klasy ustawiając wskaźniki NA NULL -zachowanie byłoby nieco różni się od C++ (zrzut pamięci w czasie wykonywania, a nie błąd w czasie kompilacji).
Oto przykładowy kod, który to demonstruje. Pierwsza struktura klasy najwyższego poziomu:
#include <stdio.h>
// The top-level class.
typedef struct sCommClass {
int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;
Wtedy mamy funkcje dla "podklasy" TCP:
// Function for the TCP 'class'.
static int tcpOpen (tCommClass *tcp, char *fspec) {
printf ("Opening TCP: %s\n", fspec);
return 0;
}
static int tcpInit (tCommClass *tcp) {
tcp->open = &tcpOpen;
return 0;
}
Oraz 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;
}
I na koniec testowy program do pokazania go w akcji:
// 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;
}
To daje wyjście:
Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com
Więc widać, że różne funkcje są wywoływane, w zależności od podklasy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-12 00:39:22
Przestrzenie nazw są często wykonywane przez wykonanie:
stack_push(thing *)
Zamiast
stack::push(thing *)
Aby utworzyć C strukturę w coś w rodzaju C++ klasy można przekształcić:
class stack {
public:
stack();
void push(thing *);
thing * pop();
static int this_is_here_as_an_example_only;
private:
...
};
Do
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
I do:
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
}
Nie zrobiłem destruktora ani kasowania, ale to działa zgodnie z tym samym wzorem.
This_is_here_as_an_example_only jest jak statyczna zmienna klasy -- współdzielona pomiędzy wszystkimi instancjami danego typu. Wszystkie metody są naprawdę statyczne, z wyjątkiem tego, że niektóre biorą to *
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 16:14:13
Uważam, że implementacja OOP w C jest nie tylko użyteczna, ale także doskonałym sposobem na nauczenie się OOP i zrozumienie jego wewnętrznego działania. Doświadczenie wielu programistów pokazało, że aby efektywnie i pewnie wykorzystać technikę, programista musi zrozumieć, w jaki sposób podstawowe pojęcia są ostatecznie realizowane. Emulowanie klas, dziedziczenie i polimorfizm w C uczy właśnie tego.
Aby odpowiedzieć na oryginalne pytanie, oto kilka zasobów, które uczą jak zrobić OOP w C:
EmbeddedGurus.com wpis na blogu "Programowanie obiektowe w C" pokazuje jak zaimplementować klasy i pojedyncze dziedziczenie w przenośnym C: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c/
Application Note "" C+" - Object Oriented Programming in C " pokazuje jak zaimplementować klasy, pojedyncze dziedziczenie i późne wiązanie (polimorfizm) w C za pomocą preprocesora makra: http://www.state-machine.com/resources/cplus_3.0_manual.pdf , przykładowy kod jest dostępny od http://www.state-machine.com/resources/cplus_3.0.zip
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-29 01:37:14
Widziałem to. Nie polecam. C++ początkowo zaczynał w ten sposób jako preprocesor, który wytwarzał kod C jako etap pośredni.
Zasadniczo to, co robisz, to tworzenie tabeli wysyłkowej dla wszystkich metod, w której przechowujesz odwołania do funkcji. Wyprowadzenie klasy pociągałoby za sobą skopiowanie tej tabeli dyspozytorskiej i zastąpienie wpisów, które chcesz nadpisać, a twoje nowe "metody" muszą wywołać oryginalną metodę, jeśli chce ona wywołać metodę bazową. W końcu piszesz c++na nowo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-09 04:04:02
Jasne, że to możliwe. To jest to, co GObject , framework, na którym oparte są wszystkie GTK+ i GNOME.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 15:57:01
Biblioteka plików C stdio jest doskonałym przykładem tworzenia abstrakcji, enkapsulacji i modułowości w nieskomplikowanym C.
Dziedziczenie i polimorfizm-inne aspekty często uważane za istotne dla OOP - niekoniecznie zapewniają obiecujący przyrost wydajności i rozsądne pojawiły się argumenty , które mogą faktycznie utrudniać rozwój i myślenie o problemie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-24 11:58:49
Trywialny przykład ze zwierzęciem i psem: lustro mechanizmu vtable C++(w dużej mierze i tak). Oddzielasz również alokację i instancję (Animal_Alloc, Animal_New), więc nie wywołujemy malloc () wiele razy. Musimy również jawnie przekazać wskaźnik this
.
Jeśli miałbyś wykonywać funkcje nie-wirtualne, to jest to trival. Po prostu nie dodajesz ich do vtable i funkcje statyczne nie wymagają wskaźnika this
. Dziedziczenie wielokrotne zazwyczaj wymaga wielu vtables do rozwiązania niejasności.
Również powinieneś być w stanie użyć setjmp/longjmp do obsługi wyjątków.
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. Jest to testowane na kompilatorze C++, ale powinno być łatwe, aby działało na kompilatorze C.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 16:02:51
Sprawdź GObject . Ma być OO W C i jedna implementacja tego czego szukasz. Jeśli naprawdę chcesz OO, przejdź do C++ lub innego języka OOP. GObject może być naprawdę trudne do pracy z czasami, jeśli jesteś przyzwyczajony do czynienia z językami OO, ale jak wszystko, przyzwyczaisz się do konwencji i przepływu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-28 19:53:40
To było ciekawe do przeczytania. Sam zastanawiałem się nad tym samym pytaniem, a korzyści płynące z myślenia o nim są takie:
Próba wyobrażenia sobie, jak zaimplementować pojęcia OOP w języku Nie-OOP pomaga mi zrozumieć mocne strony języka OOp (w moim przypadku C++). To pomaga mi lepiej ocenić, czy używać C lub c++ dla danego typu aplikacji-gdzie korzyści z jednego out-waży drugi.
W moim przeglądaniu stron dla informacje i opinie na ten temat znalazłem autora, który pisał kod dla wbudowanego procesora i miał dostępny tylko kompilator C: http://www.eetimes.com/discussion/other/4024626/Object-Oriented-C-Creating-Foundation-Classes-Part-1
W jego przypadku analiza i adaptacja koncepcji OOP w prostym C była słusznym dążeniem. Wydaje się, że był otwarty na rezygnację z niektórych koncepcji OOP ze względu na hit wydajności wynikający z próby ich wdrożenia w C.
Lekcja, którą wziąłem, jest taka, że można to zrobić do pewnego stopnia, i tak, jest kilka dobrych powodów, aby spróbować.
W końcu maszyna krąży po bitach wskaźnika stosu, powodując, że licznik programu przeskakuje i oblicza operacje dostępu do pamięci. Z punktu widzenia wydajności, im mniej tych obliczeń wykonanych przez twój program, tym lepiej... ale czasami musimy zapłacić ten podatek po prostu, abyśmy mogli zorganizować nasz program w sposób, który czyni go najmniej podatnym za ludzki błąd. Kompilator języka OOP dąży do optymalizacji obu aspektów. Programista musi być znacznie ostrożniejszy w implementacji tych pojęć w języku takim jak C.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 16:28:40