Użytkownik: Znajomość C jest warunkiem koniecznym uczenia się C++,
prawda?
Stroustrup: Nieprawda. Wspólny podzbiór C i C++ jest łatwiejszy do
nauczenia się, niż C. Mniej typów błędów jest koniecznych do ręcznego
wyłapywania (system typów C++ jest bardziej ścisły i wyrazisty), mniej
sztuczek do uczenia się (C++ pozwala na wyrażanie wielu rzeczy bez stosowania
obejść) i dostępne lepsze biblioteki. Najlepsza część wstępna do nauczenia się
w C++ to nie jest "cały C".
Bjarne Stroustrup FAQ
Jeśli kogoś interesuje, pozwolę sobie jako "praktyczny użytkownik" (choć niektórzy mówią "fanatyk" ;) C++, na odrobinę publicystyki. Zrzuciłem to do osobnego rozdziału, jeśli ktoś nie chce być zanudzany zbyt długim wstępem. ;)
Rozdział ten zresztą zaczął się trochę niepostrzeżenie zbytnio rozrastać i stał się rodzajem "advocacy". Swoją drogą nie sądziłem, że to będzie potrzebne, ale ku mojemu zdziwieniu jest potrzebne, i co więcej, nie wygląda na to, żeby miało za jakiś czas przestać. Zaczęło się oczywiście od tego, że próbowałem wyjaśnić, co mnie zainspirowało do napisania tej, nazwijmy to, pracy.
Nie ukrywam oczywiście, że zainspirowały mnie do tego dwie rzeczy. Pierwsza, to sposób, w jaki język C++ jest wykładany po dziś dzień(!) na polskich uczelniach (ciekawe, czy kiedyś nadejdą takie czasy, żebym mógł wyciąć ten akapit:), a także we współcześnie dostępnych książkach. Może niektórym (zwłaszcza w US) trudno by było w to uwierzyć, ale na wielu polskich uczelniach nadal popularnym kompilatorem jest Borland C++ 3.1 (zresztą na maszynach klasy 386, wciąż je zalegających, nic standardowego uruchomić się nie da). Jest tylko kilka chlubnych wyjątków. Nadal też popularne są książki, które nie tylko wykładają C++ sprzed standardu ISO (1998 rok), ale również kolejność ułożenia materiału pozostawia wiele do życzenia (np. na początku operujemy wszystkim jak w języku C, a na końcu gdzieś są wzorce, a to jak wzorce, to może od razu std::string, listy, wektory, kolejki...). Druga rzecz z kolei, to mnóstwo mitów nt. języka C++, wciąż będących na topie w obiegowych opiniach.
Dlaczego zatem "bez cholesterolu" ? Przede wszystkim – bo to jest ostatnio modne ;*). A tak na poważnie – ma to oznaczać przedstawienie C++ jako języka, który wcale nie jest wypasiony w nie wiadomo co i przez to naobrastał w tłuszcz. Również jako przeciwwagę dla wciąż przez wielu reklamowanego C, co do którego panuje kilka dość rozpowszechnionych opinii (nie mających nic wspólnego z prawdą), jak np. to, że język C jest łatwiejszy, "chudszy" i szybszy od C++. W wyniku tego wielu amatorów programowania zajęło sie C, a nie C++ (co widać po ilości oprogramowania OpenSource pisanego w C). Potem stosowali oni wyuczone praktyki z doświadczenia z językiem C. Efekt, jak można zaobserwować, jest taki że ci ludzie już się sensownego sposobu programowania w C++ nauczyć często nie potrafią – a wspomniane mity nt. języka C (dzięki również nim) nadal krążą.
Możesz mi zresztą wierzyć lub nie, ale uczenie się języka C zanim się pozna język C++ jest zwykłą stratą czasu. Po pierwsze, język C++ i tak przyda ci się bardziej, niż język C. Po drugie, jeśli nawet język C będzie ci potrzebny, zawsze można nauczyć się go jako "bardziej surową i okrojoną" wersję języka C++, co będzie znacznie łatwiejsze. Nikt przecie nie zaczyna nauki od asemblera, lecz od jakiegoś mniej wymagającego języka, a poza tym:
Nie raz słyszałem przy propozycjach poważniejszego potraktowania C++ stwierdzenia, że "po co nam to C++, czy zamierzamy programować obiektowo?". Nawet nie chciałoby mi się machnąć na to ręką, bo po samej tej wypowiedzi można już się domyślić, że taki gościu o C++ co najwyżej słyszał i to też niezbyt wiele. Ale niestety takie mamy czasy, że tacy ciemniacy decydują dziś o wielu istotnych sprawach, więc przynajmniej trzeba umieć im pokazać, że się wie, o czym mówi. A na C++ przejść warto dlatego, że:
Ale wśród obrońców języka C napotykamy wciąż mnóstwo "defensywnych" opinii, z
którymi dość wielu ludzi się zgadza, np.:
Twierdzenie to popiera się sugestami, że przecież skoro C++ zawiera tyle
różnych właściwości, wspiera tyle różnych technik programowania, że jest
znacznie bardziej skomplikowany, więc nauczyć się go będzie trudniej. Na
upartego można by tu przyznać rację, chociaż trudno stwierdzić, czy taki
ktoś rzeczywiście wie, o czym mówi. Skomplikowane techniki programowania
są dziś tak wszechobecne, że w każdym języku, tak samo w C, jak i w C++,
trzeba umieć się nimi posługiwać. Idiotyzm tego stwierdzenia zatem polega
na tym, że sugeruje się, jakoby w C++ trzeba było opanować obsługę każdej
możliwej techniki programowania, podczas gdy w C można programować w "starym
dobrym stylu proceduralnym". Przecież to bzdura - jeśli dla danego programu
wystarczają techniki proceduralne, to wystarczy znać C++ tylko na poziomie
udostępniającym te właśnie techniki i już można programować. Skoro jest
prawdą, że poznanie technik proceduralnych wystarczy do programowania w C,
to dlaczego nie miałoby to wystarczyć do programowania w C++?
A jeśli z kolei ktoś odczuwa potrzebę stosowania bardziej skomplikowanych
technik programowania, to może się okazać, że na naukę języka C niepotrzebnie
stracił czas. Bo używanie tych technik w C jest, owszem, możliwe, tyle że
to jest istna katorga. Zresztą, nie potrzeba tu nawet wchodzić w jakieś bardzo
skomplikowane obszary, by pokazać, że niemal każdy program da się napisać
w C++ łatwiej, niż w C. A co do uczenia się?
Zwolennikom twierdzenia, jakoby C można było się nauczyć łatwiej,
proponowałbym nauczyć początkującego obsługi funkcji printf i zmiennych
tekstowych (i niech on sobie to porówna choćby z write i stringiem z pascala).
Istnieje przecież tyle "prostych" języków: Lisp, Smalltalk, czy nawet
asembler. Asembler ma przecież tylko kilka rozkazów i wszystko robi się w
podobny sposób – dlaczego zatem asembler nie jest popularniejszy od C
(nb: kiedyś był ;)? Przede wszystkim dlatego, że nie da się w nim zrobić
niczego w sposób wygodny i czytelny. Z C jest już lepiej, ale różnica pomiędzy
C a C++ przedstawia się dość podobnie.
Mówi się też o C++, że nie da się go opanować w "rozsądnie krótkim czasie".
Zarzuca się temu językowi, że jest bardzo skomplikowany i "przez stanowcze
stwierdzenie" dowodzi się, że to jest dla języka programowania "bezwzględnie
złe". Czy rzeczywiście? Programowanie niestety jest skomplikowaną dziedziną i
proste języki programowania nie dostarczają niestety narzędzi do rozwiązywania
wszystkich problemów. C++ jest skomplikowany? Możliwe, ale głównie dlatego, że
udostępnia użytkownikowi bardzo wiele sposobów programowania. Jeśli ktoś
uważa, że jest zbyt skomplikowany, to prosiłbym również o podanie
jakiegokolwiek innego języka, który udostępnia tyle samo możliwości i jest
mniej skomplikowany. C# i Java do tego aspirują, ale im to na razie specjalnie
nie wychodzi; co więcej, w miarę "doganiania C++" w udostępnianych
właściwościach, przeganiają go w stopniu komplikacji. Już obecnie ta "łatwa
i przyjemna" Java znacznie przegoniła C++ jeśli chodzi o stopień
skomplikowania.
Oczywiście, że język C++ jest o wiele bardziej skomplikowany, niż np. C, ale
też z drugiej strony porównywanie z C pełnego C++, który posiada takie
właściwości, jakich w C nie da się w sensowny sposób uzyskać, jest bezsensem.
Żeby mieć jakiekolwiek miarodajne porównanie, należałoby porównywać w tym
zakresie, w którym te języki się pokrywają, w zakresie który udostępnia obu
tym językom podobne właściwości. Zatem w C++ należałoby dać sobie spokój
zarówno z metodami wirtualnymi, dziedziczeniem i wzorcami (również
przeciążaniem), a pozostawić przede wszystkim referencje, przesuwanie
zmiennych lokalnych, słowo struct przed nazwą typu, funkcje inline. Dopiero
wtedy sprawdzić, jaki jest faktyczny stopień skomplikowania tak okrojonego
C++. I po zebraniu tego wszystkiego okazuje się niestety, że C++ wcale nie
jest trudniejszy, a wręcz przeciwnie. Jeśli dodamy do tego jeszcze biblioteke
standardową, dzięki której łatwiej się używa różnych podstawowych typów danych
(jak choćby string) i porównując do tego sposoby uzyskiwania tego samego w C,
proporcje te wychodzą jeszcze gorzej (wszystkim ewentualnym oponentom
zaznaczam, że używanie typów polimorficznych, którym np. ostream niewątpliwie
jest, bynajmniej nie oznacza używania jakichkolwiek bardziej skomplikowanych
technik, niż przy printf). Dodatkowo, znajomość C++ jest użyteczna nawet w
takim ograniczonym zakresie. Porównywać możemy jeszcze takie rzeczy, jak np.
komplikacje projektów robionych w C i C++. Oczywiście trudno o tym mówić
jednoznacznie, ale co do projektów robionych podobnymi metodami co do C z
ostrożnym wsparciem zaawansowanych możliwości, również okazuje się, że w C++
taki projekt robi się prostszy. Owszem, bywało w C++ sporo projektów
rozrośniętych ponad miarę, ale widywałem też projekty w C++, które były
przenoszone z C i zawierały one co najmniej połowę mniej kodu przy tej samej
funkcjonalności (podpowiem też, że używany w wielu firmach softwarowych
przelicznik rozkazów asemblera na linijki kodu jest wyższy dla C++, niż dla
C). Może to nie dowodzi, ale na pewno to sugeruje, że C++ jest jednak językiem
wyższego poziomu, niż C. A w każdym razie, że można w nim pisać na wyższym
poziomie.
Nie twierdzę zatem, że "całego" C++ można nauczyć się szybciej, niż C.
Można jednak szybciej, niż C, nauczyć się podzbioru C++ dającego te same
możliwości, co język C (tzw. "lepszego C"). Dzięki bardzo rozbudowanej
bibliotece standardowej można przećwiczyć programowanie w C++ na przykładach
bliższych codziennemu życiu i praktyce, bez potrzeby definiowania wszystkich
drobnych szczegółów od zera, jak to jest w przypadku języka C. Takimi rzeczami
jak np. wskaźniki i obiekty dynamiczne można się zająć znacznie później, mając
już za sobą przećwiczone operacje na skomplikowanych typach danych. W języku C
natomiast trzeba poznać wskaźniki i obiekty dynamiczne jak najszybciej, bo bez
ich znajomości nie da się operować nawet czymś tak banalnym jak napisy, czy
operacje wejścia-wyjścia, o skomplikowanych strukturach danych nie
wspominając.
Przykładowo, kiedy nowicjuszowi mówi się o operowaniu na zmiennych
tekstowych, można poprzestać na podstawowych wiadomościach o typie `string'.
Opuszcza się w ten sposób spory kawał dość trudnego na początek tematu, a
jednocześnie początkujący od razu może pobawić się zmiennymi tekstowymi.
Podobnie, wysyłanie tekstów i liczb do strumienia jest łatwiejsze do
objaśnienia, niż tłumaczenie, po co jest to całe %d, %s i dlaczego printf jest
taka skomplikowana (formatowanie zawsze można odłożyć na później). W C bez
wyłożenia dokładnych informacji o tym, co to są tablice, jak jest zbudowany
string, jak się go kopiuje, po co w scanf przekazuje przez wskaźnik (co nie
jest ani krótkim, ani łatwym tematem) zmiennych tekstowych nie da się używać
nawet do najbanalniejszych operacji (a o bardziej skomplikowanych, jak
wycinanie i sklejanie napisów to już nie wspomnę), podobnie jak poważniejsze
zastosowania printf są niemożliwe bez denerwującego określania wprost, jakiego
typu jest wypisywana wartość (o pomyłkę tu nietrudno, przy scanf jeszcze
bardziej).
Argument, z którym się również zetknąłem, to taki, że dopuszczenie ludzi do C++
zaowocuje robieniem mnóstwa dziwnych i głupich rzeczy. Że np. możliwość
przeciążania operatorów doprowadzi do tworzenia różnych, niejednoznacznych
definicji. Że przeciążanie funkcji doprowadzi do zamieszania definicyjnego.
Podobnie można mówić też o programowaniu generycznym i obiektowym. Moja
praktyka jednak nie potwierdza tego. Okazuje się, że największe potworki robią
ludzie, którzy nie potrafią z właściwości C++ zrobić sensownego użytku.
Inaczej mówiąc, ci którzy dobrze znają C++ i umieją z niego korzystać,
wykorzystują jego właściwości w sensowny i przemyślany sposób, podchodząc do
wszelkich jego właściwości z ostrożnością. Ci, którzy nie potrafią, programują
w C++ tak samo, jak w C i to ich twory są zazwyczaj ciężkie w utrzymaniu i
rozbudowie.
Zauważałem niejednokrotnie tego typu stwierdzenia, ale nikt, kto tak mówił,
nie zdołał podać żadnych konkretów. Można by jednak spróbować zrobić małą
analizę poszczególnych właściwości C++:
Podsumowując: techniki konieczne do programowania w C++ nie powodują żadnych
obciążeń podczas wykonania w stosunku do języka C. Niektóre techniki
dodatkowe, mające swoje alternatywy w C, powodują pewne koszty. Zazwyczaj te
alternatywne techniki jednak nie są mniej kosztowne, a często też są gorsze od
strony projektanckiej. Jeśli ktoś chciałby dodać jeszcze coś do powyższych
punktów – zapraszam.
Ten mit jest dość trudno obalić. Równie trudno było obalić mit, że asembler
jest najszybszym językiem programowania, co odstraszało początkowo ludzi od C
(sam w to kiedyś wierzyłem). Ten mit jednak został już dawno obalony, mianowicie
wydajne programowanie w asemblerze w dzisiejszych czasach wymaga znajomości tylu
różnych kruczków w procesorze, że programista nie ma szans tego wszystkiego
zapamiętać, natomiast kompilator pamięta i stosuje to bez problemów (słyszałem
od niektórych, jak podjęli rywalizację z kompilatorem o optymalizację na
poziomie asemblera i przegrali!). Z językiem C jest trochę inna sprawa, bo on
przypomina bardzo język wysokiego poziomu, choć w rzeczywistości jest przenośnym
asemblerem.
Osławione, a nie do końca rozumiane przez wielu wzorce, stały się niestety
źródłem takiej wydajności (zarówno czasu tworzenia projektu jak i wydajności
programu), jakiej w C uzyskać nie sposób. Pewien artykuł na
Dr. Dobb's Journal
prezentuje praktyczne przykłady na to, jak wzorce mogą zwiększać wydajność
poprzez dostarczanie założeń, że wskaźniki nie mają aliasów (słowo
restrict w C99 okazało się również półśrodkiem). Dzięki wzorcom
i jednocześnie dzięki temu, że wskaźniki są do różnych typów, kompilator od razu
może założyć ich niealiasowość jak również często niepokrywanie się zakresów, co
mocno upraszcza (i uwydajnia) kod asemblerowy. Oczywiście, że wiele kompilatorów
jeszcze nie optymalizuje aż tak dobrze, ale i tak kod jest nadal szybszy, niż
jego odpowiedniki zależne od elementów czasu-wykonania.
Trochę bardziej skomplikowany jest argument związany z generyzmem. Ponieważ
może być tego dość sporo, więc najważniejsze zdania będę zaznaczał wyraźnie; jak
ktoś nie chce czytać całości, niech idzie tylko po nich. Pod koniec dałem też
małe podsumowanie :).
Zanim rozważymy, jak istotne znaczenie dla szybkości oprogramowania ma fakt
bliskości asemblera, może najpierw rozważmy, na co w dzisiejszej inżynierii
oprogramowania przeznacza się najwięcej sił i środków. Szybkość działania
programu nie jest wcale najistotniejsza, wbrew temu co się niektórym wydaje.
Otóż, I mówiąc "jakość" nie mam bynajmniej na myśli stosunku złożoności problemu do
zużycia zasobów (to się nazywa "sprawność", ang. performance). Mówię tutaj o
czymś bardziej przyziemnym, mianowicie stosunku założeń funkcjonalnych programu
do ich faktycznej realizacji przez program. Innymi zatem słowy, programista
przez większość swojego czasu (niektóre szacunki mówią o 70%, a nawet 80%)
powiedzmy upewnia się, że program działa tak, jak powinien działać (w firmach o
marnej organizacji pracy z kolei walczy z bugami). Zastanówmy się zatem, skąd biorą się bugi. Większość na pewno zaraz
zaproponuje, że "przez niedopatrzenie programisty". Niestety, jest to odpowiedź
brzmiąca jak w pewnym dowcipie "w balonie" – jest stuprocentowo prawdziwa i
stuprocentowo nikomu nieprzydatna. Jakość pracy pojedynczego programisty (poza
pewnymi bardzo szczególnymi przypadkami) nie jest znaczącym czynnikiem, a w
każdym razie na ten czynnik wpływać się specjalnie nie da (poza odpowiednią
organizacją pracy), a przynajmniej nie za wiele. Dużo istotniejszy jest ten
mniej widoczny, za to jedyny znaczący czynnik, mianowicie ilość POTENCJALNYCH
pomyłek. Należy bowiem wiedzieć, że Dlatego właśnie największe wysiłki w tworzeniu metod i języków programowania
(i to począwszy od asemblera) były ukierunkowane właśnie na zminimalizowanie
owych okazji do popełniania pomyłek. To ma oczywiście jeszcze inny cel,
mianowicie upewnianie się (i tworzenie odpowiedniego oprogramowania testującego
i wspomagającego testowanie) jest bardziej czasochłonne (a więc kosztowniejsze)
w przypadku źródeł o dużej ilości potencjalnych pomyłek. Żeby powiedzieć "nie-wprost", do czego tak naprawdę dążono w tych wysiłkach,
zastanówmy się, jak zwiększyć ilość potencjalnych pomyłek. Jak wiadomo, kiedy
powstaje program, mnóstwo jego elementów pracuje w dość podobny sposób lub
posiada podobną konstrukcję. W asemblerze zatem dochodziło często do powtarzania
tych samych kawałków kodu. Otóż jednym z najskuteczniejszych sposobów
powiększania ilości okazji do popełniania pomyłek jest znana dobrze wszystkim
programistom, zwłaszcza początkującym, tzw. metoda Copy'ego-Paste'a. Przede
wszystkim za jej skutecznością we wspomnianej dziedzinie przemawia fakt, że Jest to mniej więcej uproszczona definicja pierwszego prawa Copy'ego-Paste'a.
Zastrzegam tylko od razu (może ktoś nie wie), co oznacza tak naprawdę metoda
Copy'ego-Paste'a. Otóż nie oznacza ono wyłącznie skopiowania kodu. Oznacza ono
skopiowanie kodu i dokonanie w nim drobnych zmian adoptujących do innego
otoczenia (miejsca owych zmian nazwijmy punktami specjalizacji). Praw
Copy'ego-Paste'a jest kilka, które może tu dla porządku wymienię: (Założenie) Niech będzie dany pewien fragment programu, którego
funkcjonalność jest potrzebna w N nowych miejscach, w związku z czym dokonuje
się skopiowania tego fragmentu kodu (zwanego źródłowym) N razy w inne miejsca
(zwane kodem docelowym), zmieniając tylko zewnętrznie zależne szczegóły tego
fragmentu. Jeśli kod źródłowy zawiera X defektów, to kod
docelowy posiada N*X defektów. Jeśli do dokonania zmian funkcjonalności
fragmentu źródłowego potrzeba X+C wysiłku, to do dokonania równoważnej zmiany
w kodzie docelowym potrzeba N*X+C wysiłku. Stała C jest tutaj wysiłkiem
włożonym w przygotowanie projektu zmian, która jest czynnikiem niewielkim w
porównaniu z X. Jeśli do przetestowania jednostki zawierającej
kod źródłowy potrzeba X "testkejsów" to do przetestowania jednostki
zawierającej kod docelowy potrzeba N*X "testkejsów". Każdy punkt specjalizacji (tzn. miejsce, które
należy zmodyfikować po dokonaniu kopiowania) jest dodatkowym miejscem okazji
do pomyłki. Dla kopiowania "następny z poprzedniego", w każdym wzorze powyżej, X należy
zastąpić 2X. Metoda Copy'ego-Paste'a jest często (nadal!) stosowana i to często z uwagi na
brak w językach programowania odpowiednich metod pozwalających na jego unikanie
(w czym przoduje język C) – ale również z uwagi na brak wiedzy i umiejętności
niektórych programistów. Co jest zatem przeciwieństwem copy-paste? Musimy zdawać sobie sprawę, że nie
wystarczy tutaj zrobić kod raz i użyć w wielu miejscach (podprogramy), ale
również uzależnić ten kod od kilku elementów tak, żeby był stosowalny w wielu
miejscach, również z uwagi na różnorodność struktur danych (często nazywa się to
"reużywalnością", ang. reusability). Jednak często jest to przeróbka pod
konkretne zastosowanie. A niestety Wszystkim fanom języków o krótkiej składni leję od razu kubeł zimnej wody na
głowę: ilość kodu to nie jest ilość linii, ani wielkość plików źródłowych w
bajtach. Jest to ilość względna, pasująca do konkretnego języka programowania.
Przydałoby się zatem pisać kod adoptowalny do szerokiej gamy zastosowań o
których nie wiadomo w momencie jego pisania. Taka właściwość kodu nazywana jest
GENERYZMEM (i zgodnie z zasadami słowotwórstwa, zwiększanie poziomu generyzmu
kodu można krócej nazwać "generycyzacją" – ewentualnie "ugenerycznianiem").
Otóż, To znaczy: zamiast wstawiać identyczny kod ponownie, zorganizowano
podprogramy z argumentami. Zamiast powtarzać dany kod po kilka razy dla
kolejnych elementów, można zrobić pętlę, która zawrze w sobie kod iterowany
zmienną sterującą. Zwracam tutaj też uwagę na kilka innych rzeczy. Przede
wszystkim, podprogramy faktycznie służą zmniejszeniu wielkości kodu. Pętle już
raczej nieznacznie. Następnie, jeśli chodzi o pętle i podprogramy to to są
właściwości dynamiczne. Mianowicie przy pętlach operacje mogą być zależne nie od
zahardkodowanych wartości, tylko od jakichś wartości powstałych w programie
(wartości "dynamicznych"). Ale tu się z tych własności nie korzysta; dynamizm
jest wykorzystywany tylko w celu "skrócenia zapisu", czyli generycyzacji
(współczesne kompilatory mają rozwijanie pętli jako opcję optymalizacji). W C
często idzie się dalej. Tutaj zamiast kopiowania kodu, można go uzależnić od
różnych dynamicznych właściwości. Stosuje się do tego funkcje. Zatem zamiast
hardkodować wartości i kopiować używające je fragmenty, wpisuje się je do
odpowiednich struktur (lub podaje jako argumenty funkcji), a funkcja to już
odpowiednio przetworzy. Zwracam tutaj uwagę na różnicę w szybkości, którą daje
się już zaobserwować. Przy większej komplikacji danych, wszystkie obliczenia
muszą zostać przeprowadzone na bieżąco przez proces. Gdyby zamiast tych obliczeń
stosowano copy-paste, to oczywiście kod przyspieszyłby nawet sporo, bo wtedy nie
program by je obliczał, tylko obliczyłby je kompilator i do programu powstawiał
tylko wyniki (ewentualnie policzyłby sobie użytkownik podczas dokonywania
specjalizacji). Jednak przez copy-paste utracilibyśmy generyzm. Coś za coś, czyli
O tym przekonało się wielu twórców języków programowania. Choćby Smalltalk,
który miał być niby językiem w pełni dynamicznym, stosuje statyczne właściwości
wszędzie, gdzie to tylko możliwe (tzn. jeśli uzyskany efekt będzie ten sam). A
przynajmniej tak robią dobre, dopracowane kompilatory (GNU Smalltalk do nich nie
należy i efektywnie nadaje się tylko do nauki tego języka). Co nam zatem oferuje
lepszego C++? Dwie bardzo istotne właściwości: funkcje inline (argumentu, że
większość kompilatorów C je implementuje, nie przyjmuję!) oraz wzorce. Właśnie
dzięki wzorcom można zrobić coś pośredniego między hardkodowaniem a kodem
zbiorczym. Tym samym proponowałbym więc porównać szybkość funkcji z
zahardkodowanymi (za pośrednictwem wzorców) wartościami i to samo napisane w C,
gdzie taka wartość jest argumentem funkcji. Dzięki wzorcom zatem Czyli pozwala na uzyskanie tego samego efektu w kodzie wynikowym, co
copy-paste, ale równocześnie ze zmniejszeniem ilości linii kodu i powtarzających
się fragmentów. Wypadałoby zatem przypomnieć wszystkim zwolennikom języka C,
że parametryzowany kod (poza używaniem preprocesora, co jest niezalecane przez
wszelkie standardy kodowania) robi się tam za pomocą funkcji (często
uzależniając wybór fragmentu kodu do wykonania za pomocą switch), a
parametryzowane dane przez używanie wskaźnika void*. Wszystko z wyborem
fragmentów do wykonania lub interpretowaniem danych przez "odpowiednie"
wyciąganie ich bezpośrednio z pamięci, w czasie działania programu (w tej
dziedzinie biją go na głowę nawet języki z kompilatorami JIT, gdzie obliczenia
wykonuje się co prawda podczas wykonywania programu, ale raz, a nie za każdym
dostępem). Efektywnie zatem
Podsumowując zatem to wszystko, co napisałem wyżej: od generyzmu się nie
ucieknie. Programować metodą copy-paste można, owszem, ale tylko do pewnego
momentu. Zatem istotne jest raczej to, jakie możliwości w uzyskaniu generyzmu
ma dany język, a dokładnie, jak w nim trzeba programować, żeby programować
generycznie. W języku C aby uzyskać generyzm musimy stosować metody
dynamiczne, które powodują obciążenia podczas wykonywania i są niepomiernie
bardziej zawodne. W języku C++, dzięki istnieniu wzorców, mamy możliwość
uzyskania generyzmu nie pozbywając się zalet statyzmu (brak obciążeń podczas
wykonywania i większa niezawodność).
Jak zatem widać, C++ o wiele lepiej niż C spełnia zasadę "nie płać za to,
czego nie używasz" (co było zresztą podstawową zasadą obowiązującą podczas jego
tworzenia).
Żeby ostatecznie się rozprawić z mitem, jakoby ktoś C++ w ogóle nie
potrzebował (jeśli ma C), spróbuję się też odnieść do wypowiedzi, jak to
próbowano bronić używania języka C w bardzo specyficznych środowiskach
(oczywiście takich, dla których istnieje kompilator C++). Spróbuję tu
przytoczyć spotykane wypowiedzi już nawet takie bardziej stonowane i
sugerujące wręcz skłonność do kompromisu:
Naprawdę? W takim razie oto lista przykładów, które pokazują, jak to język
C "dobrze się nadaje do małych projektów" (w tym do w ogóle jakichkolwiek
projektów):
Inaczej mówiąc, w języku C trzeba bawić się w każdy najdrobniejszy szczegół,
za każdym razem trzeba tak czy siak ponownie to samo ręcznie wystukać, nawet
jeśli dokładnie takie same rzeczy robiło się już wiele razy. Owszem, jesteśmy
w stanie zrobić sobie to jakoś "skrótowo", ale to, jak możemy krótszym zapisem
opisać bardzo złożone operacje (to jest zresztą podstawowa właściwość każdego
języka wysokiego poziomu!), zależy wprost od tego, jakie możliwości dany język
nam daje w tej dziedzinie. W przypadku gdy język C daje nam jedynie prosty
model proceduralno-strukturalny, możliwości nie mamy zbyt wiele. Każdą
czynność musimy opisać funkcją, każdy obiekt wskaźnikiem, polimorfizm i
funkcjonalność wskaźnikami do funkcji, wszystko ładnie okraszone nieodstępnym
rzutowaniem, a lokalnego kontekstu dla obiektów i stanów (takich jak np.
sekcje krytyczne) nie możemy opisać niczym. Namiastkę metaprogramowania możemy
mieć, powiedzmy, za pomocą preprocesora, ale co większość poważnych
programistów myśli o używaniu preprocesora, to chyba nie trzeba przypominać,
a co mówi, rzadko nadaje się do cytowania w miejscach publicznych.
Jakoś to się tak dzieje, że jak na razie każdy program przepisany z C do C++
zrobił się mniejszy. Nie widziałem jeszcze, żeby się zdarzyło inaczej (chyba
że rozszerzano funkcjonalność, ale wtedy to już nie jest równoważne). Używany
zresztą przelicznik równoważności linii kodu na linie kodu asemblera jest
dla języka C niższy, niż dla C++ – widać więc jasno, że zakłada się, iż każdy
kod pisany w C można w C++ napisać w mniejszej liczbie linii.
Zresztą prawda jest też taka, że faktycznie nikt nie wybiera języka C do
małych projektów (bo i faktycznie nie ma to za wiele sensu). Ludzie nie
znający C++ zazwyczaj do tych celów wybierają perla :)
Programista, który twierdzi, że po to, aby mieć wszystko pod kontrolą,
potrzebuje języka, który na wszelki wypadek nie ma nic poza tym, co można
sobie w myślach przełożyć na asembler, nie nadaje się do programowania w
ogóle (i niech spada testować :). Można się w to bawić, ale po pierwsze, jeśli
ma się akurat takie hobby (a nie pisze się oprogramowanie, za które żąda się
potem twardej gotówki), a po drugie, jeśli pisze się aplikację niewielkich
rozmiarów (przy większych rozmiarach stopień komplikacji powoduje większe
kłopoty z zapanowaniem nad tym wszystkim). Pomijam już fakt, że małą aplikację
z kolei to wprawny programista napisze w C++ znacznie mniejszym kosztem. Można
by nawet zadać pytanie, dlaczego w C, a nie w asemblerze, albo Fortranie. I
obawiam się, że odpowiedź potwierdzi moje najgorsze przypuszcznia: nie w
asemblerze dlatego, że nie jest przenośny i nie w Fortranie, bo mało kto
potrafi w tym cokolwiek napisać. Jeśli faktycznie bardzo potrzebne jest w
danym miejscu programowanie na niskim poziomie, charakterystycznym dla języka
C, to przecież można to w C++ robić również, tyle tylko, że w C++ można do
tego czegoś jeszcze dorobić wygodny wysokopoziomowy interfejs.
I powtarzam nie wiem już po raz który – nie ma niczego w C++, co by się
działo poza kontrolą programisty. Trzeba tylko po prostu wiedzieć, jak takie
czy inne mechanizmy działają, a przede wszystkim, jak działają różne elementy
biblioteczne. Dodatkowo, w C++ można pisać w dokładnie tym samym stylu, co w C
i w takiej sytuacji nie używamy żadnej konstrukcji która "potencjalnie byłaby
poza kontrolą programisty". Jeśli w języku C++ dzieje się coś poza kontrolą
programisty, to chyba tylko takiego programisty, który po prostu nie zna C++.
No, to chyba oczywiste – gdyby za sterami samolotu siadł gościu, który
potrafi najwyżej prowadzić samochód, to można by z całą odpowiedzialnością
stwierdzić, że taki samolot robiłby wiele rzeczy poza kontrolą pilota :).
He he he :) Może to niektórych zdziwi, ale właśnie C++ był w szczególności
dedykowany do takich rzeczy, jak pisanie sterowników, czy jądra systemów
operacyjnych. Nie rozumiem zresztą, dlaczego cała kupa najróżniejszych
mędrków uważa, że do pisania niskopoziomowego oprogramowania potrzebny jest
niskopoziomowy język programowania (to taki sam "aksjomat idiotów", jak np.
to, że GUI musi być programowane obiektowo i wymaga odśmiecacza). Język C++
nie wniósł do inżynierii oprogramowania żadnego istotnego odkrycia, ani nie
zaproponował żadnej nowej technologii – sam jest bazowany głównie na języku C,
a wszystkie jego właściwości zostały zapożyczone z innych języków (Simula,
Ada, ML, Clu). Może więc przypomnę parę faktów z początków języka C++.
Bjarne Stroustrup, kiedy pisał swoją pracę dyplomową, był zafascynowany
językiem Simula, i zamierzał w nim ową pracę ukończyć. Niestety, wyszedł tu
później pewien drobny problem. Otóż implementacja Simuli, jaką Stroustrup
wówczas dysponował, okazała się marnej jakości i w efekcie Stroustrup musiał
swoją pracę zrobić w BCPL, gdyż w przeciwnym razie groziło mu niedotrzymanie
terminu. Stanął zatem przed wyborem: albo będzie kontynuował w Simuli i miał
do dyspozycji wspaniały język wysokopoziomowy (ale będzie miał problem z
ukończeniem doktoratu), albo poświęci wygodę pisania w języku wysokopoziomowym
na rzecz wydajności i pełnej przewidywalności niskopoziomowego języka BCPL.
Później, poza innymi językami programowania wysokiego poziomu, interesował go
również język C, w którym pisało się – jak na owe czasy (okolice 1980
roku) – dość prosto, a na dodatek język ten jako jeden z niewielu nie
wnosił żadnych obciążeń nie dających programiście korzyści (no i, co nie było
takie częste w tych czasach, był niemal całkowicie niezależny od platformy).
Ale, podobnie jak BCPL, łączył w sobie pełne panowanie nad wszystkim, co się
dzieje, z niemożliwością programowania na wysokim poziomie. I dlatego zapewne
Stroustrupowi wpadła do głowy taka myśl: Czy naprawdę aby móc korzystać z
zaawansowanych właściwości języków wysokiego poziomu musimy godzić się na
poważne obciążenia, z których praktycznie nie korzystamy? Dlaczego te
zaawansowane właściwości, które występują w różnych bardzo wysokopoziomowych
językach (jak np. programowanie obiektowe), nie mogłyby być także dostępne w
języku takim jak C?
I Bjarne Stroustrup – najwyraźniej – postawił sobie za zadanie
udowodnienie, że jest to możliwe – i w moim przekonaniu udało mu
się to. Zapoczątkował język, któremu jako bodaj pierwszemu i nawet bodaj
jedynemu udało się połączyć niskopoziomowość i pełną kontrolę języka C z
wysokopoziomowymi właściwościami języków takich jak Ada, czy Smalltalk.
Faktem jest, owszem, że zwolennicy języków wysokiego poziomu będą czuli pewien
niedosyt. Język C++ jest szczególny; doświadczenia w wysokopoziomowym
programowaniu w innych językach z reguły do niczego się w C++ nie przydadzą.
Właściwości wysokiego poziomu są w nim ostrożne, wręcz można powiedzieć,
ubogie. Ale nie są wcale na tyle ubogie, żeby nie dało się w nim programować
na wysokim poziomie. Oczywiście, jeśli ktoś potrzebuje pełnej swobody
programowania wysokopoziomowego, wiele innych języków czeka na niego. Ale
niech wtedy zapomni o pełnej kontroli i przewidywalności wydajności, jaką
zapewni mu język C++ (nie wspominając już o tym, że trudno wśród nich znaleźć
taki język, który udostępnia użytkownikowi tyle paradygmatów programowania, co
C++). Jak pisał Bjarne Stroustrup, konkurentem dla C++ miał być wyłącznie
język C; konfrontacja C++ z językami wysokiego poziomu nie ma za wiele sensu.
Natomiast nie rozumiem, co mają do niego zwolennicy języków niskiego poziomu.
Przecież jeśli ktoś nie chce korzystać z zaawansowanych właściwości C++, to
nic prostszego, niż z nich po prostu nie korzystać. Jeśli ktoś nie chce
koniecznie używać std::string, to wciąż może korzystać z funkcji z string.h i
statycznych tablic. Jeśli ktoś nie chce korzystać z wzorców, tylko woli do
każdego typu danej zrobić osobną funkcję (a może zrobisz to makrodefinicjami,
misiu?), to niech sobie to zrobi nawet i tak, jeśli koniecznie musi. Wolisz
dynamiczne tablice przydzielane przez calloc – to nie korzystaj z std::vector.
Wolisz sam się dłubać w struktury ze wskaźnikiem na samą siebie – nie
korzystaj z std::list. Wolisz wszystkie dane zbiorcze obsługiwać pętlami for
– nie korzystaj ze standardowych algorytmów (napisz jeszcze, misiu,
implementację quicksorta tak, żeby chodziła "od strzału"). Wolisz piętrzące
się sterty instrukcji switch/case i ręczne budowanie obiektów przez wskaźniki
na mniejsze obiekty – nie korzystaj z programowania obiektowego. Wolisz pełną
swobodę operowania fragmentami pamięci, zamiast ścisłego systemu typów –
rzutuj. Czy język C++ narzuca ci sposób tworzenia oprogramowania?
A dodatkowo, jeśli chodzi o oprogramowanie w takich dziedzinach, jak
wspomniane w wypowiedzi, to owo oprogramowanie jest zazwyczaj jednak dość
skomplikowane, składa się z wielu niezależnych części, często tworzonych przez
oddzielne zespoły. W takim czymś koszmarem jest np. nazewnictwo, bo w C nie ma
np. przestrzeni nazw. Tak samo koszmarem jest wieczne ręczne zarządzanie
zasobami wszelkiej maści, koszmarem są możliwości organizowania projektu
ograniczone do prostego, proceduralno-strukturalnego modelu. Tak samo
koszmarem jest konieczność definiowania własnych tzw. "prymitywnych" typów
danych (np. zbiorników). Oczywiście, że takie oprogramowanie może być mimo
wszystko tworzone w C, ale jakim kosztem!
Język C posiada również bardzo słaby system typów i nie posiada żadnego
wsparcia dla typów złożonych (poza zawieraniem pól w strukturach), o ochronie
przed przypadkowym niepoprawnym użyciem nie wspominając. Jeśli się zatem chce
mieć jakiekolwiek bardziej złożone typy danych, najczęściej stosuje się
rzutowanie. Rzutowanie jest absolutnym i całkowicie bezrestrykcyjnym złamaniem
(bo naruszenie to jeszcze za słabe słowo) statycznego systemu typów. Jeśli już
mieć statyczny system typów, to taki, żeby był z niego jakiś pożytek; żeby
rzeczywiście mógł być użyty do tego, do czego jest on faktycznie przeznaczony
– do zapewnienia formalnej poprawności programów. W C ze statycznego
systemu typów pożytek jest praktycznie żaden, jeśli do wykonania jakiejkolwiek
bardziej złożonej operacji trzeba użyć rzutowania. Rzutowanie, poza tym, że
udostępnia właściwości, na jakie nie pozwala statyczny system typów, dodatkowo
"zabezpiecza" przed wykrywaniem wielu potencjalnych błędów, co jest tym
bardziej istotne w złożonym oprogramowaniu, a jeszcze bardziej istotne w
programowaniu samodzielnych urządzeń, czy elementów krytycznych, takich jak
jądra systemów operacyjnych.
Język C zatem nie nadaje się ani do małych projektów (brak w nim podstawowych
składników, używanych w większości małych programów), ani do oprogramowania
niskopoziomowego, czy o dużych wymogach co do niezawodności (bo jest w nim
bardzo łatwo przeoczyć błąd, w porównaniu z C++), a już na pewno nie nadaje
się do oprogramowania złożonego, skomplikowanego, tworzonego przez kilka
niezależnych zespołów (np. dlatego, że nie ma żadnych właściwości
pozwalających na tworzenie komponentów, ani nawet przestrzeni nazw). Kwestie
tworzenia oprogramowania ze skomplikowanym interfejsem to już nawet odrzucają
ci "skłonni do kompromisów". Jeśli poza tymi rodzajami oprogramowania są
jeszcze jakieś rodzaje współcześnie tworzonego oprogramowania, to być może C
się jeszcze do czegoś nadaje.
No dobrze, zgodzę się w ostateczności co do jednej rzeczy – kompilatory
języka C wymagają znacznie mniejszej biblioteki runtime, niż w przypadku C++.
Nie dyskwalifikuje to jednak bynajmniej C++ z zastosowań w takich rzeczach,
jak jądra systemów operacyjnych i "embedded systems". Inteligentne systemy
kompilacji potrafią wyciągnąć z biblioteki standardowej tylko te rzeczy, które
rzeczywiście są używane, a dodatkowo w bibliotece standardowej C++ większość
znajduje się w plikach nagłówkowych. Na dodatek zawierają rzeczy, które w
przypadku języka C trzeba by napisać samemu i otrzymać wcale nie mniej kodu do
łączenia.
Tak na marginesie wspomnę jeszcze o drobnym zdarzeniu, które może nie
"wstrząsnęło opinią publiczną", ale narobiło trochę zamieszania. Na pewnej
stronie www (aktualnie polskie tłumaczenie jest dostepne na stronie Anubis –
nawiasem mówiąc, ten tekst ciągle zmienia lokalizację, ale da się go znaleźć
googlami bez większych problemów) ukazał się wywiad, którego (podobno)
udzielił Bjarne Stroustrup pewnemu pismu komputerowemu. Już na samym wstępie
dowiadujemy się tam, że IBM swego czasu zrobił głupotę i wykształcił sporą
rzeszę programistów, przez co stracili oni na wartości i wielu z nich musiało
sobie poszukać innego zajęcia (a jednym z nich jest redaktor prowadzący ten
wywiad). Bjarne Stroustrup zaś przekazuje szokujące informacje o tym, że
wymyślił C++ jako język w założeniu trudny, zamieszany, skomplikowany i nie
dający się opanować (właśnie po to, żeby programiści mieli pracę). Na dodatek
przytacza kilka przykładów firm, które przenosiły projekty do C++ i omal nie
zbankrutowały; rzuca też tekst w stylu "Widział pan żeby kiedykolwiek jakaś
firma ponownie używała swego kodu?". C++ służy zatem tylko zwiedzeniu
programistów tak, aby ci "prawdziwi" (czyli piszący tylko w C) pozostali, a
ich wartość została doceniona. Na końcu jest wzmianka też o referencjach:
pewien jego znajomy miał problemy z pamiętaniem, co przekazywał do funkcji,
wartość czy zmienną, i że ten operator zawsze mu o tym przypominał (czyli:
referencje, choć wydają się fajne, to w gruncie rzeczy też są do dupy, jak i
cały C++). Niefortunnie niestety wspomniał o jednym jedynym przypadku użycia
referencji, którego w programowaniu w C++ akurat się unika (ale żeby wiedzieć
o tym, to też trzeba by trochę w C++ popisać).
Podobnych "wpadek" w całym tym tekście jest od groma (i na szczęście, jest już
teraz – jak w przytoczonym linku – zamieszczana na stronach typu "śmieszne
teksty") – jak np. że używanie wyjątków do zgłaszania braku pamięci może
powodować mnóstwo problemów i lepiej polegać na wartości zwracanej przez
malloc. Autorem tego tekstu musiał być jakiś sfrustrowany fan języka C, bo owe
wpadki dobitnie świadczą o tym, że autor sam nie wie, o czym pisze. Z tego co
pamiętam na pl.comp.lang.c wielu ludzi było skłonnych uwierzyć w prawdziwość
tego wywiadu, niestety tak naprawdę poza drobną prowokacją (oraz
samokompromitacją) autor niczego sensownego nie wniósł. Na końcu wywiadu
Bjarne Stroustrup rzekomo stwierdza, że nie jest wcale tak źle, C++ aktualnie
wymiera, a programiści, którzy twardo trzymali się C i nie dali się nabrać na
C++, przetrwają.
Niestety, wbrew temu twierdzeniu, jak na razie C++ ma się całkiem dobrze, a
właśnie język C sprowadzono do roli języka o wąskiej specjalizacji (jako
przenośny asembler o łatwym do napisania kompilatorze; częściej się go
wykorzystuje do unikalnych platform, niż do programowania komputerów).
Właściwości C++ dzięki swej elastyczności pozwalają na stosowanie coraz
większej ilości stylów programowania, zatem wybór języka programowania powoli
przestaje mieć znaczenie (niewiele jest takich języków, w których coś można
zrobić "lepiej" niż w C++ – pomijając oczywiście języki skryptowe). Jedynie
języki wąsko wyspecjalizowane w niektórych konkretnych zastosowaniach
sprawdzają się lepiej (aczkolwiek takich "wąskich" specjalizacji też jest tyle
co kot napłakał). Z kolei biblioteka STL oraz wprowadzone przez nią
funkcjonały dały początek programowaniu funkcjonalnemu w C++, co zostało
znacznie rozwinięte przez bibliotekę BOOST, w szczególności słynne "bind",
"function", a na dodatek jeszcze lambda (o dość ograniczonym zastosowaniu, ale
dość przydatne) oraz spirit (generator parserów definiowany wprost w C++).
Powoduje to, że C++ nadaje się już do takich zastosowań, które były wcześniej
zajęte przez języki w rodzaju klonów ML-a. W C++ powstało również wiele innych
niezależnych bibliotek, wśród nich jedną z bardziej wartych uwagi jest Qt produkowana przez TrollTech – wieloplatformowa biblioteka
aplikacji okienkowych (przypominam tylko, że przez długi czas palmę
pierwszeństwa w tych zastosowaniach trzymał Smalltalk, a "najszybciej" okienka
robiło się w Tk). Mimo entuzjastycznego nastawienia różnych wielbicieli innych
języków programowania (w tym również C), jak na razie C++ jest bezwzględnie na
pierwszym miejscu jeśli chodzi o ilość produkowanego komercyjnego
oprogramowania (w OpenSource i GNU podobno nieznacznie więcej powstaje w C,
ale w obu tych językach powstaje ok. 95% tego oprogramowania). Jak na razie
również nie istnieje i nie szykuje się w najbliższym czasie żadna alternatywa
dla C++, ani żaden język, który mógłby go zastąpić (Java nie miała żadnych
szans konkurować z C++ ze względu na swoją powolność; również osławiony C#
miał być w założeniu językiem do pokonania Javy, a bynajmniej nie do
konkurencji z C++; zresztą Micro$oft najwyraźniej ma zamiar inwestować w C++,
skoro zatrudnił Stana Lippmana).
A co ze wspomnianymi programistami? No cóż; to fakt, że C++ nie jest językiem
łatwym. Ale jak na razie uczenie się tego języka (pod warunkiem oczywiście, że
kogoś programowanie naprawdę interesuje) jest najlepszą inwestycją w naukę i to
przynoszącą całkiem pokaźne zyski. I paradoksalnie to właśnie dobrzy programiści
C++ są dziś bardzo poszukiwani (a doświadczeni programiści C to ludzie, którym
najtrudniej dostosować się do C++). Może właśnie to zainspirowało autora
wspomnianego wywiadu? A język, choć popularny, to jednak żeby był nawet
najwyśmienitszy, to nie zastąpi nikomu smykałki do programowania, pojęcia o
regułach programowania, algorytmice, a także umiejętności utrzymywania
czytelności i przejrzystości kodu oraz jego efektywności. I tacy programiści
raczej nie potrzebują żadnych specjalnych zabiegów, żeby łatwiej dostać pracę.
1. Język C jest prostszy od C++, więc łatwiej się go nauczyć
2. Język C++ ma obciążenia w stosunku do C, więc programy w nim będą
wolniejsze
3. Język C jest najszybszy, bo jest bliski asemblera
najwięcej zasobów podczas tworzenia
oprogramowania idzie na jego jakość.
ilość potencjalnych pomyłek jest równa
ilości okazji do ich popełnienia.
jeden błędny fragment kodu przekopiowany 10
razy daje 10 błędnych fragmentów kodu.
aby zmniejszyć ilość pomyłek, trzeba
zmniejszyć ilość kodu, nie zmieniając jego funkcjonalności.
najłatwiejszym możliwym sposobem
generycyzacji kodu jest zastosowanie dynamizmu.
dynamizm kosztuje.
C++ umożliwia hardkodowanie bez utraty
generyzmu,
W języku C generyzm uzyskuje się za
pośrednictwem dynamizmu, który stanowi narzut podczas wykonania, gdy w C++
używając wzorców można uzyskać to samo, bez narzutu podczas wykonania.
4. Język C jest lepszy dla małych projektów i programowania na niskim
poziomie
Język C++ zawiera, owszem, wiele wypasionych możliwości, więc na pewno
bardzo się przyda w bardzo skomplikowanych projektach, gdzie korzysta się z
rozbudowanych danych, również jeśli musimy zrobić jakieś skomplikowane GUI.
Natomiast w tej małej aplikacji nie ma potrzeby angażowania aż C++, można to
również napisać w C.
Język C++ jest językiem wyższego poziomu, niż język C, więc więcej rzeczy
dzieje się tam poza kontrolą programisty. W języku C nic się nam nie wymknie i
wszystko mamy pod kontrolą.
W programowaniu na wysokim poziomie, gdy mamy do czynienia z rozbudowanymi,
skomplikowanymi aplikacjami komputerowymi, które mają złożoną obsługę, na
pewno jest miejsce dla C++. Nie ma natomiast miejsca dla C++ w oprogramowaniu
na bardzo niskim poziomie – takim jak sterowniki urządzeń, jądro systemu
operacyjnego, czy jego moduły, oprogramowanie dla kas fiskalnych, czy
dekoderów telewizji satelitarnej.