C++ - C++

C++
Logo ISO C++.svg
Logo zatwierdzone przez Standard C++
Paradygmaty Wieloparadygmat : proceduralny , funkcjonalny , obiektowy , generyczny , modułowy
Rodzina C
Zaprojektowany przez Bjarne Stroustrup
Deweloper ISO/IEC JTC1 (Wspólny Komitet Techniczny 1) / SC22 (Podkomitet 22) / WG21 (Grupa Robocza 21)
Po raz pierwszy pojawiły się 1985 ; 36 lat temu ( 1985 )
Wersja stabilna
C++20 (ISO/IEC 14882:2020) / 15 grudnia 2020 ; 9 miesięcy temu ( 15.12.2020 )
Wersja zapoznawcza
C++23 / 18 czerwca 2021 ; 3 miesiące temu ( 18.06.2021 )
Dyscyplina pisania Statyczny , mianownik , częściowo wywnioskowany
OS Wieloplatformowy
Rozszerzenia nazw plików .C, .cc, .cpp, .cxx, .c++ , .h, .H, .hh, .hpp, .hxx, .h++
Strona internetowa isocpp .org
Główne wdrożenia
GCC , LLVM Clang , Microsoft Visual C++ , Embarcadero C++Builder , Kompilator Intel C++ , IBM XL C++ , EDG
Wpływem
Ada , ALGOL 68 , C , CLU , ML , Mesa , Modula-2 , Simula , Smalltalk
Pod wpływem
Ada 95 , C# , C99 , Chapel , Clojure , D , Java , JS++ , Lua , Nim , Objective-C++ , Perl , PHP , Python , Rust , Seed7

C ++ ( / ° s ı ° p l ʌ s s l ʌ s / ) jest język programowania ogólnego przeznaczenia utworzony przez Bjarne Stroustrup jako przedłużenie języku programowania C lub „C z klas ”. Język znacznie się rozwinął z biegiem czasu, a współczesny C++ ma teraz funkcje zorientowane obiektowo , ogólne i funkcjonalne, oprócz udogodnień do manipulacji pamięcią niskiego poziomu . Jest prawie zawsze implementowany jako język skompilowany , a wielu dostawców dostarcza kompilatory C++ , w tym Free Software Foundation , LLVM , Microsoft , Intel , Oracle i IBM , dzięki czemu jest dostępny na wielu platformach.

C++ został zaprojektowany z myślą o programowaniu systemowym i wbudowanym oprogramowaniu o ograniczonych zasobach i dużych systemach, z wydajnością , wydajnością i elastycznością użytkowania, które podkreślają jego projekt. C++ został również uznany za przydatny w wielu innych kontekstach, a jego kluczowymi atutami są infrastruktura oprogramowania i aplikacje o ograniczonych zasobach, w tym aplikacje komputerowe , gry wideo , serwery (np. e-commerce , wyszukiwarka internetowa lub bazy danych ) oraz aplikacje krytyczne dla wydajności ( np. przełączniki telefoniczne lub sondy kosmiczne ).

C++ jest standaryzowany przez Międzynarodową Organizację Normalizacyjną (ISO), a najnowsza wersja standardu ratyfikowana i opublikowana przez ISO w grudniu 2020 r. jako ISO/IEC 14882:2020 (nieformalnie znana jako C++20 ). Język programowania C++ został początkowo ustandaryzowany w 1998 roku jako ISO/IEC 14882:1998 , który został następnie zmieniony przez standardy C++03 , C++11 , C++14 i C++17 . Obecny standard C++20 zastępuje je nowymi funkcjami i powiększoną biblioteką standardową . Przed wstępną standaryzacją w 1998, C++ został opracowany przez duńskiego informatyka Bjarne Stroustrupa w Bell Labs od 1979 jako rozszerzenie języka C ; chciał wydajnego i elastycznego języka podobnego do C, który zapewniałby również funkcje na wysokim poziomie dla organizacji programu. Od 2012 roku C++ ma trzyletni harmonogram wydań z C++23 jako kolejnym planowanym standardem.

Historia

Bjarne Stroustrup, twórca C++, w swoim biurze AT&T New Jersey 2000

W 1979 roku duński informatyk Bjarne Stroustrup rozpoczął prace nad „C with Classes ”, poprzednik C ++. Motywacja do stworzenia nowego języka wywodziła się z doświadczenia Stroustrupa w programowaniu jego pracy doktorskiej. Stroustrup odkrył, że Simula ma funkcje, które były bardzo pomocne przy tworzeniu dużego oprogramowania, ale język był zbyt wolny dla praktyczne zastosowanie, podczas gdy BCPL był szybki, ale zbyt niski, aby nadawał się do rozwoju dużego oprogramowania.Kiedy Stroustrup zaczął pracować w AT&T Bell Labs , miał problem z analizą jądra UNIX pod kątem przetwarzania rozproszonego.Pamiętając swoje doświadczenie doktorskie, Stroustrup określone w celu zwiększenia C język z Simula -Jak funkcji. C został wybrany, ponieważ był ogólnego przeznaczenia, szybki, przenośny i szeroko stosowane., a także C i Simula'S wpływów innych języków również wpływ tego nowego języka, w tym Algol 68 , Ada , CLU i ML .

Początkowo Stroustrupa za „C z klasami” dodatkowe funkcje do kompilatora C, cpre, w tym klas , klas pochodnych , silnego typowania , inline i domyślne argumenty .

Quiz o funkcjach C++11, który zostanie wygłoszony w Paryżu w 2015 r.

W 1982 roku Stroustrup zaczął opracowywać następcę C z klasami, który nazwał „C++” ( będący operatorem przyrostu w C) po przejściu kilku innych nazw. Dodano nowe funkcje, w tym funkcje wirtualne , przeciążanie nazw funkcji i operatorów , referencje , stałe, alokację pamięci z bezpiecznym typem pamięci (nowy/usuń), ulepszone sprawdzanie typu i jednowierszowe komentarze w stylu BCPL z dwoma ukośnikami ( ) . Ponadto Stroustrup opracował nowy, samodzielny kompilator dla C++, Cfront . ++//

W 1984 Stroustrup wdrożył pierwszą bibliotekę wejścia/wyjścia strumienia. Pomysł dostarczenia operatora wyjścia zamiast nazwanej funkcji wyjścia został zasugerowany przez Douga McIlroya (który wcześniej sugerował potoki uniksowe ).

