Najgorsze praktyki w C ++, typowe błędy [zamknięte]

35

Po przeczytaniu tej słynnej wypowiedzi Linusa Torvaldsa zastanawiałem się, jakie właściwie są wszystkie pułapki dla programistów w C ++. Nie mówię wyraźnie o literówkach lub błędnym przepływie programu, o których mowa w tym pytaniu i odpowiedziach , ale o więcej błędów wysokiego poziomu, które nie są wykrywane przez kompilator i nie powodują oczywistych błędów przy pierwszym uruchomieniu, kompletnych błędów projektowych, rzeczy, które są nieprawdopodobne w C, ale prawdopodobnie zostaną wykonane w C ++ przez początkujących, którzy nie rozumieją pełnych implikacji swojego kodu.

Z zadowoleniem przyjmuję również odpowiedzi wskazujące na ogromny spadek wydajności, w przypadku którego zwykle nie można tego oczekiwać. Przykład tego, co jeden z moich profesorów powiedział mi kiedyś o generatorze analizatora składni LR (1), który napisałem:

Użyłeś nieco zbyt wielu przypadków niepotrzebnego dziedziczenia i wirtualności. Dziedziczenie sprawia, że ​​projekt jest znacznie bardziej skomplikowany (i nieefektywny z powodu podsystemu RTTI (wnioskowanie typu w czasie wykonywania)), dlatego powinien być używany tylko tam, gdzie ma to sens, np. W przypadku działań w tabeli analizy. Ponieważ intensywnie korzystasz z szablonów, praktycznie nie potrzebujesz dziedziczenia ”.

Felix Dombek
źródło
6
Niektóre z bardziej paskudnych błędów, które możesz popełnić w C / C ++, są głównie spowodowane dziedzictwem C ... czytaj niezdefiniowane zachowanie, ręczne zarządzanie pamięcią itp. Również porady profesora wydają się fałszywe / błędne (dla mnie, kto nie jest ekspert C ++) - tworzenie instancji szablonu powinno dać normalną klasę z vtable dla virtualfunkcji, prawda?
8
Albo źle pamiętasz, co powiedział twój profesor, albo nie miał pojęcia, o czym mówił. Klasy pochodne na ogół nie muszą używać RTTI (odbicie AKA), aby sprawdzić sytuację. Jeśli używają metod wirtualnych, kod może potrzebować wyszukać vtable dla wysyłki, ale przekłada się to na pojedynczą instrukcję ASM na wielu procesorach. Z powodu problemów z pamięcią podręczną może spowolnić działanie o określoną wartość, ale prawdopodobnie nie zauważysz narzutu w żadnym z najbardziej wymagających przypadków użycia. Istnieje wiele dobrych powodów, aby unikać C ++, ale wyszukiwania vtable nie są jednym z nich.
Mason Wheeler,
5
@ FelixDombek: Podane tak ogólnie i stosowane w całym tekście, że cytat profesora pokazuje po prostu ogromną ignorancję. Gdy twój projekt wymaga pewnego rodzaju polimorfizmu w czasie wykonywania, użycie funkcji wirtualnych jest często najlepszym wyborem; kiedy go nie potrzebujesz, nie używaj go: nie potrzebujesz wszystkich metod, aby być wirtualnym tylko dlatego, że na przykład używasz klas pochodnych.
Fred Nurk
5
@Mason Wheeler: RTTI zawiera informacje o typie, wystarczające do ustalenia, czy a dynamic_castpowinno się udać, czy też nie, i kilka innych rzeczy, ale refleksja obejmuje znacznie więcej, w tym możliwość pobierania informacji o atrybutach lub funkcjach członka, które nie są obecny w C ++.
David Rodríguez - dribeas
5
Komentarz profesora jest nieco mylący, ponieważ dziedziczenie i funkcje wirtualne nie są wielkim hitem wydajnościowym. Porada dotycząca oszczędnego korzystania z dziedziczenia jest dobra, ale bardziej dotyczy to struktury programu niż wydajności. Dziedziczenie, szczególnie w przypadku członków chronionych, jest tak bliskie sprzężeniu, jak to tylko możliwe, a jeśli go nie potrzebujesz, nie powinieneś go używać.
David Thornley,

