Jakie są sposoby pisania kodu obiektowego w C? Zwłaszcza w odniesieniu do polimorfizmu.
Patrz także przepełnieniem stosu tego zapytania obiektowego w C orientację .
Jakie są sposoby pisania kodu obiektowego w C? Zwłaszcza w odniesieniu do polimorfizmu.
Patrz także przepełnieniem stosu tego zapytania obiektowego w C orientację .
Odpowiedzi:
Tak. W rzeczywistości Axel Schreiner udostępnia swoją książkę „Programowanie obiektowe w ANSI-C” za darmo, która dość dokładnie omawia ten temat.
źródło
Skoro mówisz o polimorfizmie, to tak, możesz, robiliśmy takie rzeczy wiele lat 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.Tak więc w klasie komunikacyjnej miałbyś otwarte, do odczytu, zapisu i zamykania wywołanie, które byłyby utrzymywane jako cztery wskaźniki funkcji w strukturze, obok danych obiektu, coś w stylu:
Oczywiście powyższe segmenty kodu byłyby w rzeczywistości „konstruktorem”, takim jak
rs232Init()
.Kiedy „dziedziczysz” po tej klasie, po prostu zmieniasz wskaźniki, aby wskazywały własne funkcje. Każdy, kto wywołał te funkcje, zrobiłby to poprzez wskaźniki funkcji, dając ci polimorfizm:
Coś jak ręczny vtable.
Możesz nawet mieć klasy wirtualne, ustawiając wskaźniki na NULL - zachowanie będzie nieco inne niż C ++ (zrzut rdzenia w czasie wykonywania, a nie błąd w czasie kompilacji).
Oto przykładowy kod, który to pokazuje. Najpierw struktura klasy najwyższego poziomu:
Następnie mamy funkcje dla „podklasy” TCP:
I również HTTP:
I na koniec program testowy pokazujący go w akcji:
To daje wynik:
dzięki czemu można zobaczyć, że wywoływane są różne funkcje, w zależności od podklasy.
źródło
tCommClass
Zmieniono by nazwę natCommVT
, atCommClass
struct miałby tylko pola danych i jednotCommVT vt
pole wskazujące na „jedną i jedyną” wirtualną tabelę. Przenoszenie wszystkich wskaźników z każdą instancją powoduje niepotrzebne obciążenie i przypomina bardziej sposób wykonywania czynności w JavaScript niż C ++, IMHO.Przestrzenie nazw są często wykonywane przez:
zamiast
Aby zmienić strukturę C w coś w rodzaju klasy C ++ , możesz zmienić:
W
I robić:
Nie zrobiłem destruktora ani nie usunąłem, ale ma ten sam wzór.
this_is_here_as_an_example_only jest jak zmienna klasy statycznej - wspólna dla wszystkich instancji typu. Wszystkie metody są naprawdę statyczne, z wyjątkiem niektórych, że biorą to *
źródło
st->my_type->push(st, thing2);
zamiastst->my_type.push(st, thing2);
struct stack_type my_type;
zamiaststruct stack_type * my_type;
Class
strukturę? To sprawiłoby, że OO C byłby bardziej dynamiczny niż C ++. Co ty na to? Nawiasem mówiąc, +1.Uważam, że oprócz tego, że jest użyteczne samo w sobie, wdrożenie OOP w C jest doskonałym sposobem na naukę OOP i zrozumienie jego wewnętrznego działania. Doświadczenie wielu programistów wykazało, że aby zastosować technikę skutecznie i pewnie, programista musi zrozumieć, w jaki sposób ostatecznie wdrażane są podstawowe koncepcje. Uczą tego właśnie emulowanie klas, dziedziczenie i polimorfizm w C.
Aby odpowiedzieć na pierwotne pytanie, oto kilka zasobów, które uczą, jak wykonywać OOP w C:
Wpis na blogu EmbeddedGurus.com „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 /
Nota aplikacyjna „„ C + ”- programowanie obiektowe w C” pokazuje, jak zaimplementować klasy, pojedyncze dziedziczenie i późne wiązanie (polimorfizm) w C przy użyciu makr preprocesora: http://www.state-machine.com/resources/cplus_3. 0_manual.pdf , przykładowy kod jest dostępny na stronie http://www.state-machine.com/resources/cplus_3.0.zip
źródło
Widziałem to zrobione. Nie poleciłbym tego. C ++ początkowo zaczął w ten sposób jako preprocesor, który wytworzył kod C jako etap pośredni.
Zasadniczo to, co ostatecznie robisz, to utworzenie tabeli wysyłania dla wszystkich metod, w których przechowujesz odwołania do funkcji. Wyprowadzenie klasy wymagałoby skopiowania tej tabeli wysyłania i zastąpienia pozycji, które chcesz zastąpić, a nowe „metody” musiałyby wywoływać metodę oryginalną, jeśli chce wywołać metodę podstawową. Ostatecznie przepisujesz C ++.
źródło
glib
napisane w C w sposób obiektywny?Jasne, że to możliwe. To właśnie robi GObject , struktura, na której opiera się cała GTK + i GNOME .
źródło
Podbiblioteka C stdio FILE jest doskonałym przykładem tworzenia abstrakcji, enkapsulacji i modułowości w nieskażonym C.
Dziedziczenie i polimorfizm - inne aspekty często uważane za niezbędne do OOP - nie koniecznie zapewnić wzrost wydajności obiecują i rozsądne argumenty nie zostały wykonane , że może faktycznie utrudniać rozwoju i myślenia o domenie problemu.
źródło
Trywialny przykład ze zwierzęciem i psem: odzwierciedlasz mechanizm vtable C ++ (w dużej mierze i tak). Oddzielasz także alokację i tworzenie instancji (Animal_Alloc, Animal_New), abyśmy nie wywoływali malloc () wiele razy. Musimy także wyraźnie przekazać
this
wskaźnik.Jeśli miałbyś wykonywać funkcje inne niż wirtualne, to banalne. Po prostu nie dodajesz ich do funkcji vtable, a funkcje statyczne nie wymagają
this
wskaźnika. Wielokrotne dziedziczenie zwykle wymaga wielu tabel vt, aby rozwiązać niejednoznaczności.Ponadto powinieneś być w stanie używać setjmp / longjmp do obsługi wyjątków.
PS. Jest to testowane na kompilatorze C ++, ale powinno być łatwe, aby działało na kompilatorze C.
źródło
typedef
w środkustruct
nie jest możliwe w C.Sprawdź GObject . To ma być OO w C i jedna implementacja tego, czego szukasz. Jeśli jednak naprawdę chcesz OO, wybierz C ++ lub inny język OOP. GObject może być naprawdę trudny do pracy, jeśli jesteś przyzwyczajony do pracy z językami OO, ale jak wszystko, przyzwyczaisz się do konwencji i przepływu.
źródło
To było interesujące do przeczytania. Zastanawiam się nad tym samym pytaniem, a korzyści z myślenia o tym są następujące:
Próba wyobrażenia sobie, jak wdrożyć koncepcje OOP w języku innym niż OOP, pomaga mi zrozumieć mocne strony języka OOp (w moim przypadku C ++). Pomaga mi to lepiej ocenić, czy używać C czy C ++ dla danego typu aplikacji - gdy korzyści jednej z nich przewyższają drugą.
Podczas przeglądania stron internetowych w poszukiwaniu informacji i opinii 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 zwykłym C była ważnym celem. Wygląda na to, że był otwarty na poświęcenie niektórych koncepcji OOP ze względu na ogólne obciążenie wydajności wynikające z próby ich wdrożenia w C.
Lekcja, którą podjąłem, jest taka, że można to zrobić do pewnego stopnia i tak, istnieje kilka dobrych powodów, aby ją podjąć.
Na koniec maszyna kręci bitami wskaźnika stosu, co powoduje, że licznik programu przeskakuje i oblicza operacje dostępu do pamięci. Z punktu widzenia wydajności, im mniej obliczeń wykonanych przez twój program, tym lepiej ... ale czasami musimy płacić ten podatek po prostu, abyśmy mogli zorganizować nasz program w taki sposób, aby był najmniej podatny na błędy ludzkie. Kompilator języka OOP stara się zoptymalizować oba aspekty. Programista musi być znacznie bardziej ostrożny przy wdrażaniu tych pojęć w języku takim jak C.
źródło
Pomocne może okazać się przejrzenie dokumentacji Apple dotyczącej zestawu interfejsów API Core Foundation. Jest to interfejs API w czystym języku C, ale wiele typów jest zmostkowanych z odpowiednikami obiektów Objective-C.
Pomocne może być również przyjrzenie się projektowi samego Celu-C. Różni się nieco od C ++ tym, że system obiektowy jest zdefiniowany pod względem funkcji C, np.
objc_msg_send
Do wywołania metody na obiekcie. Kompilator tłumaczy składnię nawiasów kwadratowych na te wywołania funkcji, więc nie musisz tego znać, ale biorąc pod uwagę twoje pytanie, może okazać się przydatne, aby dowiedzieć się, jak działa pod maską.źródło
Istnieje kilka technik, które można zastosować. Najważniejsze jest to, jak podzielić projekt. W naszym projekcie używamy interfejsu zadeklarowanego w pliku .h oraz implementacji obiektu w pliku .c. Ważną częścią jest to, że wszystkie moduły zawierające plik .h widzą tylko obiekt jako
void *
, a plik .c jest jedynym modułem, który zna wewnętrzne struktury.Coś takiego w przypadku klasy nazywamy FOO jako przykład:
W pliku .h
Plik implementacyjny w C będzie taki.
Daję więc wskaźnik wprost do obiektu każdej funkcji tego modułu. Kompilator C ++ robi to domyślnie, aw C wypisujemy go jawnie.
Naprawdę używam
this
w moich programach, aby upewnić się, że mój program nie kompiluje się w C ++ i że ma dobrą właściwość bycia w innym kolorze w moim edytorze podświetlania składni.Pola FOO_struct można modyfikować w jednym module, a drugiego modułu nawet nie trzeba rekompilować, aby nadal można go było używać.
W tym stylu już korzystam z dużej części zalet OOP (enkapsulacji danych). Używając wskaźników funkcji, można nawet łatwo wdrożyć coś takiego jak dziedziczenie, ale szczerze mówiąc, to naprawdę bardzo rzadko przydatne.
źródło
typedef struct FOO_type FOO_type
zamiast typedef, aby unieważnić nagłówek, zyskasz dodatkową zaletę sprawdzania typu, wciąż nie ujawniając swojej struktury.Można go sfałszować za pomocą wskaźników funkcji, i faktycznie myślę, że teoretycznie możliwe jest skompilowanie programów C ++ w C.
Jednak rzadko ma sens narzucanie paradygmatu na język, a nie wybór języka, który używa paradygmatu.
źródło
Zorientowane obiektowo C, można to zrobić, widziałem tego typu kod w produkcji w Korei i był to najstraszniejszy potwór, jaki widziałem od lat (tak jak w zeszłym roku (2007), widziałem kod). Tak, można to zrobić i tak, ludzie robili to wcześniej i nadal robią to nawet w dzisiejszych czasach. Ale polecam C ++ lub Objective-C, oba są językami pochodzącymi z C, w celu zapewnienia orientacji obiektowej z różnymi paradygmatami.
źródło
Jeśli jesteś przekonany, że podejście OOP jest lepsze od problemu, który próbujesz rozwiązać, dlaczego miałbyś próbować go rozwiązać za pomocą języka innego niż OOP? Wygląda na to, że używasz niewłaściwego narzędzia do pracy. Użyj C ++ lub innego zorientowanego obiektowo języka C.
Jeśli pytasz, ponieważ zaczynasz pisać na już istniejącym dużym projekcie napisanym w C, nie powinieneś próbować narzucać własnych (lub kogokolwiek innego) paradygmatów OOP do infrastruktury projektu. Postępuj zgodnie z wytycznymi, które są już obecne w projekcie. Na ogół czyste API i izolowane biblioteki i moduły będzie przejść długą drogę w kierunku posiadania czystego OOP- owski projekt.
Jeśli po tym wszystkim naprawdę zaczynasz robić OOP C, przeczytaj to (PDF).
źródło
Tak, możesz. Ludzie pisali obiektowe C przed pojawieniem się C ++ lub Objective-C . Zarówno C ++, jak i Objective-C były częściowo próbami przejęcia niektórych koncepcji OO używanych w C i sformalizowania ich jako części języka.
Oto naprawdę prosty program, który pokazuje, jak zrobić coś, co wygląda jak / jest wywołaniem metody (istnieją lepsze sposoby na zrobienie tego. To tylko dowód, że język obsługuje te pojęcia):
źródło
Oczywiście nie będzie tak ładna, jak używanie języka z wbudowaną obsługą. Napisałem nawet „asembler obiektowy”.
źródło
Mały kod OOC do dodania:
źródło
Kopie to przez rok:
Ponieważ system GObject jest trudny w użyciu z czystym C, próbowałem napisać fajne makra, aby złagodzić styl OO za pomocą C.
Oto moja strona projektu (nie mam wystarczająco dużo czasu na napisanie en. Doc, jednak dokument w języku chińskim jest znacznie lepszy).
OOC-GCC
źródło
Jest przykładem dziedziczenia przy użyciu C w 1996 Dyskusja Jim Larson podano w sekcji 312 Programowanie Lunchtime Seminarium tutaj: wysokiego i niskiego poziom C .
źródło
Dave Hansona C Interfejsy i implementacje jest doskonała na hermetyzacji i nazewnictwa i bardzo dobrze na wykorzystaniu wskaźników funkcji. Dave nie próbuje symulować dziedziczenia.
źródło
OOP jest tylko paradygmatem, w którym dane są ważniejsze niż kod w programach. OOP nie jest językiem. Tak więc, podobnie jak zwykły C jest prostym językiem, OOP w zwykłym C jest również prosty.
źródło
Jedną z rzeczy, które możesz chcieć zrobić, jest przyjrzenie się implementacji zestawu narzędzi Xt dla X Window . Pewnie, że robi się to długo w zębie, ale wiele użytych konstrukcji zaprojektowano tak, aby działały w sposób tradycyjny w tradycyjnym C.Ogólnie oznacza to dodanie dodatkowej warstwy pośredniej tu i tam i zaprojektowanie struktur do nakładania się na siebie.
W ten sposób można naprawdę wiele zrobić na drodze OO usytuowanej w C, chociaż czasami wydaje się, że tak się dzieje, koncepcje OO nie powstały w pełni z umysłu
#include<favorite_OO_Guru.h>
. Naprawdę stanowiły one wiele ustalonych najlepszych praktyk tamtych czasów. Języki i systemy OO tylko destylują i wzmacniają części programistycznego entuzjazmu dnia.źródło
Odpowiedź na pytanie brzmi „tak, możesz”.
Obiektowy zestaw C (OOC) jest przeznaczony dla tych, którzy chcą programować w sposób zorientowany obiektowo, ale trzymają się również starego, dobrego C. OOC implementuje klasy, pojedyncze i wielokrotne dziedziczenie, obsługę wyjątków.
funkcje
• Używa tylko makr C i funkcji, nie wymaga rozszerzeń językowych! (ANSI-C)
• Łatwy do odczytania kod źródłowy dla twojej aplikacji. Zadbano o to, aby sprawy były jak najprostsze.
• Pojedyncze dziedzictwo klas
• Wielokrotne dziedziczenie przez interfejsy i mixiny (od wersji 1.3)
• Wdrażanie wyjątków (w czystym C!)
• Funkcje wirtualne dla klas
• Zewnętrzne narzędzie do łatwej implementacji klasy
Aby uzyskać więcej informacji, odwiedź http://ooc-coding.sourceforge.net/ .
źródło
Wygląda na to, że ludzie próbują emulować styl C ++ za pomocą C. Uważam, że programowanie obiektowe C to tak naprawdę programowanie strukturalne. Można jednak osiągnąć takie cele, jak późne wiązanie, enkapsulacja i dziedziczenie. W przypadku dziedziczenia jawnie definiujesz wskaźnik do struktur podstawowych w podstruktury i jest to oczywiście forma wielokrotnego dziedziczenia. Musisz także ustalić, czy Twój
skompiluj z
c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj
.Dlatego radzę trzymać się czystego stylu C i nie próbować narzucać stylu C ++. Również w ten sposób można uzyskać bardzo czysty sposób budowania interfejsu API.
źródło
Zobacz http://slkpg.byethost7.com/instance.html, aby dowiedzieć się więcej o OOP w C. Podkreśla on dane instancji dla ponownego ustalenia przy użyciu tylko natywnego C. Wielokrotne dziedziczenie odbywa się ręcznie przy użyciu opakowań funkcji. Bezpieczeństwo typu jest zachowane. Oto mała próbka:
źródło
Jestem trochę spóźniony na imprezę, ale chcę podzielić się swoim doświadczeniem na ten temat: w tej chwili pracuję z osadzonymi rzeczami, a jedynym (niezawodnym) kompilatorem, który posiadam, jest C, więc chcę zastosować obiektowe podejście w moich osadzonych projektach napisanych w C.
Większość rozwiązań, które do tej pory widziałem, intensywnie wykorzystują rzutowania, więc tracimy bezpieczeństwo typu: kompilator nie pomoże ci, jeśli popełnisz błąd. To jest całkowicie niedopuszczalne.
Wymagania, które mam:
Wyjaśniłem moje podejście szczegółowo w tym artykule: Programowanie obiektowe w C ; a ponadto istnieje narzędzie do automatycznego generowania kodu płyty bazowej dla klas podstawowych i pochodnych.
źródło
Zbudowałem małą bibliotekę, w której próbowałem i dla mnie działa naprawdę ładnie. Pomyślałem więc, że podzielę się tym doświadczeniem.
https://github.com/thomasfuhringer/oxygen
Pojedyncze dziedziczenie można dość łatwo zaimplementować za pomocą struktury i rozszerzając go dla każdej innej klasy potomnej. Prosty rzut na strukturę nadrzędną umożliwia stosowanie metod nadrzędnych na wszystkich potomkach. Tak długo, jak wiesz, że zmienna wskazuje na strukturę zawierającą tego rodzaju obiekt, zawsze możesz rzutować na klasę root i przeprowadzać introspekcję.
Jak już wspomniano, metody wirtualne są nieco trudniejsze. Ale są wykonalne. Aby uprościć sprawę, po prostu korzystam z tablicy funkcji w strukturze opisu klasy, którą każda klasa potomna kopiuje i zapełnia w razie potrzeby pojedyncze pola.
Wielokrotne dziedziczenie byłoby raczej skomplikowane do wdrożenia i ma znaczący wpływ na wydajność. Więc zostawiam to. Uważam, że w wielu przypadkach pożądane i użyteczne jest czyste modelowanie rzeczywistych okoliczności życiowych, ale prawdopodobnie w 90% przypadków pojedyncze dziedzictwo pokrywa potrzeby. Pojedyncze dziedzictwo jest proste i nic nie kosztuje.
Nie dbam też o bezpieczeństwo typu. Myślę, że nie powinieneś polegać na kompilatorze, aby zapobiec błędom programistycznym. I w każdym razie chroni cię tylko przed niewielką częścią błędów.
Zazwyczaj w środowisku obiektowym chcesz również zaimplementować liczenie referencji, aby zautomatyzować zarządzanie pamięcią w możliwym zakresie. Dlatego umieściłem również liczbę referencji w klasie root „Object” i niektóre funkcje, które zawierają enkapsulację alokacji i dezalokacji pamięci sterty.
Wszystko to jest bardzo proste i szczupłe i daje mi niezbędne elementy OO bez zmuszania mnie do radzenia sobie z potworem, którym jest C ++. Zachowuję elastyczność pozostawania w ziemi C, co między innymi ułatwia integrację bibliotek stron trzecich.
źródło
Proponuję użyć Objective-C, który jest nadzbiorem C.
Chociaż Objective-C ma 30 lat, pozwala pisać elegancki kod.
http://en.wikipedia.org/wiki/Objective-C
źródło
Tak, ale nigdy nie widziałem, aby ktokolwiek próbował wdrożyć jakikolwiek polimorfizm za pomocą C.
źródło