W 1985 roku wydana została pierwsza edycja języka programowania C++ , która stała się ostatecznym odniesieniem dla tego języka, ponieważ nie było jeszcze oficjalnego standardu. Pierwsza komercyjna implementacja C++ została wydana w październiku tego samego roku.

W 1989 r. wydano C++ 2.0, a następnie zaktualizowano drugie wydanie Języka programowania C++ w 1991 r. Nowe funkcje w 2.0 obejmowały wielokrotne dziedziczenie, klasy abstrakcyjne, statyczne funkcje składowe , stałe funkcje składowe i chronione składowe. W 1990 roku opublikowano The Annotated C++ Reference Manual . Ta praca stała się podstawą przyszłego standardu. Późniejsze dodatki obejmowały szablony , wyjątki , przestrzenie nazw , nowe rzutowania i typ Boolean .

W 1998 roku został wydany C++98, standaryzujący język, a drobna aktualizacja ( C++03 ) została wydana w 2003 roku.

Po C++98, C++ ewoluował stosunkowo powoli, aż w 2011 roku został wydany standard C++11 , dodając wiele nowych funkcji, dalej powiększając bibliotekę standardową i zapewniając więcej udogodnień dla programistów C++. Po niewielkiej aktualizacji C++14 wydanej w grudniu 2014 r. w C++17 wprowadzono różne nowe dodatki . Po sfinalizowaniu w lutym 2020 r. projekt standardu C++20 został zatwierdzony 4 września 2020 r. i oficjalnie opublikowany 15 grudnia 2020 r.

3 stycznia 2018 r. Stroustrup został ogłoszony zdobywcą nagrody Charlesa Starka Drapera w dziedzinie inżynierii w 2018 r. „za konceptualizację i rozwój języka programowania C++”.

Od 2021 r. C++ zajmuje czwarte miejsce w indeksie TIOBE , będącym miarą popularności języków programowania, po C , Javie i Pythonie .

Etymologia

Według Stroustrupa „nazwa oznacza ewolucyjny charakter zmian z C”. To imię przypisuje się Rickowi Mascittiemu (w połowie 1983 r.) i zostało użyte po raz pierwszy w grudniu 1983 r. Kiedy Mascitti został nieformalnie przesłuchany w 1992 r. o nazewnictwo, wskazał, że zostało ono podane z przymrużeniem oka . Nazwa pochodzi z C na operatora (co zwiększa się wartość o zmiennej ) oraz wspólną konwencję nazewnictwa korzystania z „+”, aby wskazać, ulepszony program komputerowy. ++

W okresie rozwoju C++ język był określany jako „nowy C” i „C z klasami”, zanim uzyskał swoją ostateczną nazwę.

Filozofia

Przez całe życie C++ jego rozwojem i ewolucją kierował zestaw zasad:

  • Musi być napędzany rzeczywistymi problemami, a jego funkcje powinny być natychmiast przydatne w rzeczywistych programach.
  • Każda funkcja powinna być możliwa do zaimplementowania (w dość oczywisty sposób).
  • Programiści powinni mieć swobodę wyboru własnego stylu programowania, a styl ten powinien być w pełni obsługiwany przez C++.
  • Zezwolenie na użyteczną funkcję jest ważniejsze niż zapobieganie wszelkim możliwym nadużyciom C++.
  • Powinna umożliwiać organizowanie programów w oddzielne, dobrze zdefiniowane części oraz umożliwiać łączenie oddzielnie opracowanych części.
  • Brak niejawnych naruszeń systemu typów (ale zezwalaj na jawne naruszenia; to znaczy te, o które wyraźnie prosi programista).
  • Typy tworzone przez użytkowników muszą mieć taką samą obsługę i wydajność jak typy wbudowane.
  • Nieużywane funkcje nie powinny negatywnie wpływać na tworzone pliki wykonywalne (np. przy niższej wydajności).
  • Nie powinno być żadnego języka poniżej C++ (z wyjątkiem języka asemblera ).
  • C++ powinien współpracować z innymi istniejącymi językami programowania , zamiast wspierać własne, oddzielne i niekompatybilne środowisko programistyczne .
  • Jeśli intencja programisty jest nieznana, pozwól programiście określić ją, zapewniając sterowanie ręczne.

Normalizacja

Scena podczas spotkania Komitetu Standardów C++ w Sztokholmie w 1996 roku
Standardy C++
Rok Standardowy C++ Nieformalna nazwa
1998 ISO/IEC 14882:1998 C++98
2003 ISO/IEC 14882:2003 C++03
2011 ISO/IEC 14882:2011 C++11 , C++0x
2014 ISO/IEC 14882:2014 C++14 , C++1y
2017 ISO/IEC 14882:2017 C++17 , C++1z
2020 ISO/IEC 14882:2020 C++20 , C++2a

C++ jest standaryzowany przez grupę roboczą ISO znaną jako JTC1/SC22/WG21 . Do tej pory opublikowała sześć wersji standardu C++ i obecnie pracuje nad kolejną wersją, C++23 .

W 1998 roku grupa robocza ISO po raz pierwszy standaryzowała język C++ jako ISO/IEC 14882:1998 , nieformalnie znany jako C++98 . W 2003 roku opublikował nową wersję standardu C++ o nazwie ISO/IEC 14882:2003 , która naprawiła problemy zidentyfikowane w C++98.

Następna poważna wersja standardu była nieformalnie określana jako „C++0x”, ale została wydana dopiero w 2011 roku. C++11 (14882:2011) zawierał wiele dodatków zarówno do języka podstawowego, jak i do standardowej biblioteki.

W 2014 roku C++14 (znany również jako C++1y) został wydany jako małe rozszerzenie do C++11 , zawierające głównie poprawki błędów i drobne ulepszenia. Procedury głosowania projektu międzynarodowego standardu ukończono w połowie sierpnia 2014 r.

Po C++14 główna wersja C++17 , nieformalnie znana jako C++1z, została ukończona przez Komitet ISO C++ w połowie lipca 2017 i została zatwierdzona i opublikowana w grudniu 2017.