Odpowiedzi:

69

Torvalds mówi tutaj ze swojego tyłka.


OK, dlaczego mówi ze swojego tyłka:

Po pierwsze, jego rant to tak naprawdę nic, ALE rant. Tutaj jest bardzo mało rzeczywistej treści. Jedynym powodem, dla którego jest naprawdę sławny lub nawet szanowany, jest to, że został stworzony przez boga Linuxa. Jego głównym argumentem jest to, że C ++ to bzdura i lubi wkurzać ludzi C ++. Oczywiście nie ma żadnego powodu, aby na to odpowiadać, a każdy, kto uważa to za rozsądny argument, i tak jest poza rozmową.

Co do tego, co można zaliczyć jako jego najbardziej obiektywne punkty:

  • STL i Boost są kompletną bzdurą. Jesteś idiotą.
  • STL i Boost powodują nieskończoną ilość bólu <- śmieszne. Oczywiście celowo przesadza, ale jakie jest jego prawdziwe stwierdzenie? Nie wiem Podczas wywoływania wymiotów kompilatora w programie Spirit lub czegoś innego istnieje trochę bardziej niż trywialnie trudna do wykrycia problem, ale nie jest to trudniejsze do wykrycia niż debugowanie UB spowodowane niewłaściwym użyciem konstruktorów C, takich jak void *.
  • Modele abstrakcyjne wspierane przez C ++ są nieefektywne. <- Jak co? On nigdy się nie rozwija, nigdy nie podaje żadnych przykładów tego, co ma na myśli, po prostu to mówi. BFD. Ponieważ nie mogę powiedzieć, o czym mówi, próba „obalenia” oświadczenia nie ma sensu. Jest to powszechna mantra bigotów C, ale to nie czyni jej bardziej zrozumiałym lub zrozumiałym.
  • Prawidłowe użycie C ++ oznacza ograniczenie się do aspektów C. <- W rzeczywistości robi to kod WORSE C ++, więc wciąż nie wiem o czym mówi WTF.

Zasadniczo Torvalds mówi ze swojego tyłka. Nie powstaje żaden zrozumiały argument. Oczekiwanie poważnego odparcia takich bzdur jest po prostu głupie. Mówi mi się, żebym „rozwijał się” w odpowiedzi na coś, co miałbym rozwinąć, gdyby to było to, co powiedziałem. Jeśli naprawdę, szczerze spojrzysz na to, co powiedział Torvalds, zobaczysz, że tak naprawdę nic nie powiedział.

To, że Bóg mówi, że to nie znaczy, że ma to jakiś sens lub powinno być traktowane poważniej niż gdyby to powiedział jakiś przypadkowy bozo. Prawda jest taka, że ​​Bóg jest kolejnym przypadkowym bozo.


Odpowiadając na aktualne pytanie:

Prawdopodobnie najgorsza i najczęstsza zła praktyka C ++ polega na traktowaniu jej jak C. Dalsze korzystanie z funkcji C API, takich jak printf, dostaje (również uważane za złe w C), strtok itp. Nie tylko nie udaje się wykorzystać dostarczonej mocy dzięki ciasnemu systemowi typów nieuchronnie prowadzą do dalszych komplikacji podczas próby interakcji z „prawdziwym” kodem C ++. Zasadniczo więc rób dokładnie odwrotność tego, co radzi Torvalds.

Naucz się korzystać z STL i Boost, aby uzyskać dalsze wykrywanie błędów w czasie kompilacji i ułatwić Ci życie na inne, ogólne sposoby (na przykład tokenizer doładowania jest zarówno bezpieczny dla typu ORAZ lepszy interfejs). To prawda, że ​​musisz nauczyć się czytać błędy szablonu, co na początku jest zniechęcające, ale (z mojego doświadczenia i tak) jest to o wiele łatwiejsze niż próba debugowania czegoś, co generuje niezdefiniowane zachowanie w czasie wykonywania, co powoduje interfejs API dość łatwe do zrobienia.

