Niektóre funkcje językowe, nawet jeśli są dodatkami, mogą zmienić cały sposób, w jaki język musi być praktycznie używany. Jako jeden przykład rozważ ten przypadek:
lock_mutex(&mutex);
// call some functions
...
unlock_mutex(&mutex);
Jeśli powyższy kod wymagałby wywoływania funkcji zaimplementowanych w C ++, moglibyśmy mieć kłopoty, ponieważ każde z tych wywołań funkcji może rzucić i nigdy nie odblokujemy muteksu w tych wyjątkowych ścieżkach.
Niszczyciele nie są już wygodniejsze, aby pomóc programistom uniknąć zapomnienia o uwolnieniu / uwolnieniu zasobów w tym momencie. RAII staje się wymogiem praktycznym, ponieważ nie jest możliwe z ludzkiego punktu widzenia przewidywanie każdego wiersza kodu, który może generować nietrywialne przykłady (nie wspominając, że te linie nie mogą teraz rzucać, ale mogą później ze zmianami). Weź inny przykład:
void f(const Foo* f1)
{
Foo f2;
memcpy(&f2, f1, sizeof f2);
...
}
Taki kod, choć ogólnie nieszkodliwy w C, jest jak spustoszenie piekła piekielnego w C ++, ponieważ memcpy
buldożery przecinają bity i bajty tych obiektów i omijają rzeczy takie jak konstruktory kopii. Funkcje takie jak memset
, realloc
, memcpy
itp, podczas codziennych narzędzi wśród programistów C używany do patrzenia na rzeczy w dość jednorodnej formie bitów i bajtów w pamięci, nie są harmonijne z bardziej złożonych i bogatszych systemów typu C ++. C ++ zachęca do bardziej abstrakcyjnego widzenia typów zdefiniowanych przez użytkownika.
Zatem tego typu rzeczy nie pozwalają już C ++, dla każdego, kto chce używać go poprawnie, traktować go jako zwykły „nadzbiór” C. Języki te wymagają zupełnie innego sposobu myślenia, dyscypliny i sposobu myślenia, aby używać najbardziej efektywnie .
Nie jestem w obozie, który uważa C ++ za zdecydowanie lepszy pod każdym względem, a właściwie większość moich ulubionych bibliotek stron trzecich to biblioteki C z jakiegoś powodu. Nie wiem, dlaczego dokładnie, ale biblioteki C mają z natury bardziej minimalistyczny charakter (być może dlatego, że brak tak bogatego systemu typów sprawia, że programiści bardziej skupiają się na zapewnieniu minimalnej wymaganej funkcjonalności bez budowania dużego i warstwowego zestawu abstrakcji), chociaż często po prostu umieszczam wokół nich owijarki C ++, aby uprościć i dostosować ich użycie do moich celów, ale ta minimalistyczna natura jest dla mnie lepsza, nawet gdy to robię. Naprawdę uwielbiam minimalizm jako atrakcyjną cechę biblioteki dla tych, którzy poświęcają dodatkowy czas na poszukiwanie takich cech, i być może C zazwyczaj to zachęca,
Preferuję C ++ znacznie częściej niż nie, ale tak naprawdę muszę raczej używać C API dla najszerszej kompatybilności binarnej (i dla FFI), chociaż często implementuję je w C ++, mimo że używam C do nagłówków. Ale czasami, gdy wchodzisz na naprawdę niski poziom, na przykład poziom alokatora pamięci lub bardzo niskopoziomową strukturę danych (i jestem pewien, że istnieją dalsze przykłady wśród tych, którzy wykonują programowanie osadzone), czasem pomocne może być możemy założyć, że typy i dane, z którymi pracujesz, są nieobecne w niektórych funkcjach, takich jak vtables, costructors i destructors, dzięki czemu możemy traktować je jako bity i bajty, które można przetasować, skopiować, zwolnić, przenieść ponownie. W przypadku problemów szczególnie niskiego poziomu czasem pomocna może być praca z dużo prostszym systemem typów, który zapewnia C,
Wyjaśnienie
Jeden ciekawy komentarz tutaj chciałem odpowiedzieć na trochę bardziej dogłębnie (uważam, że komentarze tutaj są tak surowe w odniesieniu do limitu znaków):
memcpy(&f2, f1, sizeof f2);
jest także „spustoszeniem piekielnego ognia” w C, jeśli Foo ma jakieś wskazówki, lub jest jeszcze gorszy, ponieważ brakuje ci też narzędzi, aby sobie z tym poradzić.
To słuszna kwestia, ale wszystko, na czym się skupiam, skupia się głównie na systemie typów C ++, a także w odniesieniu do RAII. Jednym z powodów, dla których takie rentgenowskie kopiowanie bajtów memcpy
lub qsort
rodzaje funkcji stanowią mniej praktyczne zagrożenie w C, jest to, że zniszczenie f1
i f2
powyżej jest jawne (jeśli nawet potrzebują one nietrywialnego zniszczenia), podczas gdy gdy niszczyciele przenoszą się na obraz stają się niejawne i zautomatyzowane (często o dużej wartości dla programistów). Nie wspominając nawet o stanie ukrytym, takim jak vptrs i tak dalej, które takie funkcje zostałyby natychmiast usunięte. Jeśli f1
posiada wskaźniki if2
płytkie kopiuje je w jakimś tymczasowym kontekście, to nie stanowi problemu, jeśli nie spróbujemy wyraźnie uwolnić posiadaczy wskaźników po raz drugi. W przypadku C ++ kompilator będzie chciał to zrobić automatycznie.
A staje się to tym większe, że zwykle w języku C, „ Jeśli Foo ma własne wskaźniki”, ponieważ jawność wymagana przy zarządzaniu zasobami często sprawia, że coś zwykle trudniej przeoczyć, podczas gdy w C ++ możemy uczynić UDT nie trywialnym konstruowalny / destrukowalny poprzez zwykłe przechowywanie w nim dowolnej zmiennej składowej, która nie jest trywialnie konstruowalna / destrukowalna (w sposób, który jest na ogół bardzo pomocny, znowu, ale nie, jeśli mamy ochotę używać funkcji takich jak memcpy
lub realloc
).
Moim głównym celem nie jest próba argumentowania jakiejkolwiek korzyści z tej jawności (powiedziałbym, że jeśli istnieją, prawie zawsze są one obciążone wadami zwiększonego prawdopodobieństwa błędu ludzkiego, który się z tym wiąże), a jedynie stwierdzenie, że funkcje jak memcpy
i memmove
i qsort
i memset
irealloc
i tak dalej nie ma miejsca w języku z UDT tak bogatym w funkcje i możliwości jak C ++. Chociaż istnieją niezależnie, myślę, że nie byłoby zbyt trudne twierdzenie, że ogromna większość programistów C ++ rozsądnie byłoby unikać takich funkcji, jak zaraza, podczas gdy są to bardzo codzienne funkcje w C, a ja ' d twierdzą, że stwarzają mniej problemów w C z tego prostego powodu, że jego system typów jest o wiele bardziej podstawowy i, być może, „głupszy”. Prześwietlanie typów C i traktowanie ich jako bitów i bajtów jest podatne na błędy. Robienie tego w C ++ jest prawdopodobnie całkowicie błędne, ponieważ takie funkcje walczą z bardzo podstawowymi cechami języka i tym, co zachęca system czcionek.
Jest to w rzeczywistości największy apel do mnie ze strony C, szczególnie w odniesieniu do tego, w jaki sposób odnosi się do interoperacyjności językowej. O wiele, wiele trudniej byłoby sprawić, aby FFI C # zrozumiał w pełni funkcjonalny system typów i funkcje językowe C ++ aż do konstruktorów, destruktorów, wyjątków, funkcji wirtualnych, przeciążenia funkcji / metody, przeciążenia operatora, wszystkich różnych typów dziedziczenie itp. W przypadku C jest to stosunkowo głupszy język, który stał się raczej standardowy, jeśli chodzi o interfejsy API, ponieważ wiele różnych języków może importować bezpośrednio przez FFI lub pośrednio przez niektóre funkcje eksportujące API C w pożądanej formie (np. Java Native Interface ). I właśnie tam w większości nie mam wyboru, jak korzystać z C, ponieważ interoperacyjność tego języka jest w naszym przypadku praktycznym wymogiem (choć często „
Ale wiesz, jestem pragmatykiem (a przynajmniej staram się być). Jeśli C byłby tym najbardziej plugawym i cholernym, podatnym na błędy, nieprzyjemnym językiem, niektórzy z moich entuzjastów C ++ twierdzili, że jest (i uważałbym się za entuzjastę C ++, z wyjątkiem tego, że jakoś nie doprowadziło to do nienawiści do C z mojej strony ; wręcz przeciwnie, wywarło na mnie odwrotny skutek, sprawiając, że lepiej doceniam oba języki pod ich względami i różnicami), wtedy spodziewałbym się, że pojawią się w realnym świecie w postaci niektórych z najbardziej wadliwych i nieszczelnych i niewiarygodne produkty i biblioteki pisane w C. I tego nie znajduję. Lubię Linuksa, lubię Apache, Lua, Zlib, uważam, że OpenGL jest tolerowany ze względu na długą tradycję w stosunku do takich zmieniających się wymagań sprzętowych, Gimp, Libpng, Kair itp. Przynajmniej jakiekolwiek przeszkody, jakie stawia język, nie wydają się stanowić impasu, jeśli chodzi o pisanie fajnych bibliotek i produktów we właściwych rękach, i to naprawdę wszystko, czym jestem zainteresowany. Więc nigdy nie byłem typem tak zainteresowanym najbardziej namiętnymi wojny językowe, z wyjątkiem pragmatycznego odwołania się i powiedzenia: „Hej, są tam fajne rzeczy! Nauczmy się, jak to zrobili i może są fajne lekcje, nie tak specyficzne dla idiomatycznej natury języka, które możemy przywrócić na dowolny używany przez nas język. ” :-RE