W ramach procesu normalizacji ISO publikuje również raporty techniczne i specyfikacje :

  • ISO/IEC TR 18015:2006 w sprawie wykorzystania C++ w systemach wbudowanych oraz implikacji wydajności języka C++ i funkcji bibliotecznych,
  • ISO/IEC TR 19768:2007 (znany również jako C++ Technical Report 1 ) dotyczący rozszerzeń bibliotek w większości zintegrowanych z C++11 ,
  • ISO/IEC TR 29124:2010 na specjalne funkcje matematyczne, zintegrowane z C++17
  • ISO/IEC TR 24733:2011 w sprawie arytmetyki dziesiętnej zmiennoprzecinkowej ,
  • ISO/IEC TS 18822:2015 na standardowej bibliotece systemu plików, zintegrowanej z C++17
  • ISO/IEC TS 19570:2015 na równoległych wersjach algorytmów standardowej biblioteki, zintegrowanych z C++17
  • ISO/IEC TS 19841:2015 dotycząca oprogramowania pamięci transakcyjnej ,
  • ISO/IEC TS 19568:2015 w sprawie nowego zestawu rozszerzeń bibliotek, z których niektóre są już zintegrowane z C++17 ,
  • ISO/IEC TS 19217:2015 na temat koncepcji C++ , zintegrowany z C++20
  • ISO / IEC TS 19571: 2016 na rozszerzenia bibliotek dla współbieżności, z których niektóre są już zintegrowane z C++20
  • ISO / IEC TS 19568: 2017 w sprawie nowego zestawu rozszerzeń bibliotek ogólnego przeznaczenia
  • ISO/IEC TS 21425:2017 na rozszerzenia biblioteki dla zakresów, zintegrowane z C++20
  • ISO / IEC TS 22277: 2017 na temat współprogramów, zintegrowany z C++20
  • ISO/IEC TS 19216:2018 w bibliotece sieciowej
  • ISO/IEC TS 21544:2018 na modułach, zintegrowany z C++20
  • ISO/IEC TS 19570:2018 w sprawie nowego zestawu rozszerzeń bibliotek dla równoległości

Więcej specyfikacji technicznych jest w trakcie opracowywania i oczekuje na zatwierdzenie, w tym odbicia statyczne.

Język

Język C++ składa się z dwóch głównych komponentów: bezpośredniego mapowania funkcji sprzętowych dostarczanych głównie przez podzbiór C oraz abstrakcji o zerowych narzutach opartych na tych mapowaniach. Stroustrup opisuje C ++ jako „lekki abstrakcyjny język programowania [zaprojektowany] do budowania i używania wydajnych i eleganckich abstrakcji”; oraz „oferowanie zarówno dostępu do sprzętu, jak i abstrakcji to podstawa C++. Efektywne wykonywanie tego jest tym, co odróżnia go od innych języków”.

Dziedziczy większość C ++ składni C za . Poniżej znajduje się wersja programu Hello World Bjarne'a Stroustrupa, która używa funkcji strumienia C++ Standard Library do pisania wiadomości na standardowe wyjście :

#include <iostream>

int main()
{
    std::cout << "Hello, world!\n";
}

Przechowywanie obiektów

Podobnie jak w C, C++ obsługuje cztery typy zarządzania pamięcią : statyczne obiekty czasu trwania pamięci, obiekty czasu trwania pamięci wątków, automatyczne obiekty czasu trwania pamięci i dynamiczne obiekty czasu trwania pamięci.

Statyczne obiekty czasu trwania przechowywania

Obiekty czasu trwania pamięci statycznej są tworzone przed main()wprowadzeniem (patrz wyjątki poniżej) i niszczone w odwrotnej kolejności do tworzenia po main()wyjściu. Dokładna kolejność tworzenia nie jest określona przez standard (chociaż istnieją pewne zasady zdefiniowane poniżej), aby zapewnić implementacjom pewną swobodę w organizacji ich implementacji. Bardziej formalnie, obiekty tego typu mają żywotność, która "będzie trwać przez czas trwania programu".

Obiekty czasu trwania pamięci statycznej są inicjowane w dwóch fazach. Najpierw wykonywana jest „inicjalizacja statyczna”, a dopiero po wykonaniu całej inicjalizacji statycznej wykonywana jest „inicjalizacja dynamiczna”. W inicjalizacji statycznej wszystkie obiekty są najpierw inicjowane zerami; następnie wszystkie obiekty, które mają stałą fazę inicjalizacji, są inicjowane za pomocą wyrażenia stałego (tj. zmienne inicjowane za pomocą literału lub constexpr). Chociaż nie jest to określone w standardzie, faza inicjalizacji statycznej może zostać zakończona w czasie kompilacji i zapisana w partycji danych pliku wykonywalnego. Inicjalizacja dynamiczna obejmuje całą inicjalizację obiektów za pomocą konstruktora lub wywołania funkcji (chyba że funkcja jest oznaczona constexpr, w C++11). Kolejność dynamicznej inicjalizacji jest definiowana jako kolejność deklaracji w jednostce kompilacji (tj. ten sam plik). Nie ma gwarancji co do kolejności inicjalizacji między jednostkami kompilacji.

Obiekty czasu przechowywania wątków

Zmienne tego typu są bardzo podobne do statycznych obiektów czasu trwania. Główną różnicą jest to, że czas tworzenia jest tuż przed utworzeniem wątku, a niszczenie odbywa się po połączeniu wątku.

Automatyczne obiekty czasu przechowywania

Najpopularniejszymi typami zmiennych w C++ są zmienne lokalne wewnątrz funkcji lub bloku oraz zmienne tymczasowe. Wspólną cechą zmiennych automatycznych jest to, że mają okres istnienia ograniczony do zakresu zmiennej. Są one tworzone i potencjalnie inicjowane w momencie deklaracji (szczegóły poniżej) i niszczone w odwrotnej kolejności tworzenia po opuszczeniu zakresu. Jest to realizowane przez alokację na stosie .

Zmienne lokalne są tworzone, gdy punkt wykonania mija punkt deklaracji. Jeśli zmienna ma konstruktor lub inicjator, służy to do zdefiniowania początkowego stanu obiektu. Zmienne lokalne są niszczone po zamknięciu lokalnego bloku lub funkcji, w której są zadeklarowane. Destruktory C++ dla zmiennych lokalnych są wywoływane pod koniec życia obiektu, umożliwiając dyscyplinę automatycznego zarządzania zasobami określaną jako RAII , która jest szeroko stosowana w C++.

Zmienne składowe są tworzone podczas tworzenia obiektu nadrzędnego. Elementy członkowskie tablicy są inicjowane od 0 do ostatniego elementu tablicy w kolejności. Zmienne składowe są niszczone, gdy obiekt nadrzędny jest niszczony w odwrotnej kolejności tworzenia. np. jeśli rodzic jest "obiektem automatycznym", zostanie zniszczony, gdy wyjdzie poza zakres, co spowoduje zniszczenie wszystkich jego członków.

Zmienne tymczasowe są tworzone w wyniku oceny wyrażenia i są niszczone, gdy instrukcja zawierająca wyrażenie zostanie w pełni oceniona (zwykle na ;końcu instrukcji).