Nie mówię, że C nie jest tak dobry. Oczywiście bardziej lubię C ++. Programiści C lubią C lepiej. W grze występują kompromisy i subiektywne upodobania. Istnieje również wiele dezinformacji i FUD. Powiedziałbym, że jest więcej FUD i dezinformacji krążących wokół C ++, ale jestem stronniczy w tym względzie. Na przykład problemy z „wzdęciem” i „wydajnością” w C ++ prawdopodobnie nie są tak naprawdę większymi problemami i na pewno są wyrzucone z proporcji rzeczywistości.

Jeśli chodzi o problemy, o których mówi profesor, nie są one unikalne dla C ++. W OOP (i w programowaniu ogólnym) wolisz kompozycję niż dziedziczenie. Dziedziczenie jest najsilniejszą możliwą relacją sprzężenia, która istnieje we wszystkich językach OO. C ++ dodaje jeszcze jedną, silniejszą, przyjaźń. Dziedziczenie polimorficzne powinno być stosowane do reprezentowania abstrakcji i relacji „jest-a”, nigdy nie powinno być wykorzystywane do ponownego użycia. Jest to drugi największy błąd, jaki możesz popełnić w C ++, i jest to dość duży błąd, ale nie jest on unikalny dla języka. Możesz również tworzyć zbyt złożone relacje dziedziczenia w języku C # lub Java, a będą one miały dokładnie takie same problemy.

Edward Strange
źródło
1
Ironiczne, jak długo po 2007 roku, git uruchomił tylko przenośne wersje Linuksa. Cóż, każdy system, który był podobny do unixa. Z drugiej strony, biorąc pod uwagę okoliczności, które doprowadziły do ​​powstania gita, z pewnością nie mam tego przeciwko niemu.
Chris K
9
Linusowi trudno jest znaleźć dobrych programistów C ++, którzy chcieliby dla niego pracować. Zastanawiać się, dlaczego? Myślę, że to tylko problem z kurczakiem i jajkiem.
Bo Persson
19

Zawsze uważałem, że niebezpieczeństwa związane z C ++ były bardzo przesadzone przez niedoświadczonych programistów C z Classes.

Tak, C ++ jest trudniejszy do pobrania niż coś takiego jak Java, ale jeśli programujesz przy użyciu nowoczesnych technik, dość łatwo jest napisać solidne programy. Szczerze mówiąc, nie mam o wiele trudniejszego programowania w C ++ niż w językach takich jak Java i często brakuje mi niektórych abstrakcji C ++, takich jak szablony i RAII, kiedy projektuję w innych językach.

To powiedziawszy, nawet po latach programowania w C ++, co jakiś czas popełniam naprawdę głupi błąd, który nie byłby możliwy w języku wyższego poziomu. Częstą pułapką w C ++ jest ignorowanie czasu życia obiektu: w Javie i C # na ogół nie musisz się martwić o czas życia obiektu *, ponieważ wszystkie obiekty istnieją na stercie i są zarządzane dla ciebie przez magiczny śmieciarz.

Teraz, we współczesnym C ++, zwykle nie musisz również przejmować się czasem życia obiektu. Masz destruktory i inteligentne wskaźniki, które zarządzają dla ciebie czasem życia obiektów. W 99% przypadków działa to wspaniale. Ale od czasu do czasu zostaniesz wkręcony przez wiszący wskaźnik (lub odniesienie). Na przykład niedawno miałem obiekt (nazwijmy go Foo), który zawierał wewnętrzną zmienną odniesienia do innego obiektu (nazwijmy go Bar). W pewnym momencie głupio zaaranżowałem rzeczy tak, że wcześniej nie Barwchodziły w zakres Foo, ale Foodestruktor ostatecznie wywołał funkcję członka Bar. Nie trzeba dodawać, że sprawy nie potoczyły się dobrze.

Teraz nie mogę winić za to C ++. To był mój zły projekt, ale chodzi o to, że tego rodzaju rzeczy nie miałyby miejsca w zarządzanym języku wyższego poziomu. Nawet z inteligentnymi wskaźnikami i tym podobnymi czasami trzeba mieć świadomość istnienia obiektu przez cały czas.


