Myślę, że C jest w porządku i przyzwoity do wdrażania koncepcji obiektowych, wzruszając ramionami . Większość różnic, jakie widzę między podzbiorem wspólnych mianowników języków uważanych za obiektowe, są niewielkie i mają charakter składniowy z mojego rodzaju pragmatycznego punktu widzenia.
Zacznijmy od, powiedzmy, ukrywania informacji. W C możemy to osiągnąć po prostu ukrywając definicję struktury i pracując z nią przez nieprzejrzyste wskaźniki. Który skutecznie modeluje public
vs. private
rozróżnienie pól danych, ponieważ mamy z klas. Jest to dość łatwe i mało antyidiomatyczne, ponieważ standardowa biblioteka C polega na tym w znacznej mierze w celu ukrycia informacji.
Oczywiście tracisz możliwość łatwego kontrolowania, gdzie struktura jest przydzielona w pamięci za pomocą nieprzezroczystych typów, ale to tylko godna uwagi różnica między, powiedzmy, C i C ++. C ++ jest zdecydowanie lepszym narzędziem do porównywania jego zdolności do programowania obiektowych pojęć nad C przy jednoczesnym zachowaniu kontroli nad układami pamięci, ale to niekoniecznie oznacza, że Java lub C # jest lepszy od C pod tym względem, ponieważ te dwa sprawiają, że jesteś całkowicie tracą możliwość kontrolowania, gdzie obiekty są przydzielane w pamięci.
I musimy użyć składni takiej fopen(file, ...); fclose(file);
jak w przeciwieństwie do file.open(...); file.close();
dużego whoop. Kogo to obchodzi? Może po prostu ktoś, kto mocno opiera się na autouzupełnianiu w swoim IDE. Przyznaję, że może to być bardzo przydatna funkcja z praktycznego punktu widzenia, ale może nie taka, która wymaga dyskusji na temat tego, czy język jest odpowiedni dla OOP.
Brakuje nam możliwości skutecznego wdrażania protected
pól. Całkowicie się tam złożę. Ale nie sądzę, aby istniała konkretna zasada, która mówi: „ Wszystkie języki OO powinny mieć funkcję umożliwiającą dostęp do podklas członkom klasy podstawowej, do których normalni klienci nadal nie powinni mieć dostępu ”. Poza tym rzadko widuję przypadki użycia dla chronionych członków, którzy nie są co najmniej trochę podejrzliwi, aby stać się przeszkodą w utrzymaniu.
I oczywiście mamy do „emulować” oo polimorfizmu z tabelami wskaźników funkcji i wskaźniki do nich do dynamicznego wysłania trochę więcej boilerplate zainicjować te analogiczna vtables
a vptrs
, ale trochę boilerplate nigdy nie sprawiało mi wiele smutku.
Dziedziczenie jest bardzo podobne. Możemy łatwo modelować to poprzez kompozycję, a przy wewnętrznym działaniu kompilatorów sprowadza się to do tego samego. Oczywiście tracimy bezpieczeństwo typu, jeśli chcemy obniżyć , i powiedziałbym, że jeśli chcesz być w ogóle obniżony , nie używaj do tego C, ponieważ rzeczy, które ludzie robią w C, aby emulować obniżanie, mogą być przerażające z punktu widzenia bezpieczeństwa, ale wolałbym, żeby ludzie wcale nie byli przygnębieni . Bezpieczeństwo typu jest czymś, czego łatwo można przeoczyć w C, ponieważ kompilator zapewnia tyle swobody interpretacji rzeczy jak tylko bity i bajty, poświęcając zdolność do wychwytywania potencjalnych błędów w czasie kompilacji, ale niektóre języki uważane za obiektowe nie są są nawet wpisywane statycznie.
Więc nie, myślę, że jest w porządku. Oczywiście nie użyłbym C, aby stworzyć bazę kodu na dużą skalę, która byłaby zgodna z zasadami SOLID, ale niekoniecznie z powodu jej niedociągnięć w froncie zorientowanym obiektowo. Wiele funkcji, których brakowałbym, gdybym próbował użyć C do takiego celu, byłyby związane z funkcjami języka, które nie są bezpośrednio uważane za warunek wstępny dla OOP, takie jak silne bezpieczeństwo typu, destruktory, które są automatycznie wywoływane, gdy obiekty wykraczają poza zakres, operator przeciążenie, szablony / ogólne i obsługa wyjątków. Właśnie wtedy brakuje mi dodatkowych funkcji, które sięgam po C ++.