Obiekty dynamicznego czasu przechowywania

Obiekty te mają dynamiczną żywotność i mogą być tworzone bezpośrednio za pomocą wywołania do i niszczone jawnie za pomocą wywołania do . C++ obsługuje również i , z C, ale nie są one kompatybilne z i . Użycie zwraca adres do przydzielonej pamięci. Wytyczne C++ Core odradzają używanie bezpośrednio do tworzenia obiektów dynamicznych na rzecz inteligentnych wskaźników poprzez pojedyncze posiadanie i wielokrotne posiadanie zliczane przez odwołania, które zostały wprowadzone w C++11. newdeletemallocfreenewdeletenewnewmake_unique<T>make_shared<T>

Szablony

Szablony C++ umożliwiają programowanie ogólne . C++ obsługuje szablony funkcji, klas, aliasów i zmiennych. Szablony mogą być parametryzowane według typów, stałych czasu kompilacji i innych szablonów. Szablony są implementowane przez tworzenie instancji w czasie kompilacji. Aby utworzyć wystąpienie szablonu, kompilatory zastępują określone argumenty parametrami szablonu, aby wygenerować konkretną instancję funkcji lub klasy. Niektóre zamiany nie są możliwe; są one eliminowane przez zasady rozwiązywania problemów z przeciążeniem opisane frazą „ Błąd podstawienia nie jest błędem ” (SFINAE). Szablony to potężne narzędzie, którego można używać do programowania ogólnego , metaprogramowania szablonów i optymalizacji kodu, ale ta moc pociąga za sobą koszty. Użycie szablonu może zwiększyć rozmiar kodu, ponieważ każde wystąpienie szablonu tworzy kopię kodu szablonu: po jednej dla każdego zestawu argumentów szablonu, jednak jest to ta sama lub mniejsza ilość kodu, który zostałby wygenerowany, gdyby kod został napisany ręcznie. Jest to w przeciwieństwie do generyków wykonawczych widzianych w innych językach (np. Java ), gdzie w czasie kompilacji typ jest wymazywany i zachowywana jest pojedyncza treść szablonu.

Szablony różnią się od makr : podczas gdy obie te funkcje języka w czasie kompilacji umożliwiają kompilację warunkową, szablony nie ograniczają się do podstawienia leksykalnego. Szablony są świadome semantyki i systemu typów języka towarzyszącego, a także wszystkich definicji typów w czasie kompilacji i mogą wykonywać operacje wysokiego poziomu, w tym programistyczne sterowanie przepływem na podstawie oceny parametrów ściśle sprawdzanych pod względem typu. Makra są zdolne do warunkowej kontroli nad kompilacją w oparciu o z góry określone kryteria, ale nie mogą tworzyć instancji nowych typów, rekursywać ani przeprowadzać oceny typu i w efekcie są ograniczone do zastępowania tekstu przed kompilacją oraz włączania/wyłączania tekstu. Innymi słowy, makra mogą sterować przepływem kompilacji w oparciu o wstępnie zdefiniowane symbole, ale nie mogą, w przeciwieństwie do szablonów, niezależnie tworzyć nowych symboli. Szablony są narzędziem do statycznego polimorfizmu (patrz poniżej) i programowania generycznego .

Ponadto szablony są mechanizmem czasu kompilacji w języku C++, który jest Turing-complete , co oznacza, że ​​wszelkie obliczenia wyrażalne przez program komputerowy mogą być obliczone w pewnej formie przez metaprogram szablonu przed środowiskiem wykonawczym.

Podsumowując, szablon to sparametryzowana w czasie kompilacji funkcja lub klasa napisana bez znajomości konkretnych argumentów użytych do jej utworzenia. Po wystąpieniu wynikowy kod jest równoważny kodowi napisanemu specjalnie dla przekazanych argumentów. W ten sposób szablony zapewniają sposób na oddzielenie ogólnych, szeroko stosowanych aspektów funkcji i klas (zakodowanych w szablonach) od określonych aspektów (zakodowanych w parametrach szablonu) bez poświęcania wydajności z powodu abstrakcji.

Obiekty

C++ wprowadza do C funkcje programowania obiektowego (OOP). Oferuje klasy , które zapewniają cztery funkcje powszechnie obecne w językach OOP (i niektórych innych niż OOP): abstrakcję , enkapsulację , dziedziczenie i polimorfizm . Cechą wyróżniającą klasy C++ w porównaniu z klasami w innych językach programowania jest obsługa destruktorów deterministycznych , które z kolei zapewniają wsparcie dla koncepcji pozyskiwania zasobów (RAII).

Kapsułkowanie

Enkapsulacja to ukrywanie informacji w celu zapewnienia, że ​​struktury danych i operatory są używane zgodnie z przeznaczeniem oraz aby model użytkowania był bardziej oczywisty dla programisty. C++ zapewnia możliwość definiowania klas i funkcji jako podstawowych mechanizmów enkapsulacji. W ramach klasy elementy członkowskie można zadeklarować jako publiczne, chronione lub prywatne, aby jawnie wymusić hermetyzację. Publiczny członek klasy jest dostępny dla dowolnej funkcji. Prywatny element członkowski jest dostępny tylko dla funkcji, które są członkami tej klasy oraz dla funkcji i klas jawnie przyznanych przez klasę („znajomi”). Chroniony element członkowski jest dostępny dla członków klas, które dziedziczą z klasy, oprócz samej klasy i wszystkich znajomych.

Zasada zorientowania obiektowego zapewnia hermetyzację wszystkich i tylko funkcji, które mają dostęp do wewnętrznej reprezentacji typu. C++ wspiera tę zasadę za pomocą funkcji członkowskich i funkcji zaprzyjaźnionych, ale jej nie wymusza. Programiści mogą deklarować części lub całą reprezentację typu jako publiczną i mogą sprawić, że encje publiczne nie będą częścią reprezentacji typu. Dlatego C++ obsługuje nie tylko programowanie obiektowe, ale także inne paradygmaty dekompozycji, takie jak programowanie modułowe .

Powszechnie uważa się, że dobrą praktyką jest uczynienie wszystkich danych prywatnymi lub chronionymi oraz upublicznienie tylko tych funkcji, które są częścią minimalnego interfejsu dla użytkowników klasy. Może to ukryć szczegóły implementacji danych, pozwalając projektantowi na późniejszą fundamentalną zmianę implementacji bez jakiejkolwiek zmiany interfejsu.

Dziedzictwo