* Jeśli zarządzanym zasobem jest pamięć, to znaczy.

Charles Salvia
źródło
8
Nigdy tak naprawdę nie musisz się martwić o trwałość obiektu w Javie i C #? Ich GC dba o pamięć, ale dla mnie to tylko niewielka część RAII; na przykład różne „jednorazowe” interfejsy, które mają te języki.
Fred Nurk
Konieczność dbania o żywotność obiektu byłaby rzadka w Javie, z wyjątkiem niewygodnego projektu jego biblioteki we / wy.
dan04
Twój ciekawy problem referencyjny jest czymś, co chcę rozwiązać. Rozpocząłem dyskusję na swoim blogu na temat kierunku, w którym zmierzam do jej rozwiązania (wskaźnik obiecuje). Zasadniczo myślę, że język mógłby użyć jeszcze kilku inteligentnych wskaźników. Weź udział w dyskusji, jeśli jesteś zainteresowany. Nikt inny nie był taki, cokolwiek ... ale jeśli jest to coś, co chciałbyś zobaczyć rozwiązany ... Właściwie to wpadam na ten problem przez ponad 10% czasu.
Edward Strange
13

Różnica w kodzie jest zwykle bardziej związana z programistą niż z językiem. W szczególności dobry programista C ++ i programista C dojdą do podobnie dobrych (nawet jeśli różnych) rozwiązań. Teraz C jest prostszym językiem (jako językiem), co oznacza, że ​​jest mniej abstrakcji i lepsza widoczność tego, co faktycznie robi kod.

Część jego rantowania (jest znany ze swoich rantów przeciwko C ++) opiera się na fakcie, że więcej osób przejmie C ++ i napisze kod, nie rozumiejąc, co kryją niektóre abstrakcje i nie przyjmują błędnych założeń.

David Rodríguez - dribeas
źródło
3
Jaki jest koszt iteracji po std::vector<bool>zmianie każdej wartości? for ( std::vector<bool>::iterator it = v.begin(), end = v.end(); it != end; ++it ) { *it = !*it; }? Co jest abstrakcyjne *it = !*it;?
David Rodríguez - dribeas
2
Chociaż wybieranie konkretnych obrzydliwości językowych, krytykowanych jako błędy, może być niesprawiedliwe ...
Fred Nurk
2
@Fred Nurk: std::vector<bool>jest znanym błędem, ale jest to naprawdę dobry przykład tego, co jest omawiane: abstrakcje są dobre, ale musisz uważać na to, co kryją. To samo może i stanie się w kodzie użytkownika. Na początek widziałem zarówno w C ++, jak i Javie, którzy używają wyjątków do kontroli przepływu, i kod, który wygląda jak wywołanie funkcji zagnieżdżenia, które jest tak naprawdę programem uruchamiającym wyjątki ratunkowe: void endOperation();zaimplementowane jako throw EndOperation;. Dobry programista uniknie tych zaskakujących konstrukcji, ale faktem jest, że można je znaleźć.
David Rodríguez - dribeas
5
Jednym z punktów Torvalds jest to, że: może odepchnąć początkujących po prostu wybierając C zamiast C ++ (wydaje się, że jest więcej początkujących C ++), a bardziej złożone C ++ ma bardziej stromą krzywą uczenia się i są większe szanse na potknięcie się w przypadku narożnika .
David Rodríguez - dribeas
2
+1, dokładnie na to narzeka Linus. Wygląda na anty-C ++, ale tak nie jest. Jest tylko programistą anty-C ++.
greyfade
13

Nadużywanie try/catchbloków.

File file("some.txt");
try
{
  /**/

  file.close();
}
catch(std::exception const& e)
{
  file.close();
}

Zwykle wynika to z języków takich jak Java i ludzie będą argumentować, że w C ++ brakuje finalizeklauzuli.

