Wiele osób powiedziało, że C ++ jest zupełnie innym językiem niż C, ale sam Bjarne powiedział, że C ++ jest językiem rozszerzonym od C, stąd też ++
pochodzi. Dlaczego więc wszyscy mówią, że C i C ++ to zupełnie inne języki? W jaki sposób C różni się od C ++ poza rozszerzonymi funkcjami w C ++?
programming-languages
c++
c
Joshua Partogi
źródło
źródło
Odpowiedzi:
W latach 80., gdy dopiero zaczynało się tworzenie C ++, C ++ był prawie właściwym nadzbiorem C. Tak to się wszystko zaczęło.
Jednak z biegiem czasu zarówno C, jak i C ++ ewoluowały i odbiegały od siebie, mimo że zgodność między językami zawsze była uważana za ważną.
Ponadto różnice techniczne między C i C ++ sprawiły, że typowe idiomy w tych językach są jeszcze bardziej rozbieżne w tym, co uważa się za „dobrą praktykę”.
Jest to czynnik napędzający ludzi mówiących na przykład: „nie ma takiego języka jak C / C ++” lub „C i C ++ to dwa różne języki”. Chociaż możliwe jest pisanie programów, które są akceptowalne zarówno dla kompilatora C, jak i C ++, kod ogólnie nie jest uważany za przykład dobrego kodu C ani dobrego kodu C ++.
źródło
void *
ale C ++ nigdy nie miał. (Nie przegłosował)Sam Stroustrup odpowiada na to w swoim FAQ :
To wsparcie dla programowania obiektowego i programowania generycznego, że make C ++ „zupełnie inna” do C. Ty może prawie napisać czystym C, a następnie skompilować go z C ++ (tak długo, jak dbać o typie kontroli dosłownym). Ale nadal piszesz C - nie piszesz C ++.
Jeśli piszesz w C ++, to korzystasz z funkcji obiektowych i szablonów, a to nie przypomina tego, co zobaczysz w C.
źródło
Mówiąc prosto, to, co jest uważane za idiomatyczne w C, zdecydowanie nie jest idiomatyczne w C ++.
C i C ++ są w praktyce bardzo różnymi językami, ze względu na sposób ich używania. C dąży do minimalizmu, gdzie C ++ jest bardzo złożonym językiem, z wieloma funkcjami.
Istnieją także pewne praktyczne różnice: C można łatwo wywołać z dowolnego języka i często definiuje ABI platformy, podczas gdy C ++ jest dość trudny w użyciu z innych bibliotek. Większość języków ma interfejs FFI lub interfejs w C, nawet języki zaimplementowane w C ++ (na przykład java).
źródło
Oprócz oczywistego faktu, że C ++ obsługuje programowanie obiektowe, myślę, że masz tutaj swoją odpowiedź: http://en.wikipedia.org/wiki/Compatibility_of_C_and_C++
Ten artykuł zawiera przykłady kodu pokazujące rzeczy, które są w porządku w C, ale nie w C ++. Na przykład:
Przeniesienie programu C do C ++ jest często proste i polega głównie na naprawie błędów kompilacji (dodawanie rzutowań, nowych słów kluczowych itp.).
źródło
C ++ dodaje nie tylko nowe funkcje, ale także nowe koncepcje i nowe idiomy do C. Mimo że C ++ i C są ze sobą blisko powiązane, faktem jest, że aby pisać skutecznie w języku, musisz myśleć w stylu tego języka. Nawet najlepszy kod C nie może korzystać z różnych mocnych stron i idiomów języka C ++, dlatego jest bardziej prawdopodobne niż zły kod C ++.
źródło
„Rozszerzone funkcje” sprawiają, że brzmi to tak, jak w C ++, które dodali, variadic macros lub coś w tym stylu. „Rozszerzone funkcje” w C ++ są kompletnym przeglądem języka i całkowicie zastępują najlepsze praktyki C, ponieważ nowe funkcje C ++ są o wiele lepsze niż oryginalne funkcje C, że oryginalne funkcje C są całkowicie i całkowicie zbędne w zdecydowanej większości przypadków . Sugerowanie, że C ++ jedynie rozszerza C, sugeruje, że nowoczesny czołg bojowy wysuwa maślankę do celów prowadzenia wojny.
źródło
Różnica polega na tym, że w C myślisz proceduralnie, aw C ++ myślisz obiektowo. Języki są dość podobne, ale podejście jest zupełnie inne.
źródło
Podczas gdy C ++ może być super zestawem C pod względem składniowym - tzn. Dowolna konstrukcja programu C może być kompilowana przez kompilator C ++.
Jednak prawie nigdy nie piszesz programów w C ++ tak, jak zrobiłbyś to z programem C. Lista może być nieskończona lub ktoś powinien po prostu przeprowadzić więcej badań, aby przedstawić ją jako wyczerpujące raporty. Jednak zamieszczam kilka wskazówek, które powodują kluczowe różnice.
Chodzi o to, że C ++ ma następujące funkcje, których dobrzy programiści C ++ muszą używać jako najlepszych praktyk programistycznych, nawet jeśli kompilacja C jest możliwa.
Jak należy to zrobić w C ++ w stosunku do C
Klasy i dziedziczenie. To najważniejsze różnice, które pozwalają na systematyczną orientację obiektową, dzięki czemu ekspresja programowania jest bardzo silna. Chyba - ten punkt nie wymaga lepszego wyjaśnienia. Jeśli jesteś w C ++ - prawie zawsze lepiej jest korzystać z klas.
Prywatyzacja - klasy, a nawet struktury mają członków prywatnych. Umożliwia to enkapsulację klasy. Odpowiednikiem w C jest rzutowanie obiektu jako void * do aplikacji, aby aplikacja nie miała dostępu do zmiennych wewnętrznych. Jednak w C ++ możesz mieć elementy z klasami publicznymi i prywatnymi.
Przekaż przez odniesienie. C ++ pozwala na modyfikację w oparciu o referencję, dla której wymagane są przekazywanie wskaźników. Przekazywanie przez odniesienie utrzymuje kod bardzo czysty i bardziej bezpieczny przed zagrożeniami związanymi ze wskaźnikiem. Masz równie dobrze wskaźnik w stylu C i to działa - ale jeśli jesteś w C ++, to lepiej, jeśli tylko
nowe i usuń vs. malloc i za darmo. Instrukcje new () i delete () nie tylko przydziału i cofnięcia przydziału pamięci, ale także pozwalają na wykonywanie kodu w ramach funkcji destruktora w celu wywołania w łańcuchu. Jeśli używasz C ++ - w rzeczywistości BAD jest używanie malloc i za darmo.
Typy IO i przeciążenie operatora Przeciążenie operatora sprawia, że kod jest czytelny lub bardziej intuicyjny, jeśli jest dobrze wykonany. To samo dotyczy operatorów << i >> io. Sposobem na wykonanie tego w C byłoby użycie wskaźników funkcji - ale jest to bałagan i tylko dla zaawansowanych programistów.
Używanie „łańcucha”. Char * z C działa wszędzie. Więc C i C ++ są prawie takie same. Jednakże, jeśli jesteś w C ++ - zawsze jest o wiele lepiej (i bezpieczniej) używać klas String, które oszczędzają Ci niebezpieczeństw związanych z uruchomieniem tablic, które są prawie wszystkim.
Funkcje Wciąż nie byłbym fanem w C ++ 1. Szablony - Chociaż nie używam ciężkich szablonów w wielu kodach - może okazać się bardzo wydajny dla bibliotek. Prawie nie ma odpowiednika w C. Ale w normalny dzień - szczególnie jeśli robisz matematyczne braki.
Rzeczy, które lubię w C i których brakuje w C ++
Algorytmy polimorficzne przy użyciu wskaźników funkcji. W C, gdy uruchamiasz złożone algorytmy - czasami możesz użyć zestawu wskaźników funkcji. To czyni prawdziwy polimorfizm w potężny sposób. Kiedy jesteś w C ++ masz CAN użyciu wskaźników funkcji - ale to jest złe. Powinieneś używać tylko metod - w przeciwnym razie bądź przygotowany na bałagan. Jedyną formą polimorfizmu w klasach C ++ jest przeciążenie funkcji i operatora, ale jest to dość ograniczone.
Proste wątki. Podczas tworzenia wątków były wątki - jest to dość proste i łatwe do zarządzania. Dzieje się tak, gdy trzeba tworzyć wątki, które mają być „prywatne” dla klas (aby miały dostęp do prywatnych członków). Istnieją frameworki typu boost - ale nic w podstawowym C ++.
Dipan.
źródło
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:
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:
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 jakmemset
,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):
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
lubqsort
rodzaje funkcji stanowią mniej praktyczne zagrożenie w C, jest to, że zniszczenief1
if2
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ślif1
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
lubrealloc
).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
imemmove
iqsort
imemset
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
źródło
memcpy(&f2, f1, sizeof f2);
jest także „spustoszeniem piekielnego ognia” w C, jeśliFoo
ma jakieś wskaźniki własności, lub jest jeszcze gorszy, ponieważ brakuje ci też narzędzi, aby sobie z tym poradzić. Więc ludzie piszący C nie robią takich rzeczy tak bardzoFoo
instancje mogą teraz chcieć zniszczyć tablicę dynamiczną, wektor lub coś w tym celu. Często zdarza się, że w niektórych szczególnych przypadkach pomocne jest pisanie pewnych struktur danych, aby oprzeć się na fakcie, że zniszczenie jest jawne, a nie ukryte.Foo
ma jakieś własne wskaźniki, moglibyśmy nadać mu poprawne kopiowanie / przenoszenie ctor, dtor, stosowanie semantyki wartości itd., I mieć znacznie bogatszy i bezpieczniejszy kod. Ale czasami zdarza mi się sięgać po C w przypadkach, gdy chcę uogólnić strukturę danych lub alokator na jednorodnym poziomie bitów i bajtów, z pewnymi założeniami, które mogę poczynić na temat typów, które w swoich wąskich przypadkach użycia są uproszczone trochę przy takich założeniach czuję się bardziej pewnie w CAPI void filter_image(byte* pixels, int w, int h);
prosty przykład w przeciwieństwie do podobnegoAPI void filter_image(ImageInterface& img);
(który łączyłby nasz kod z takim interfejsem obrazu, zawężenie jego zastosowania). W takich przypadkach czasami po prostu implementuję takie funkcje w C, ponieważ w C ++ niewiele można zyskać, a to zmniejsza prawdopodobieństwo, że taki kod będzie wymagał przyszłych zmian.