Dziedziczenie umożliwia jednemu typowi danych uzyskanie właściwości innych typów danych. Dziedziczenie z klasy bazowej może być zadeklarowane jako publiczne, chronione lub prywatne. Ten specyfikator dostępu określa, czy niepowiązane i pochodne klasy mogą uzyskiwać dostęp do dziedziczonych publicznych i chronionych elementów członkowskich klasy bazowej. Tylko publiczne dziedziczenie odpowiada temu, co zwykle rozumie się przez „dziedziczenie”. Pozostałe dwie formy są znacznie rzadziej używane. Jeśli specyfikator dostępu zostanie pominięty, „klasa” dziedziczy prywatnie, a „struktura” dziedziczy publicznie. Klasy bazowe mogą być deklarowane jako wirtualne; nazywa się to dziedziczeniem wirtualnym . Dziedziczenie wirtualne zapewnia, że ​​na wykresie dziedziczenia istnieje tylko jedna instancja klasy bazowej, co pozwala uniknąć niektórych problemów związanych z niejednoznacznością dziedziczenia wielokrotnego.

Dziedziczenie wielokrotne to funkcja C++ umożliwiająca wyprowadzenie klasy z więcej niż jednej klasy bazowej; pozwala to na bardziej rozbudowane relacje dziedziczenia. Na przykład klasa „Latający kot” może dziedziczyć zarówno po „Cat”, jak i „Latający ssak”. Niektóre inne języki, takie jak C# lub Java , osiągają coś podobnego (choć bardziej ograniczonego), umożliwiając dziedziczenie wielu interfejsów przy jednoczesnym ograniczeniu liczby klas bazowych do jednego (interfejsy, w przeciwieństwie do klas, dostarczają tylko deklaracje funkcji składowych, bez implementacji lub składowej dane). Interfejs taki jak w C# i Javie można zdefiniować w C++ jako klasę zawierającą tylko czysto wirtualne funkcje, często nazywaną abstrakcyjną klasą bazową lub „ABC”. Funkcje składowe takiej abstrakcyjnej klasy bazowej są zwykle jawnie zdefiniowane w klasie pochodnej, a nie dziedziczone niejawnie. Dziedziczenie wirtualne w C++ wykazuje funkcję rozwiązywania niejednoznaczności o nazwie dominance .

Operatorzy i przeciążenie operatora

Operatory, których nie można przeciążyć
Operator Symbol
Operator rozdzielczości zakresu ::
Operator warunkowy ?:
operator kropki .
Operator wyboru członka .*
Sizeof podmiot” sizeof
operator „ typeid typeid

C++ dostarcza ponad 35 operatorów, obejmujących podstawową arytmetykę, manipulację bitami, pośrednictwo, porównania, operacje logiczne i inne. Prawie wszystkie operatory mogą być przeciążone dla typów zdefiniowanych przez użytkownika, z kilkoma godnymi uwagi wyjątkami, takimi jak dostęp do elementów członkowskich ( .i .*), a także operator warunkowy. Bogaty zestaw przeciążalnych operatorów ma kluczowe znaczenie dla tego, aby typy zdefiniowane przez użytkownika w C++ wyglądały jak typy wbudowane.

Przeciążalne operatory są również istotną częścią wielu zaawansowanych technik programowania C++, takich jak inteligentne wskaźniki . Przeciążenie operatora nie zmienia priorytetu obliczeń z udziałem tego operatora, ani nie zmienia liczby operandów, których używa operator (każdy operand może jednak zostać zignorowany przez operator, chociaż zostanie on oceniony przed wykonaniem). Przeciążone operatory „ &&” i „ ||” tracą swoją właściwość oceny zwarcia .

Wielopostaciowość

Polimorfizm umożliwia jeden wspólny interfejs dla wielu implementacji, a obiekty zachowują się inaczej w różnych okolicznościach.

C++ obsługuje kilka rodzajów statycznych (rozwiązanych w czasie kompilacji ) i dynamicznych (rozwiązanych w czasie wykonywania ) polimorfizmów , obsługiwanych przez opisane powyżej funkcje języka. Polimorfizm w czasie kompilacji nie pozwala na podejmowanie pewnych decyzji w czasie wykonywania , podczas gdy polimorfizm w czasie wykonywania zwykle wiąże się z obniżeniem wydajności.

Polimorfizm statyczny

Przeciążanie funkcji umożliwia programom deklarowanie wielu funkcji o tej samej nazwie, ale z różnymi argumentami (np. polimorfizm ad hoc ). Funkcje wyróżnia liczba lub rodzaj ich parametrów formalnych . Tak więc ta sama nazwa funkcji może odnosić się do różnych funkcji w zależności od kontekstu, w którym jest używana. Typ zwracany przez funkcję nie jest używany do rozróżniania przeciążonych funkcji, a różne typy zwracane spowodowałyby komunikat o błędzie w czasie kompilacji.

Podczas deklarowania funkcji programista może określić dla jednego lub więcej parametrów wartość domyślną . Dzięki temu parametry z wartościami domyślnymi mogą być opcjonalnie pominięte podczas wywoływania funkcji, w którym to przypadku zostaną użyte argumenty domyślne. Gdy funkcja jest wywoływana z mniejszą liczbą argumentów niż zadeklarowanych parametrów, jawne argumenty są dopasowywane do parametrów w kolejności od lewej do prawej, a wszelkie niedopasowane parametry na końcu listy parametrów są przypisywane do ich domyślnych argumentów. W wielu przypadkach określenie domyślnych argumentów w jednej deklaracji funkcji jest lepsze niż dostarczanie przeciążonych definicji funkcji z różnymi liczbami parametrów.

Szablony w C++ zapewniają wyrafinowany mechanizm pisania ogólnego, polimorficznego kodu (tj. polimorfizmu parametrycznego ). W szczególności za pomocą ciekawie powtarzającego się wzorca szablonu można zaimplementować formę statycznego polimorfizmu, która ściśle naśladuje składnię zastępowania funkcji wirtualnych. Ponieważ szablony C++ są zgodne z typem i Turing-complete , można ich również użyć, aby umożliwić kompilatorowi rozwiązywanie rekurencyjnych warunków warunkowych i generowanie istotnych programów za pomocą metaprogramowania szablonów . Wbrew niektórym opiniom kod szablonu nie wygeneruje kodu zbiorczego po kompilacji z odpowiednimi ustawieniami kompilatora.

Dynamiczny polimorfizm

Dziedzictwo