Ale ten kod zawiera dwa problemy:

  • Trzeba zbudować fileprzed try/catch, ponieważ nie można tak naprawdę closepliku, który nie istnieje catch. Prowadzi to do „przecieku lunety”, który filejest widoczny po zamknięciu. Możesz dodać blok, ale ...: /
  • Jeśli ktoś podejdzie i doda znak returnpośrodku tryzakresu, plik nie zostanie zamknięty (dlatego ludzie dziwią się z powodu braku finalizeklauzuli)

Jednak w C ++ mamy znacznie wydajniejsze sposoby radzenia sobie z tym problemem, które:

  • Java finalize
  • C # using
  • Idź S defer

Mamy RAII, którego naprawdę interesującą właściwość najlepiej podsumować jako SBRM(Scoped Bound Resources Management).

Tworząc klasę w taki sposób, aby jej destruktor oczyścił zasoby, które posiada, nie nakładamy na siebie obowiązku zarządzania zasobami na każdego użytkownika!

To cecha tęsknię w każdym innym języku, i prawdopodobnie ten, który jest najbardziej zapomniane.

Prawda jest taka, że ​​rzadko trzeba nawet pisać try/catchblok w C ++, z wyjątkiem najwyższego poziomu, aby uniknąć zakończenia bez logowania.

Matthieu M.
źródło
1
Nie sądzę, że ma to wpływ na Javę w takim stopniu, jak na C. (możesz bezpośrednio zastąpić fopeni fclosetutaj.) RAII to „właściwy” sposób robienia rzeczy tutaj, ale jest niewygodny dla osób, które chcą korzystać z bibliotek C z C ++ .
dan04,
W przypadku tego rodzaju odpowiedzi odpowiednie byłoby podanie przykładu prawidłowego rozwiązania.
Claus Jørgensen
@ ClausJørgensen: Cóż, rozwiązanie niestety nie jest tak naprawdę „efektowne”, ponieważ dotyczy tylko File file("some.txt");i to wszystko (nie open, nie close, nie try...)
Matthieu M.
D ma również RAII
Demi
@Demetri: Nie znam się zbyt dobrze na D, czy mógłbyś wyjaśnić, w jaki sposób RAII współdziała z Garbage Collection? Wiem, że w Pythonie można napisać metodę „deinit”, jednak dokumentacja ostrzega, że ​​w przypadku cyklu odwołań niektóre obiekty nie zobaczą wywołanej metody deinit.
Matthieu M.
9

Częstym błędem, który spełnia twoje kryteria, jest niezrozumienie działania konstruktorów kopiowania w przypadku przydzielonej pamięci w klasie. Straciłem rachubę czasu, który spędziłem naprawiając awarie lub wycieki pamięci, ponieważ „noob” umieścił swoje obiekty na mapie lub wektorze i nie napisał poprawnie konstruktorów kopii i destrukterów.

Niestety C ++ jest pełne takich „ukrytych” błędów. Ale narzekanie na to jest jak narzekanie, że pojechałeś do Francji i nie mogłeś zrozumieć, co mówią ludzie. Jeśli tam pojedziesz, naucz się języka.

Henz
źródło
1
Myślę, że problem z C ++ polega na tym, że bardzo łatwo jest strzelić sobie w stopę. Pewnie, wokół są dobrzy programiści C ++, mnóstwo dobrego oprogramowania napisanego w C ++. Ale bardzo trudno jest zostać dobrym programistą C ++. Seria Scott Meyers „Efficient C ++” pokazuje, ile subtelności ma ten język.
Marco Mustapic
Zgadzam się. Problem polega jednak na tym, że wielu (większość) programistów C ++ myśli, że wie, co robi, kiedy wyraźnie tego nie robi. Miałeś na myśli „Effective C ++”?
Henry,
Przynajmniej jest to coraz lepsze dzięki nowym raczej restrykcyjnym regułom dotyczącym niejawnego generowania operacji kopiowania / przenoszenia w C ++ 0x. W wielu przypadkach trzech naruszeń zasady niejawne generowanie operacji kopiowania będzie przestarzałe i powinno wygenerować ostrzeżenie.
sellibitze
6

