„Jeśli naprawdę chcesz cukru OO - idź użyć C ++” - to była natychmiastowa odpowiedź, którą otrzymałem od jednego z moich znajomych, kiedy o to zapytałem. Wiem, że dwie rzeczy są tutaj zupełnie nie tak. Pierwszy OO NIE jest „cukrem”, a po drugie C ++ NIE wchłonął C.
Musimy napisać serwer w C (front-end, do którego będzie w Pythonie), dlatego badam lepsze sposoby zarządzania dużymi programami C.
Modelowanie dużego systemu pod kątem obiektów i interakcji między obiektami sprawia, że jest on łatwiejszy w zarządzaniu, utrzymywaniu i rozszerzaniu. Ale kiedy próbujesz przetłumaczyć ten model na C, który nie zawiera obiektów (i wszystkiego innego), musisz podjąć ważne decyzje.
Czy tworzysz niestandardową bibliotekę, aby zapewnić abstrakcje OO, których potrzebuje Twój system? Rzeczy takie jak obiekty, enkapsulacja, dziedziczenie, polimorfizm, wyjątki, pub / sub (zdarzenia / sygnały), przestrzenie nazw, introspekcja itp. (Na przykład GObject lub COS ).
Lub po prostu używasz podstawowych konstrukcji C ( struct
i funkcji) do aproksymacji wszystkich swoich klas obiektów (i innych abstrakcji). (na przykład niektóre odpowiedzi na to pytanie w SO )
Pierwsze podejście zapewnia ustrukturyzowany sposób implementacji całego modelu w C. Ale dodaje również warstwę złożoności, którą musisz zachować. (Pamiętaj, że złożoność była tym, co chcieliśmy zmniejszyć, używając obiektów w pierwszej kolejności).
Nie wiem o drugim podejściu i jego skuteczności w przybliżeniu wszystkich abstrakcji, których możesz potrzebować.
Tak więc moje proste pytania brzmią: jakie są najlepsze praktyki w realizacji projektowania obiektowego w C. Pamiętaj, że nie pytam JAK to zrobić. To i te pytania mówią o tym, a jest nawet książka na ten temat. Bardziej mnie interesują realistyczne porady / przykłady, które odnoszą się do prawdziwych problemów, które pojawiają się, gdy się to dzieje.
Uwaga: nie rób, dlaczego C nie powinien być używany na rzecz C ++. Minęliśmy już ten etap.
źródło
extern "C"
i mógł być używany z Pythona. Możesz to zrobić ręcznie lub możesz poprosić SWIG o pomoc. Zatem pragnienie interfejsu użytkownika w Pythonie nie jest powodem, aby nie używać C ++. Nie znaczy to, że nie ma uzasadnionych powodów, aby chcieć zostać z C.Odpowiedzi:
Od mojej odpowiedzi do: Jak powinienem tworzyć złożone projekty w C (nie OO, ale o zarządzaniu złożonością w C):
Od mojej odpowiedzi do Jakie są typowe konwencje nazewnictwa dla funkcji publicznych i prywatnych OO C (myślę, że to najlepsza praktyka):
Ponadto wiele narzędzi UML jest w stanie wygenerować kod C na podstawie diagramów UML. Wersja open source to Topcased .
źródło
Myślę, że musisz rozróżnić OO i C ++ w tej dyskusji.
Implementowanie obiektów jest możliwe w C i to całkiem proste - po prostu twórz struktury ze wskaźnikami funkcji. To jest twoje „drugie podejście” i chciałbym się z tym pogodzić. Inną opcją byłoby nie używanie wskaźników funkcji w strukturze, ale raczej przekazywanie struktury danych do funkcji w wywołaniach bezpośrednich, jako wskaźnika „kontekstowego”. To jest lepsze, IMHO, ponieważ jest bardziej czytelne, łatwiejsze do prześledzenia i umożliwia dodawanie funkcji bez zmiany struktury (łatwe w dziedziczeniu, jeśli żadne dane nie zostaną dodane). W rzeczywistości C ++ zazwyczaj implementuje
this
wskaźnik.Polimorfizm staje się bardziej skomplikowany, ponieważ nie ma wbudowanego wsparcia dziedziczenia i abstrakcji, więc musisz albo dołączyć strukturę nadrzędną do klasy dziecka, albo wykonać wiele kopii past, każda z tych opcji jest szczerze mówiąc okropna, chociaż technicznie prosty. Ogromna ilość błędów czeka na wystąpienie.
Funkcje wirtualne można łatwo osiągnąć poprzez wskaźniki funkcji wskazujące na różne funkcje zgodnie z wymaganiami, ponownie - bardzo podatne na błędy, gdy wykonuje się je ręcznie, wiele żmudnej pracy nad prawidłową inicjalizacją tych wskaźników.
Jeśli chodzi o przestrzenie nazw, wyjątki, szablony itp. - Myślę, że jeśli jesteś ograniczony do C - powinieneś po prostu zrezygnować z nich. Pracowałem nad pisaniem OO w C, nie zrobiłbym tego, gdybym miał wybór (w tym miejscu pracy dosłownie walczyłem o wprowadzenie C ++, a menedżerowie byli „zaskoczeni” tym, jak łatwo było to zintegrować z resztą modułów C na końcu.).
Nie trzeba dodawać, że jeśli możesz używać C ++ - użyj C ++. Nie ma prawdziwego powodu, aby tego nie robić.
źródło
Oto podstawy tworzenia orientacji obiektowej w C
1. Tworzenie obiektów i enkapsulacja
Zwykle tworzy się obiekt taki jak
object_instance = create_object_typex(parameter);
Metody można zdefiniować tutaj na jeden z dwóch sposobów.
Należy pamiętać, że w większości przypadków
object_instance (or object_instance_private_data)
zwracany jest typvoid *.
Aplikacja nie może odwoływać się do poszczególnych elementów ani funkcji tego.Ponadto każda metoda używa tych instancji_obiektu dla kolejnej metody.
2. Polimorfizm
Możemy użyć wielu funkcji i wskaźników funkcji, aby zastąpić niektóre funkcje w czasie wykonywania.
na przykład - wszystkie metody_obiektu są zdefiniowane jako wskaźnik funkcji, który można rozszerzyć na metody publiczne i prywatne.
Możemy również zastosować przeciążenie funkcji w ograniczonym sensie, używając sposobu,
var_args
który jest bardzo podobny do zmiennej liczby argumentów zdefiniowanych w printf. tak, nie jest to tak elastyczne w C ++ - ale jest to najbliższy sposób.3. Definiowanie dziedziczenia
Definiowanie dziedziczenia jest nieco trudne, ale można wykonać następujące czynności za pomocą struktur.
tutaj
doctor
iengineer
pochodzi od osoby i można to zrobić na wyższym poziomie, powiedzmyperson
.Najlepszy przykład tego jest używany w GObject i pochodnych obiektach z niego.
4. Tworzenie wirtualnych klas Cytuję prawdziwy przykład z biblioteki o nazwie libjpeg używanej przez wszystkie przeglądarki do dekodowania jpeg. Tworzy wirtualną klasę o nazwie error_manager, którą aplikacja może utworzyć konkretną instancję i dostarczyć z powrotem -
Zauważ, że JMETHOD rozwija się we wskaźniku funkcji przez makro, które należy załadować odpowiednio odpowiednimi metodami.
Próbowałem powiedzieć tak wiele rzeczy bez zbyt wielu indywidualnych wyjaśnień. Ale mam nadzieję, że ludzie będą mogli spróbować swoich własnych rzeczy. Jednak moim zamiarem jest tylko pokazanie, jak rzeczy się mapują.
Ponadto będzie wiele argumentów, że nie będzie to dokładnie prawdziwa właściwość odpowiednika C ++. Wiem, że OO w C-nie będzie tak ścisły w swojej definicji. Ale działając w ten sposób można zrozumieć niektóre podstawowe zasady.
Ważną rzeczą nie jest to, że OO jest tak surowe jak w C ++ i JAVA. Chodzi o to, że można strukturalnie zorganizować kod z myślą OO i obsługiwać go w ten sposób.
Zdecydowanie polecam ludziom zobaczyć prawdziwy projekt libjpeg i śledzić zasoby
za. Programowanie obiektowe w C
b. jest to dobre miejsce, w którym ludzie wymieniają się pomysłami
c. a tu jest pełna książka
źródło
Orientacja obiektowa sprowadza się do trzech rzeczy:
1) Modułowy projekt programu z autonomicznymi klasami.
2) Ochrona danych z enkapsulacją prywatną.
3) Dziedziczenie / polimorfizm i różne inne pomocne składnie, takie jak konstruktory / destruktory, szablony itp.
1 jest jak dotąd najważniejszy, a także całkowicie niezależny od języka, chodzi o projektowanie programu. W C robisz to, tworząc autonomiczne „moduły kodu” składające się z jednego pliku .h i jednego pliku .c. Potraktuj to jako odpowiednik klasy OO. Możesz zdecydować, co powinno być umieszczone w tym module poprzez zdrowy rozsądek, UML lub dowolną metodę projektowania OO, której używasz dla programów C ++.
2 jest również bardzo ważne, nie tylko w celu ochrony umyślnego dostępu do prywatnych danych, ale także w celu ochrony przed niezamierzonym dostępem, tj. „Bałaganem przestrzeni nazw”. C ++ robi to w bardziej elegancki sposób niż C, ale nadal można to osiągnąć w C za pomocą słowa kluczowego static. Wszystkie zmienne, które zadeklarowałbyś jako prywatne w klasie C ++, powinny być zadeklarowane jako statyczne w C i umieszczone w zakresie plików. Są one dostępne tylko z poziomu własnego modułu kodu (klasy). Możesz pisać „setters / getters” tak jak w C ++.
3 jest pomocne, ale nie konieczne. Możesz pisać programy OO bez dziedziczenia lub bez konstruktorów / destruktorów. Te rzeczy są miłe, z pewnością mogą uczynić programy bardziej eleganckimi i być może także bezpieczniejszymi (lub odwrotnie, jeśli są używane niedbale). Ale nie są konieczne. Ponieważ C nie obsługuje żadnej z tych pomocnych funkcji, po prostu musisz się bez nich obejść. Konstruktory można zastąpić funkcjami init / destruct.
Dziedziczenia można dokonać za pomocą różnych sztuczek strukturalnych, ale odradzałbym to, ponieważ prawdopodobnie uczyni to twój program bardziej złożonym bez żadnego zysku (dziedziczenie powinno być ostrożnie stosowane, nie tylko w języku C, ale w dowolnym języku).
Wreszcie, każdą sztuczkę OO można wykonać w książce C. Axela-Tobiasa Schreinera „Programowanie obiektowe z ANSI C” z początku lat 90. to potwierdza. Jednak nie poleciłbym tej książki nikomu: dodaje nieprzyjemnej, dziwnej złożoności twoim programom C, która po prostu nie jest warta zamieszania. (Książka jest dostępna tutaj za darmo dla osób, które pomimo mojego ostrzeżenia są nadal zainteresowane).
Radzę więc zastosować 1) i 2) powyżej i pominąć resztę. Jest to sposób pisania programów w C, który od ponad 20 lat sprawdza się.
źródło
Pożyczanie doświadczenia z różnych środowisk uruchomieniowych Objective-C, pisanie dynamicznej, polimorficznej funkcji OO w C nie jest zbyt trudne (z drugiej strony wydaje się, że jest nadal w użyciu po 25 latach). Jeśli jednak zaimplementujesz funkcję obiektu w stylu Objective-C bez rozszerzania składni języka, kod, z którym się skończysz, jest dość niechlujny:
objc_msgSend(object, selector, …)
. Wiedząc, jakiej klasy jest obiekt, może znaleźć implementację pasującą do selektora, a tym samym wykonać poprawną funkcję.To wszystko jest częścią biblioteki OO ogólnego przeznaczenia zaprojektowanej, aby umożliwić wielu programistom wzajemne korzystanie i wzajemne rozszerzanie klas, więc może to być przesada w twoim własnym projekcie. Często projektowałem projekty C jako „statyczne” zorientowane na klasy projekty wykorzystujące struktury i funkcje: - każda klasa jest definicją struktury C określającej układ ivar - każda instancja jest tylko instancją odpowiedniej struktury - obiektów nie można „messaged”, ale
MyClass_doSomething(struct MyClass *object, …)
zdefiniowane są funkcje podobne do metod . To sprawia, że w kodzie jest bardziej przejrzyste niż podejście ObjC, ale ma mniejszą elastyczność.Miejsce, w którym kłamstwa zależą od twojego projektu: brzmi to tak, jakby inni programiści nie używali interfejsów C, więc wybór sprowadza się do wewnętrznych preferencji. Oczywiście, jeśli zdecydujesz, że chcesz coś w rodzaju biblioteki wykonawczej objc, wówczas będą dostępne międzyplatformowe biblioteki wykonawcze objc.
źródło
GObject tak naprawdę nie kryje żadnej złożoności i wprowadza własną złożoność. Powiedziałbym, że ad-hoc jest łatwiejszy niż GObject, chyba że potrzebujesz zaawansowanych rzeczy GObject, takich jak sygnały lub maszyny interfejsu.
Sprawa jest nieco inna w przypadku COS, ponieważ zawiera preprocesor, który rozszerza składnię C o niektóre konstrukcje OO. Istnieje podobny preprocesor dla GObject, G Object Builder .
Możesz także wypróbować język programowania Vala , który jest pełnym językiem wysokiego poziomu, który kompiluje się do C oraz w sposób umożliwiający korzystanie z bibliotek Vala ze zwykłego kodu C. Może używać GObject, własnej struktury obiektowej lub metody ad-hoc (z ograniczonymi funkcjami).
źródło
Po pierwsze, chyba że jest to zadanie domowe lub nie ma kompilatora C ++ dla urządzenia docelowego. Nie sądzę, że musisz używać C, możesz dość łatwo zapewnić interfejs C z łączeniem C z C ++.
Po drugie, przyjrzałbym się, jak bardzo będziesz polegać na polimorfizmie i wyjątkach, a także na innych funkcjach zapewnianych przez frameworki, jeśli nie będzie to zbyt proste struktury z powiązanymi funkcjami, będą znacznie łatwiejsze niż w pełni funkcjonalne frameworki, jeśli znaczna część twojego projekt potrzebuje ich, a następnie ugryź pocisk i skorzystaj z frameworka, abyś nie musiał sam implementować tych funkcji.
Jeśli tak naprawdę nie masz jeszcze projektu do podjęcia decyzji, zrób skok i zobacz, co mówi kod.
wreszcie nie musi to być jeden lub inny wybór (chociaż jeśli od samego początku wybierasz framework, prawdopodobnie możesz go trzymać), powinno być możliwe rozpoczęcie od prostych struktur dla prostych części i dodawanie tylko w bibliotekach w razie potrzeby.
EDYCJA: Więc decyzja kierownictwa pozwala zignorować pierwszy punkt.
źródło