Zmienne wskaźniki i odwołania do typu klasy bazowej w C++ mogą również odwoływać się do obiektów dowolnych klas pochodnych tego typu. Dzięki temu tablice i inne rodzaje kontenerów mogą przechowywać wskaźniki do obiektów różnych typów (odwołania nie mogą być bezpośrednio przechowywane w kontenerach). Umożliwia to dynamiczny (w czasie wykonywania) polimorfizm, w którym odwoływane obiekty mogą zachowywać się inaczej, w zależności od ich (rzeczywistego, pochodnego) typu.

C++ udostępnia również operator, który pozwala kodowi na bezpieczną próbę konwersji obiektu, za pomocą bazowego odniesienia/wskaźnika, do bardziej pochodnego typu: downcasting . Próba jest konieczne, ponieważ często nie wiadomo, który pochodzi typ jest określany. ( Upcasting , konwersja do bardziej ogólnego typu, zawsze można sprawdzić/wykonać w czasie kompilacji za pośrednictwem , ponieważ klasy przodków są określone w interfejsie klasy pochodnej, widocznym dla wszystkich wywołujących.) opiera się na informacjach o typie w czasie wykonywania (RTTI), metadane w programie umożliwiające rozróżnianie typów i ich relacji. Jeśli a do wskaźnika nie powiedzie się, wynik jest stałą, natomiast jeśli miejscem docelowym jest odwołanie (które nie może mieć wartości null), rzutowanie zgłasza wyjątek. Obiekty, o których wiadomo, że są określonego typu pochodnego, można rzutować na ten za pomocą , z pominięciem RTTI i sprawdzania typu bezpiecznego środowiska uruchomieniowego , więc powinno to być używane tylko wtedy, gdy programista jest bardzo pewny, że rzutowanie jest i zawsze będzie prawidłowe. dynamic_caststatic_castdynamic_castdynamic_castnullptrstatic_castdynamic_cast

Funkcje członków wirtualnych

Zwykle, gdy funkcja w klasie pochodnej zastępuje funkcję w klasie bazowej, funkcja do wywołania jest określana przez typ obiektu. Dana funkcja jest zastępowana, gdy nie ma różnicy w liczbie lub typie parametrów między dwiema lub większą liczbą definicji tej funkcji. Dlatego w czasie kompilacji może nie być możliwe określenie typu obiektu, a tym samym prawidłowej funkcji do wywołania, biorąc pod uwagę jedynie wskaźnik klasy bazowej; decyzja jest zatem odkładana do czasu uruchomienia. Nazywa się to wysyłką dynamiczną . Wirtualne funkcje lub metody składowe umożliwiają wywołanie najbardziej specyficznej implementacji funkcji, zgodnie z rzeczywistym typem czasu wykonywania obiektu. W implementacjach C++ zwykle robi się to za pomocą wirtualnych tabel funkcji . Jeśli typ obiektu jest znany, można to ominąć, dołączając w pełni kwalifikowaną nazwę klasy przed wywołaniem funkcji, ale ogólnie wywołania funkcji wirtualnych są rozwiązywane w czasie wykonywania.

Oprócz standardowych funkcji członkowskich przeciążenia operatorów i destruktory mogą być wirtualne. Niedokładna reguła oparta na praktycznym doświadczeniu mówi, że jeśli jakakolwiek funkcja w klasie jest wirtualna, to destruktor również powinien. Ponieważ typ obiektu podczas jego tworzenia jest znany w czasie kompilacji, konstruktory, a co za tym idzie, konstruktory kopiujące, nie mogą być wirtualne. Niemniej jednak może zaistnieć sytuacja, w której należy utworzyć kopię obiektu, gdy wskaźnik do obiektu pochodnego jest przekazywany jako wskaźnik do obiektu bazowego. W takim przypadku typowym rozwiązaniem jest utworzenie (lub podobnej) funkcji wirtualnej, która po wywołaniu tworzy i zwraca kopię klasy pochodnej. clone()

Funkcję członkowską można również uczynić „czysto wirtualną”, dołączając ją po nawiasie zamykającym i przed średnikiem. Klasa zawierająca czystą funkcję wirtualną nazywa się klasą abstrakcyjną . Obiekty nie mogą być tworzone z klasy abstrakcyjnej; można je jedynie wyprowadzić. Każda klasa pochodna dziedziczy funkcję wirtualną jako czystą i musi zapewnić jej nieczystą definicję (i wszystkie inne czyste funkcje wirtualne) przed utworzeniem obiektów klasy pochodnej. Program, który próbuje utworzyć obiekt klasy z czysto wirtualną funkcją składową lub odziedziczoną czysto wirtualną funkcją składową, jest źle sformułowany. = 0

Wyrażenia lambda

C++ zapewnia obsługę funkcji anonimowych , znanych również jako wyrażenia lambda , o następującej formie:

[capture](parameters) -> return_type { function_body }

Od C++20 możesz pisać parametry szablonu bez słowa kluczowego : template

[capture]<template_parameters>(parameters) -> return_type { function_body }

Jeśli lambda nie przyjmuje parametrów, można pominąć (), czyli

[capture] -> return_type { function_body }

Również zwracany typ wyrażenia lambda można wywnioskować automatycznie, jeśli to możliwe, np.:

[](int x, int y) { return x + y; } // inferred
[](int x, int y) -> int { return x + y; } // explicit

Lista popiera definicję zamknięć . Takie wyrażenia lambda są zdefiniowane w standardzie jako cukier składniowy dla nienazwanego obiektu funkcji . [capture]

Obsługa wyjątków

Obsługa wyjątków służy do informowania o istnieniu problemu lub błędu w czasie wykonywania, od miejsca wykrycia do miejsca, w którym można rozwiązać problem. Pozwala to na wykonanie tego w jednolity sposób i niezależnie od głównego kodu, przy jednoczesnym wykrywaniu wszystkich błędów. W przypadku wystąpienia błędu zostanie zgłoszony (podniesiony) wyjątek, który następnie zostanie przechwycony przez najbliższy odpowiedni program obsługi wyjątków. Wyjątek powoduje wyjście z bieżącego zakresu, a także z każdego zewnętrznego zakresu (propagacji), dopóki nie zostanie znaleziony odpowiedni program obsługi, wywołując kolejno destruktory dowolnych obiektów w tych zamkniętych zakresach. Jednocześnie wyjątek prezentowany jest jako obiekt niosący dane o wykrytym problemie.

Niektóre przewodniki po stylu C++, takie jak Google, LLVM i Qt, zabraniają używania wyjątków.