C ++ pozwala na wiele różnych funkcji i stylów programowania, ale to nie znaczy, że są to naprawdę dobre sposoby na używanie C ++. W rzeczywistości niepoprawnie łatwo używać C ++.

Trzeba się go nauczyć i właściwie zrozumieć , po prostu uczenie się przez działanie (lub używanie go w inny sposób) doprowadzi do nieefektywnego i podatnego na błędy kodu.

Dario
źródło
4

Cóż ... Na początek możesz przeczytać C ++ FAQ Lite

Następnie kilka osób zbudowało kariery, pisząc książki o zawiłościach C ++:

Herb Sutter i Scott Meyers mianowicie.

Jeśli chodzi o rant Torvaldsa, który nie ma treści ... chodźcie na poważnie: żaden inny język nie miał tyle atramentu na rozlanie niuansów tego języka. Twoje książki w języku Python, Ruby i Java skupiają się na pisaniu aplikacji ... Twoje książki w C ++ skupiają się na głupich funkcjach / wskazówkach / pułapkach.

czerwony brud
źródło
1
Hmm ... javapuzzlers.com , jimbrooks.org/web/python/#Pitfalls . Powiedziałbym, że Accelerated C ++ (na przykład) skupia się bardziej na tym, jak pisać kod niż te ...
Jerry Coffin
1
Przywołałeś kilka przykładów zasobów, które wskazują przypadkowe przypadki w ich odpowiednich językach; rzeczy, które wyglądają dziwnie i nie jesteś do końca pewien, jak by działały (chociaż lista pytań w Pythonie jest bliska) ... C ++ ma całą branżę, która wskazuje rzeczy, które wyglądają idealnie poprawne i zachowują się w sposób, którego się nie spodziewasz.
czerwono-brud
3

Zbyt ciężkie szablony mogą początkowo nie powodować błędów. Z biegiem czasu ludzie będą musieli zmodyfikować ten kod i będzie im trudno zrozumieć ogromny szablon. Wtedy pojawiają się błędy - nieporozumienie powoduje komentarze „Kompiluje i uruchamia”, co często prowadzi do prawie, ale niezupełnie poprawnego kodu.

Ogólnie, jeśli widzę, że robię trzypoziomowy głęboki szablon ogólny, zatrzymuję się i myślę, jak można go zredukować do jednego. Często problem rozwiązuje się przez wyodrębnienie funkcji lub klas.

Michael K.
źródło
8
Utrzymywanie skomplikowanego kodu w obliczu zmieniających się wymagań zawsze powoduje błędy bez większego wysiłku, nie ma tu nic specjalnego w szablonach.
Fred Nurk
2

Ostrzeżenie: nie jest to tak duża odpowiedź, jak krytyka rozmowy, do której „użytkownik niewiedzy” odsyła w swojej odpowiedzi.

Jego pierwszym głównym punktem jest (podobno) „ciągle zmieniający się standard”. W rzeczywistości przykłady, które podaje, dotyczą zmian w C ++, zanim powstał standard. Od 1998 r. (Kiedy sfinalizowano pierwszy standard C ++) zmiany w języku były dość minimalne - w rzeczywistości wielu twierdziło, że prawdziwym problemem jest to, że należy wprowadzić więcej zmian. Jestem dość pewien, że cały kod zgodny z oryginalnym standardem C ++ nadal jest zgodny z bieżącym standardem. Chociaż jest to nieco mniej pewne, chyba że coś zmieni się szybko (i dość nieoczekiwanie) to samo będzie w zasadzie również w nadchodzącym standardzie C ++ (teoretycznie cały kod, który użyłexportpęknie, ale praktycznie nie istnieje; z praktycznego punktu widzenia nie stanowi to problemu). Mogę wymyślić kilka innych języków, systemów operacyjnych (lub wielu innych elementów związanych z komputerem), które mogą wysuwać takie roszczenia.

