W Kiedy używać C zamiast C ++, a C ++ ponad C? istnieje oświadczenie wrt. do rozmiaru kodu / wyjątków C ++:
Odpowiedzi Jerry (między innymi):
(...) trudniej jest tworzyć naprawdę małe pliki wykonywalne w C ++. W naprawdę małych systemach i tak rzadko piszesz dużo kodu, a dodatkowe (...)
na co zapytałem, dlaczego tak jest, na co Jerry odpowiedział:
najważniejsze jest to, że C ++ obejmuje obsługę wyjątków, co (przynajmniej zwykle) dodaje pewne minimum do rozmiaru pliku wykonywalnego. Większość kompilatorów pozwoli ci wyłączyć obsługę wyjątków, ale kiedy to zrobisz, wynik nie jest już całkiem C ++. (...)
czego tak naprawdę nie wątpię w technicznym świecie rzeczywistym.
Dlatego jestem zainteresowany (wyłącznie z ciekawości), aby usłyszeć z przykładów ze świata rzeczywistego, w których projekt wybrał C ++ jako język, a następnie postanowił wyłączyć wyjątki. (Nie tylko „nie używaj” wyjątków w kodzie użytkownika, ale wyłącz je w kompilatorze, abyś nie mógł rzucać ani wychwytywać wyjątków.) Dlaczego projekt zdecydował się to zrobić (nadal używa C ++, a nie C, ale nie wyjątki) - jakie są / były (techniczne) przyczyny?
Dodatek: Dla tych, którzy chcą rozwinąć swoje odpowiedzi, byłoby miło opisać, w jaki sposób obsługiwane są implikacje braku wyjątków:
- Kolekcje STL (
vector
, ...) nie działają poprawnie (nie można zgłosić niepowodzenia alokacji) new
nie można rzucać- Konstruktory nie mogą zawieść
źródło
Odpowiedzi:
Prawie każda gra konsolowa dostępna w C ++ z wyjątkiem wyłączonym, nawet dzisiaj. W obliczu jest to domyślna konfiguracja kompilatorów C ++ ukierunkowanych na te konsole. Czasami nie można zagwarantować, że niektóre funkcje C ++ działają poprawnie na tych kompilatorach, np. Wielokrotne dziedziczenie (mam na myśli bardzo dobrze znany domyślny kompilator konsoli).
Kolejnym przykładem jest sprzętowy zestaw SDK Arduino usnig gcc bez wyjątku aktywowany w C ++ i inne rzeczy, takie jak brak STL .
Istnieje kilka technicznych powodów, dobrych lub złych, cokolwiek, to nie moja rada, ale powody, które słyszałem:
Myślę, że wyjątki mogą być przydatne nawet w grach, ale to prawda, że w grach konsolowych nie jest to tak naprawdę przydatne.
Aktualizacja:
Dodaję kolejny zaskakujący przykład: LLVM / CLang nie używa wyjątku ani RTTI z następujących powodów :
CLang jest dobrze znany ze swojej szybkości kompilacji i wyraźnych błędów, ale także z jednego rzadkiego kompilatora, który ma naprawdę łatwy do śledzenia kod.
źródło
Jerry powiedział: ... wynik nie jest już całkowicie C ++ , podczas gdy moja metafora jest taka, że jest to oczywiście C ++, tylko nieco inny dialekt, ponieważ programy wykorzystują inne formy, konwencje i style pisane.
Oto moje główne powody wyłączenia:
Kompatybilność binarna
Przekraczanie granic języka i tłumaczeń nie jest powszechnie dobrze zdefiniowane ani niezdefiniowane. Jeśli chcesz zagwarantować, że Twój program działa w domenie określonego zachowania, musisz poddać kwarantannie wyjątki w punktach wyjścia modułu.
Rozmiar pliku wykonywalnego
Oto binarne rozmiary programu bez wyjątków, który napisałem, zbudowałem bez włączonych wyjątków:
Bez wyjątków:
Z wyjątkami:
Przypomnienie: jest to zbiór bibliotek i programów, które zawierają zerowe rzuty / połowy. Flaga kompilator robi umożliwić wyjątków w C ++ biblioteki standardowej. Dlatego koszt w prawdziwym świecie jest większy niż 19% w tym przykładzie.
Kompilator: apple gcc4.2 + llvm. Rozmiary w MB.
Prędkość
Pomimo terminu „wyjątki zerowego kosztu” wciąż dodają pewne koszty ogólne, nawet jeśli nic się nie rzuca. W powyższym przypadku jest to program krytyczny pod względem wydajności (przetwarzanie sygnałów, generowanie, prezentacja, konwersje, z dużymi zestawami danych / sygnałami itp.). Wyjątki nie są konieczną funkcją w tym projekcie, a wydajność jest bardzo ważna.
Poprawność programu
Wydaje się to dziwnym powodem ... Jeśli rzucanie nie jest opcją, musisz napisać stosunkowo surowe, poprawne, dobrze przetestowane programy, aby zagwarantować, że Twój program działa poprawnie, a klienci poprawnie używają interfejsów (jeśli podasz mi zły argument lub zrobisz to nie sprawdzaj kodu błędu, to zasługujesz na UB). Wynik? Jakość wdrożenia znacznie się poprawia, a problemy szybko się naprawiają.
Prostota
Implementacje obsługi wyjątków nie są często aktualizowane. Dodają również dużo złożoności, ponieważ implementacja może mieć wiele wiele sekwencji wyjścia. Łatwiej jest czytać i obsługiwać bardzo złożone programy, gdy używają niewielkiego zestawu dobrze zdefiniowanych, wpisywanych strategii wyjścia, które pojawiają się i są obsługiwane przez klienta. W innych przypadkach implementacje mogą z czasem wprowadzić więcej rzutów lub ich zależności mogą je wprowadzić. Klienci nie mogą łatwo ani odpowiednio bronić się przed wszystkimi tymi wyjściami. Piszę i aktualizuję wiele bibliotek, często ewoluuję i ulepszamy. Próba utrzymywania tego w synchronizacji z wyjątkowymi sekwencjami wyjściowymi (w dużej bazie kodu) nie byłaby dobrym wykorzystaniem czasu i prawdopodobnie spowodowałaby dużo hałasu i kłopotów. Ze względu na zwiększoną poprawność programu i więcej testów,
Historia / istniejący kod
W niektórych przypadkach nigdy nie zostały wprowadzone z powodów historycznych. Istniejąca baza kodów nie korzystała z nich, zmiana programów mogła zająć wiele lat i uczynić je naprawdę brzydkimi z powodu nakładania się konwencji i implementacji.
Wady
Oczywiście są wady, największe to: Niezgodność (w tym binarna) z innymi bibliotekami oraz fakt, że będziesz musiał zaimplementować dużą liczbę programów, aby pasowały do tego modelu.
źródło
malloc
Zwroty0
), alokator wchodzi wwhile (1)
tryb, który ma przełącznik kontekstu, po czym następuje kolejna próba alokacji. W tej bazie kodu było tak, że nowy za pośrednictwem alokatora mógł zwrócić 0, ale nowsza implementacja działała dobrze. (ciąg dalszy)Google nie akceptuje wyjątków w Przewodniku po stylach C ++ , głównie z powodów historycznych:
(Nacisk redakcji)
źródło
Qt prawie nigdy nie używa wyjątków. Błędy w Qt są oznaczone kodami błędów i sygnałami. Oficjalnie podanym powodem jest:
Dzisiaj powszechną krytyką Qt jest to, że bezpieczeństwo wyjątków nie jest pełne.
źródło
Symbian C ++ (używany w niektórych telefonach Nokia) nie korzysta z wyjątków, przynajmniej nie bezpośrednio, ponieważ kompilatory C ++ nie wdrożyły ich niezawodnie, gdy Symbian został opracowany po raz pierwszy.
źródło
Ja / nigdy / używam wyjątków. Istnieje wiele powodów, ale dwa główne powody to to, że nigdy nie potrzebowałem ich do stworzenia solidnego kodu i że zmniejszają one wydajność w czasie wykonywania.
Pracowałem nad kodem produkcyjnym, który zarówno używa, jak i wyłącza wyjątki - kod, który dopuszczał wyjątki, był jednakowo gorszy. W niektórych miejscach zastosowano wyjątki do prawdziwej kontroli przepływu, a nie do obsługi błędów, co jest bardzo ciężkie, antykonkurencyjne i trudne do debugowania. Ogólnie problemy z debugowaniem w kodzie wypełnionym wyjątkiem były trudniejsze niż w kodzie bez wyjątku - częściowo ze względu na stos i wewnętrzne trudności mechanizmu wyjątku, ale znacznie więcej niż to z powodu leniwego kodu, który był zachęcany jako wynik dostępności obsługi wyjątków.
W samych wyjątkach nie ma nic poważnego źle / jeśli nie zależy ci na wydajności / i nie masz czasu, aby zrobić coś właściwie - są one językową funkcją obsługi błędów i dobrym zamiennikiem właściwego mechanizmu obsługi błędów. Są jednak prawie zawsze / lepsze / sposoby radzenia sobie z błędami - tak jak w przypadku własnej logiki (trudno to rozwinąć - jest to prawie zdrowy rozsądek). Jeśli to nie twój błąd, ale pochodzi z biblioteki (np. Biblioteki standardowej), musisz uszanować wybór wyjątków lub mieć awarię, ale zawsze kwestionowałbym ten wybór. Nigdy nie widziałem sytuacji, w której wyjątki byłyby rzeczywiście najlepszym rozwiązaniem.
Asercje są bardziej debugowalne, kody błędów mają mniejszą wagę ... między nimi, jeśli są właściwie używane, łatwiej jest czytać, debugować i utrzymywać kod, który jest szybszy. Wygrywa cały ...
źródło
W standardzie kodowania Joint Strike Fighter C ++ autorstwa Bjarne i in. al. wyjątki są zakazane ze względu na trudne wymagania myśliwców w czasie rzeczywistym.
Cytat z Bjarne C ++ FAQ .
Pamiętaj, że C ++ prawdopodobnie obsługuje najszerszą gamę oprogramowania ze wszystkich języków ...
źródło
Jest to dość ważne w projektowaniu bibliotek C ++. Często z interfejsem C trochę nieprzyjemnie jest wyrzucać klientowi wyjątek z biblioteki innej firmy. Chodzi o to, że jeśli twoja biblioteka rzuca się, odcinasz grupę klientów, którzy z niej skorzystaliby, biorąc pod uwagę, że miała ona gwarancję braku rzucania (z dowolnego powodu, dla którego klient ma ograniczenie wyjątków).
Osobiście widziałem przypadki nadużywania wyjątków, gdy zespołowi polecono „wrzucić wyjątek”, ilekroć dzieje się coś nie tak strasznego. Oczywiście widzisz tutaj błąd - wyjątek został zgłoszony w kodzie, zanim ktokolwiek wymyślił, co z tym zrobić. Ten projekt miał kilka awarii, ponieważ te głębokie rzuty czasami się wycofywały.
źródło
Być może pod tym względem warto wspomnieć o Embedded C ++ . Embedded C ++ to odmiana C ++ zaprojektowana (oczywiście wystarczająco) dla systemów osadzonych. Jest to w zasadzie właściwy podzbiór C ++, z usuniętymi (między innymi) szablonami, przestrzeniami nazw i obsługą wyjątków.
Powinienem dodać, że chociaż EC ++ zachwiał się odrobinę, gdy był nowy, wydaje się, że w większości ucichły. Nie jestem pewien, czy ludzie stracili zainteresowanie, czy też ich pierwsza próba była po prostu tak doskonała, że nikt nie widział powodu, by się tym zajmować przez dekadę.
<closed captioning for the humor impaired>Yeah, right!</closed captioning>
źródło
Dobrym przykładem jest programowanie w trybie jądra.
Możesz użyć C ++ tam dla czystszego kodu, ale bez wyjątków. Nie ma dla nich biblioteki wykonawczej, a obsługa wyjątków używa zbyt dużej pamięci stosu, która jest bardzo ograniczona w jądrze (eksperymentowałem z wyjątkami C ++ w jądrze systemu Windows NT, a rzucanie i odwijanie wyjątków zjada połowę dostępnego stosu - bardzo łatwo uzyskać przepełnienie stosu i zawiesić cały system).
Zazwyczaj definiujesz własne
new
idelete
operatorów. Znalazłem też całkiem przydatneplacement new
odniesienia do wartości rc i a +++. Nie można używać STL ani innych bibliotek trybu użytkownika C ++, które opierają się na wyjątkach.źródło
Gdy próbujesz utrzymać pamięć wykonywalną w dół. Zwykle wykonywane w systemach wbudowanych (lub mobilnych). W przypadku aplikacji komputerowych nie jest to nigdy potrzebne, jednak na serwerach można rozważyć, czy chcesz jak najwięcej pamięci RAM dla serwera WWW lub SQL.
źródło