Kod powodujący wyjątek jest umieszczany w bloku. Wyjątki są obsługiwane w oddzielnych blokach (programy obsługi); każdy blok może mieć wiele programów obsługi wyjątków, jak widać w poniższym przykładzie. trycatchtry

#include <iostream>
#include <vector>
#include <stdexcept>

int main() {
    try {
        std::vector<int> vec{3, 4, 3, 1};
        int i{vec.at(4)}; // Throws an exception, std::out_of_range (indexing for vec is from 0-3 not 1-4)
    }
    // An exception handler, catches std::out_of_range, which is thrown by vec.at(4)
    catch (std::out_of_range &e) {
        std::cerr << "Accessing a non-existent element: " << e.what() << '\n';
    }
    // To catch any other standard library exceptions (they derive from std::exception)
    catch (std::exception &e) {
        std::cerr << "Exception thrown: " << e.what() << '\n';
    }
    // Catch any unrecognised exceptions (i.e. those which don't derive from std::exception)
    catch (...) {
        std::cerr << "Some fatal error\n";
    }
}

Możliwe jest również celowe zgłaszanie wyjątków za pomocą słowa kluczowego; te wyjątki są obsługiwane w zwykły sposób. W niektórych przypadkach wyjątki nie mogą być stosowane z przyczyn technicznych. Jednym z takich przykładów jest krytyczny element systemu wbudowanego, w którym każda operacja musi mieć zagwarantowane zakończenie w określonym czasie. Nie można tego określić z wyjątkami, ponieważ nie istnieją narzędzia do określenia maksymalnego czasu wymaganego do obsługi wyjątku. throw

W przeciwieństwie do obsługi sygnałów , w której funkcja obsługi jest wywoływana od punktu awarii, obsługa wyjątków opuszcza bieżący zakres przed wprowadzeniem bloku catch, który może znajdować się w bieżącej funkcji lub dowolnym z poprzednich wywołań funkcji znajdujących się obecnie na stosie.

Biblioteka standardowa

Projekt standardu „Working Paper”, który został zatwierdzony jako C++98; połowa jego rozmiaru została poświęcona Bibliotece Standardowej C++.

C ++ standardowy składa się z dwóch części: języka podstawowego i biblioteki standardowej. Programiści C++ oczekują tego ostatniego przy każdej większej implementacji C++; zawiera zagregowane typów ( wektory , listy, mapy, zestawy, kolejki, stosy, tablic, krotki), algorytmy (Znajdź, for_each , binary_search , random_shuffle, etc.), urządzeń wejścia / wyjścia ( iostream , dla odczytu i zapisu do konsola i pliki), biblioteka systemu plików, obsługa lokalizacji, inteligentne wskaźniki do automatycznego zarządzania pamięcią, obsługa wyrażeń regularnych , biblioteka wielowątkowa , obsługa atomów (pozwalająca na odczyt lub zapis zmiennej przez co najwyżej jeden wątek na raz bez żadnych zewnętrznych synchronizacja), narzędzia czasowe (pomiary, pobieranie aktualnego czasu itp.), system do konwersji raportowania błędów, który nie korzysta z wyjątków C++ na wyjątki C++, generator liczb losowych i nieco zmodyfikowana wersja standardowej biblioteki C (do jest zgodny z systemem typu C++).

Duża część biblioteki C++ jest oparta na Standardowej Bibliotece Szablonów (STL). Przydatne narzędzia dostarczane przez STL obejmują kontenery jako kolekcje obiektów (takich jak wektory i listy ), iteratory zapewniające dostęp do kontenerów w postaci tablicy oraz algorytmy wykonujące operacje, takie jak wyszukiwanie i sortowanie.

Ponadto dostępne są (multi)mapy ( tablice asocjacyjne ) i (multi)zestawy, z których wszystkie eksportują zgodne interfejsy. Dlatego przy użyciu szablonów można pisać ogólne algorytmy, które działają z dowolnym kontenerem lub dowolną sekwencją zdefiniowaną przez iteratory. Podobnie jak w C, funkcje z biblioteki są dostępne za pomocą #include dyrektywy dołączyć standardowy nagłówek . C ++ standardowa biblioteka zawiera 105 standardowych nagłówków, z których 27 są nieaktualne.

Standard zawiera STL, który został pierwotnie zaprojektowany przez Aleksandra Stiepanowa , który przez wiele lat eksperymentował z generycznymi algorytmami i kontenerami. Kiedy zaczynał z C++, w końcu znalazł język, w którym można było tworzyć ogólne algorytmy (np. sortowanie STL), które działają nawet lepiej niż na przykład standardowa biblioteka C qsort, dzięki funkcjom C++, takim jak inlining i kompilacja. wiązanie czasu zamiast wskaźników funkcji. Norma nie odnosi się do niej jako „STL”, ponieważ jest to jedynie część biblioteki standardowej, ale termin ten jest nadal powszechnie używany w celu odróżnienia go od reszty biblioteki standardowej (strumienie wejścia/wyjścia, internacjonalizacja, diagnostyka, podzbiór biblioteki C itp.).

Większość kompilatorów C++ i wszystkie główne zapewniają zgodną ze standardami implementację standardowej biblioteki C++.

Podstawowe wytyczne C++

Wytyczne C++ Core są inicjatywą prowadzoną przez Bjarne Stroustrupa, wynalazcę C++ oraz Herba Suttera, zwołania i przewodniczącego Grupy Roboczej C++ ISO, aby pomóc programistom w pisaniu „nowoczesnego C++” przy użyciu najlepszych praktyk dla standardów językowych C+ +14 i nowsze oraz aby pomóc twórcom kompilatorów i narzędzi do sprawdzania statycznego w tworzeniu reguł wyłapywania złych praktyk programistycznych.

Głównym celem jest wydajne i spójne pisanie C++ bezpiecznego dla typów i zasobów.

Podstawowe wytyczne zostały ogłoszone podczas przemówienia otwierającego CPPCon 2015.

Wytycznym towarzyszy Biblioteka Wsparcia Wytycznych (GSL), biblioteka zawierająca tylko nagłówki typów i funkcji do implementacji Wytycznych Podstawowych oraz statyczne narzędzia sprawdzające do egzekwowania zasad Wytycznych.

Zgodność

Aby dać producentom kompilatorów większą swobodę, komitet standardów C++ postanowił nie dyktować implementacji maglowania nazw , obsługi wyjątków i innych funkcji specyficznych dla implementacji. Wadą tej decyzji jest to, że kod wynikowy tworzony przez różne kompilatory powinien być niezgodny. Były jednak próby standaryzacji kompilatorów dla konkretnych maszyn lub systemów operacyjnych (np. C++ ABI), choć obecnie wydaje się, że zostały one w dużej mierze zarzucone.