Następnie przechodzi w „ciągle zmieniające się style”. Ponownie większość jego punktów jest bardzo bliska nonsensów. Stara się scharakteryzować for (int i=0; i<n;i++)jako „stary i zepsuty” oraz for (int i(0); i!=n;++i)„nowy upał”. Rzeczywistość jest taka, że ​​chociaż istnieją typy, dla których takie zmiany mogą mieć sens, ponieważ intnie ma to żadnej różnicy - a nawet gdy można coś zyskać, rzadko jest to konieczne do napisania dobrego lub poprawnego kodu. Nawet w najlepszym razie robi górę z kretowiska.

Kolejnym jego twierdzeniem jest to, że C ++ „optymalizuje w złym kierunku” - w szczególności, chociaż przyznaje, że korzystanie z dobrych bibliotek jest łatwe, twierdzi, że C ++ „sprawia, że ​​pisanie dobrych bibliotek jest prawie niemożliwe”. Tutaj uważam, że jest to jeden z jego najbardziej podstawowych błędów. W rzeczywistości pisanie dobrych bibliotek dla prawie każdego języka jest niezwykle trudne. Niemal minimum napisanie dobrej biblioteki wymaga tak dobrego zrozumienia problematycznej domeny, że Twój kod działa dla wielu możliwych aplikacji w tej domenie (lub z nią związanych). Większość tego, co naprawdę robi C ++ , to „podnieść poprzeczkę” - po zobaczeniu, o ile lepsza może być biblioteka , ludzie rzadko są skłonni wrócić do pisania tego rodzaju bzdur, jakie mieliby w przeciwnym razie.naprawdę dobrzy koderzy piszą sporo bibliotek, z których następnie (jak sam przyznaje) mogą korzystać (reszta). Tak naprawdę jest to przypadek, w którym „to nie jest błąd, to funkcja”.

Nie będę próbował trafić w każdy punkt w kolejności (zabierałoby to strony), ale przechodzę bezpośrednio do jego punktu końcowego. Cytuje Bjarne'a mówiąc: „Optymalizacja całego programu może być wykorzystana do wyeliminowania nieużywanych tabel funkcji wirtualnych i danych RTTI. Taka analiza jest szczególnie odpowiednia dla stosunkowo małych programów, które nie używają dynamicznego łączenia”.

Krytykuje to, podnosząc nieuzasadnione twierdzenie, że „To naprawdę trudny problem”, nawet posuwając się do porównania z problemem zatrzymania. W rzeczywistości nie ma nic takiego - w rzeczywistości zrobił to linker zawarty w Zortech C ++ (właściwie pierwszy kompilator C ++ dla MS-DOS w latach 80-tych). To prawda, że ​​trudno jest mieć pewność, że wyeliminowano wszystkie możliwe dane, ale nadal jest całkiem rozsądne, aby wykonać całkiem uczciwą robotę.

Niezależnie od tego jednak znacznie ważniejsze jest to, że jest to absolutnie nieistotne dla większości programistów. Jak wiedzą ci z nas, którzy zdemontowali sporo kodu, chyba że piszesz asembler bez bibliotek, Twoje pliki wykonywalne prawie na pewno zawierają sporo „rzeczy” (zarówno kodu, jak i danych, w typowych przypadkach), które prawdopodobnie nawet o tym nie wiem, nie wspominając o tym, że kiedykolwiek faktycznie używałem. Dla większości ludzi przez większość czasu to nie ma znaczenia - chyba że pracujesz dla najmniejszych systemów wbudowanych, dodatkowe zużycie pamięci jest po prostu nieistotne.

Ostatecznie prawdą jest, że ten rant ma nieco więcej substancji niż idiotyzm Linusa - ale to daje mu dokładnie to cholerstwo z lekką pochwałą, na którą zasługuje.

Jerry Coffin
źródło
1

Jako programista C, który musiał pisać w C ++ z powodu nieuniknionych okoliczności, oto moje doświadczenie. Jest bardzo mało rzeczy, których używam, czyli C ++ i głównie trzymam się C. Głównym powodem jest to, że nie rozumiem C ++ aż tak dobrze. Nie miałem / nie miałem mentora, który pokazałby mi zawiłości C ++ i jak napisać w nim dobry kod. I bez wskazówek bardzo dobrego kodu C ++ bardzo trudno jest napisać dobry kod w C ++. IMHO jest to największą wadą C ++, ponieważ trudno jest znaleźć dobrych programistów C ++ chętnych do obsługi początkujących.

Niektóre z osiągnięć wydajności, które widziałem, zwykle wynikają z magicznej alokacji pamięci STL (tak, możesz zmienić alokator, ale kto to robi, kiedy zaczyna od C ++?). Zwykle słyszysz argumenty ekspertów C ++, że wektory i tablice oferują podobną wydajność, ponieważ wektory używają tablic wewnętrznie, a abstrakcja jest super wydajna. Stwierdziłem, że jest to prawdą w praktyce w przypadku dostępu do wektora i modyfikacji istniejących wartości. Ale nie jest to prawdą w przypadku dodania nowego wpisu, budowy i zniszczenia wektorów. gprof wykazał, że łącznie 25% czasu na aplikację poświęcono konstruktorom wektorów, destruktorom, memmove (do przeniesienia całego wektora w celu dodania nowego elementu) i innym przeciążonym operatorom wektorowym (jak ++).

W tej samej aplikacji wektor czegośSmall został użyty do przedstawienia czegośBig. Nie było potrzeby losowego dostępu do czegoś Małego w czymś Dużym. Nadal użyto wektora zamiast listy. Powód, dla którego zastosowano wektor? Ponieważ oryginalny koder był zaznajomiony z tablicową składnią wektorów i niezbyt dobrze z iteratorami potrzebnymi do list (tak, pochodzi on z języka C). Następnie udowadnia, że ​​do poprawnego posługiwania się C ++ potrzeba wielu wskazówek ekspertów. C oferuje tak mało podstawowych konstrukcji bez absolutnie żadnej abstrakcji, że możesz to zrobić znacznie łatwiej niż C ++.

aufather
źródło
0

STL i boost są przenośne na poziomie kodu źródłowego. Myślę, że Linus mówi o tym, że C ++ nie ma ABI (binarnego interfejsu aplikacji). Musisz więc skompilować wszystkie biblioteki, z którymi się łączysz, z tą samą wersją kompilatora i tymi samymi przełącznikami, albo ograniczyć się do C ABI na granicach dll. Uważam też to za anonimowe ... ale jeśli nie tworzysz bibliotek stron trzecich, powinieneś być w stanie przejąć kontrolę nad środowiskiem kompilacji. Uważam, że ograniczenie się do C ABI nie jest warte kłopotów. Wygoda przesyłania ciągów, wektorów i inteligentnych wskaźników z jednej biblioteki DLL do drugiej jest warta kłopotów z koniecznością odbudowania wszystkich bibliotek podczas aktualizacji kompilatorów lub zmiany przełączników kompilatora. Złote zasady, których przestrzegam to:

- Dziedzicz, aby ponownie użyć interfejsu, a nie implementacji

-Preferowanie agregacji nad dziedziczeniem

-Preferuj, tam gdzie to możliwe, bezpłatne funkcje dla metod członkowskich

-Zawsze używaj idiomu RAII, aby twój kod był wyjątkowo bezpieczny. Unikaj próby złapania.

-Używaj inteligentnych wskaźników, unikaj nagich (nie posiadanych) wskaźników

-Preferowanie semantyki wartości do semantyki odniesienia

-Nie wymyślaj ponownie koła, użyj stl i doładowania

-Użyj idiomu Pimpl, aby ukryć prywatny i / lub zapewnić zaporę kompilatora


źródło
-6

Nie umieszczanie finału ;na końcu deklaracji klauzuli, przynajmniej w niektórych wersjach VC.

Marco Mustapic
źródło
4
Jest to być może bardzo częsty błąd dla początkujących (podobnie jak dla kogoś, kto wciąż uczy się podstawowej składni), ale czy jest wielu, którzy uważają się za kompetentnych i nadal uważają ten błąd za godny uwagi?
Fred Nurk
1
Napisałem to tylko dlatego, że kompilator dał ci błąd, który nie miał nic wspólnego z brakiem średnika.
Marco Mustapic
2
tak, dokładnie ten sam błąd występuje w kompilatorze C.
Mircea Chirea