Z C

C++ jest często uważany za nadzbiór C, ale nie jest to do końca prawda. Większość kodu C można łatwo skompilować poprawnie w C++, ale istnieje kilka różnic, które powodują, że niektóre poprawne kody C są nieprawidłowe lub zachowują się inaczej w C++. Na przykład C zezwala na niejawną konwersję z do innych typów wskaźników, ale C++ nie (ze względów bezpieczeństwa typu). Ponadto C++ definiuje wiele nowych słów kluczowych, takich jak i , które mogą być używane jako identyfikatory (na przykład nazwy zmiennych) w programie C. void*newclass

Niektóre niezgodności zostały usunięte w wydanej w 1999 roku rewizji standardu C ( C99 ), która obsługuje teraz funkcje C++, takie jak komentarze wierszy ( //) i deklaracje zmieszane z kodem. Z drugiej strony, C99 wprowadził szereg nowych funkcji, których C++ nie wspierał, a które były niekompatybilne lub nadmiarowe w C++, takie jak tablice o zmiennej długości , natywne typy liczb zespolonych (jednak klasa w standardowej bibliotece C++ zapewnia podobną funkcjonalność , chociaż nie są zgodne z kodem), wyznaczone inicjatory, literały złożone i słowo kluczowe. Niektóre z funkcji wprowadzonych przez C99 zostały uwzględnione w kolejnej wersji standardu C++ , C++11 (z tych, które nie były redundantne). Jednak standard C++11 wprowadza nowe niezgodności, takie jak uniemożliwienie przypisania literału ciągu do wskaźnika znakowego, który pozostaje prawidłowym C. std::complexrestrict

Aby mieszać kod C i C++, każda deklaracja lub definicja funkcji, która ma być wywołana z/użyta zarówno w C, jak i C++, musi być zadeklarowana z połączeniem C poprzez umieszczenie jej w bloku. Taka funkcja może nie opierać się na cechach zależnych od zniekształcania nazw (tj. przeciążania funkcji). extern "C" {/*...*/}

Krytyka

Pomimo powszechnego przyjęcia, niektórzy znani programiści krytykowali język C++, w tym Linus Torvalds , Richard Stallman , Joshua Bloch , Ken Thompson i Donald Knuth .

Jednym z najczęściej krytykowanych punktów C++ jest jego postrzegana złożoność jako języka, z krytyką, że duża liczba nieortogonalnych cech w praktyce wymaga ograniczenia kodu do podzbioru C++, unikając w ten sposób korzyści czytelności wspólnego stylu i idiomów . Jak wyraził Joshua Bloch :

Myślę, że C++ znacznie przekroczył próg złożoności, a mimo to programuje go wiele osób. Ale to, co robisz, to zmuszanie ludzi do podszeregowania. Więc prawie każdy sklep, który znam, który używa C++, mówi: „Tak, używamy C++, ale nie robimy dziedziczenia wielokrotnych implementacji i nie używamy przeciążania operatorów”. Jest tylko kilka funkcji, których nie będziesz używać, ponieważ złożoność wynikowego kodu jest zbyt wysoka.I nie sądzę, że to jest dobre, gdy musisz zacząć to robić.Tracisz tę przenośność programisty, w której każdy może przeczytaj kod wszystkich innych, co moim zdaniem jest bardzo dobrą rzeczą.

Donald Knuth (1993, komentując wstępnie ustandaryzowany C++), który powiedział o Edsgerze Dijkstrze, że „myślenie o programowaniu w C++” „sprawiłoby go fizycznie chorego”:

Problem, który mam z nimi dzisiaj polega na tym, że... C++ jest zbyt skomplikowany. W tej chwili nie mogę pisać przenośnego kodu, który moim zdaniem działałby na wielu różnych systemach, chyba że unikam wszystkich egzotycznych funkcji. Ilekroć projektanci języka C++ mieli dwa konkurujące ze sobą pomysły, jak powinni rozwiązać jakiś problem, mówili "OK, zrobimy je oba". Więc język jest zbyt barokowy jak na mój gust.

Ken Thompson , który był kolegą Stroustrupa w Bell Labs, tak ocenia:

Z pewnością ma swoje dobre strony. Ale ogólnie uważam, że to zły język. Robi wiele rzeczy w połowie dobrze i to tylko kupa śmieci, które wzajemnie się wykluczają. Każdy, kogo znam, niezależnie od tego, czy jest to osoba osobista, czy korporacyjna, wybiera podzbiór, a te podzbiory są różne. Więc to nie jest dobry język do przenoszenia algorytmu — mówienia: „Napisałem to; tutaj, weź to”. Jest zbyt duży, zbyt skomplikowany. I oczywiście jest zbudowany przez komitet . Stroustrup prowadził kampanię przez wiele lat, znacznie wykraczając poza wszelki wkład techniczny, jaki wniósł do języka, aby go zaadoptować i używać. I tak jakby kierował wszystkimi komitetami normalizacyjnymi z batem i krzesłem. I nikomu nie powiedział „nie”. Umieścił każdą cechę w tym języku, jaki kiedykolwiek istniał. Nie był czysto zaprojektowany — był po prostu połączeniem wszystkiego, co się pojawiło. I myślę, że to drastycznie ucierpiało.

Jednak Brian Kernighan , również kolega z Bell Labs, kwestionuje tę ocenę:

C++ był niezwykle wpływowy. ... Wiele osób twierdzi, że C++ jest zbyt duży i zbyt skomplikowany itd. itd., ale w rzeczywistości jest to bardzo potężny język i prawie wszystko, co jest w nim, jest tam z naprawdę rozsądnego powodu: nie jest to ktoś, kto robi przypadkowe wynalazki , to w rzeczywistości ludzie próbują rozwiązywać problemy świata rzeczywistego. Wiele programów, które dzisiaj uważamy za oczywiste, a których właśnie używamy, to programy w C++.

Sam Stroustrup komentuje, że semantyka C++ jest znacznie czystsza niż jego składnia: „w C++ istnieje znacznie mniejszy i czystszy język, który walczy o wyjście”.

Inne skargi mogą obejmować brak odbicia lub wyrzucanie śmieci , długie czasy kompilacji, postrzegane pełzanie funkcji i pełne komunikaty o błędach, szczególnie z metaprogramowania szablonów.

Zobacz też

Bibliografia

Dalsza lektura

Zewnętrzne linki