Wymagany i zalecany styl kodowania w C++
Transkrypt
Wymagany i zalecany styl kodowania w C++
Wymagany i zalecany styl kodowania w C++ Warszawa, 17.03.2011 1 Wymagane 1.1 Wygląd kodu 1.1.1 Nazewnictwo 1. Spójny styl nazywania klas, metod, zmiennych, np. : ● nazwy klas zaczynają się z dużej litery (np. GameTable) ● nazwy zmiennych i metod z małej litery (np. currentPosition, movePawn) ● nazwy stałych składają się z dużych liter i podkreśleń oddzielających wyrazy (np. GAME_SPEED) ● wszystkie nazwy w języku angielskim 2. Klasy wyjątków powinny mieć końcówkę nazwy: Exception (np. GenericException). [G 32] 1.1.2 Formatowanie 3. Spójny styl „wcinania” kodu (TAB-ami lub spacjami) np.: int calculateValue(int a, int b) { if( isWeirdPair(a, b) ) return a/b; if( isNicePair(a, b) ) { int c=a+b; return 2*c; } return a*b; } lub np.: int calculateValue(int a, int b) { if( isWeirdPair(a, b) ) return a/b; if( isNicePair(a, b) ) { int c=a+b; return 2*c; } return a*b; } 4. Maksymalna liczba linii zajmowanych przez funkcję/metodę – 1 ekran (nie więcej niż 40 linii). Jeśli w pewnym miejscu nie da się tego stanu osiągnąć, należy umieć to uzasadnić. 5. Maksymalna długość linii nie powinna przekraczać szerokości ekranu (najlepiej 80 kolumn/znaków). By w Visual Studio 2010 rysować linię marginesu w odpowiednim miejscu, należy zainstalować odpowiednie rozszerzenia. W menu → Tools → Extension manager... → Online Gallery, wyszukać „editor guidelines” i zainstalować rozszerzenia: „Editor Guidelines” oraz „Editor Guidelines UI”. Po ponownym uruchomieniu Visual Studio, należy ustawić kursor w kolumnie, od której powinien zaczynać się margines a następnie po kliknięciu prawym przyciskiem myszy wybrać Guidelines → Add guideline. 1.1.3 Porządek elementów w ciele klasy 6. Spójny styl rozmieszczania pól i metod klasy: ● Pola powinny być umieszczone na początku klasy w kolejności: public, protected, private, w dalszej części powinny być umieszczone konstruktory, a później metody. Metody pogrupowane raczej wg funkcjonalności niż zakresu widoczności (metoda prywatna może występować między dwoma publicznymi) – celem jest jak najlepsza czytelność kodu [JCC 3.1.3]. 1/4 ● Alternatywne rozmieszczenie przedstawione jest poniżej. Kolejne części klasy powinny występować w kolejności: public, protected, private [G 44]. Pola nie powinny być przemieszane z metodami w ciele klasy w obrębie kolejnych części (public, protected, private) klasy. 1.1.4 Inne 7. W jednym pliku powinna znajdować się tylko jedna klasa (ewentualnie wraz z małymi klasami pomocniczymi). 1.1.5 Inne ściśle związane z językiem 8. Komentarze opisujące: klasy, metody, funkcje, pola powinny znajdować się w pliku nagłówkowym (*.h) (ewentualne komentarze dotyczące szczegółów działania funkcji, metod mogą być w pliku *.cpp). 1.2 Sposób programowania 1.2.1 Ogólne 9. Nie powtarzać się, czyli nie pisać w paru miejscach tego samego kodu. Jest to jedna z najbardziej fundamentalnych zasad eleganckiego programowania. Szczególnym, negatywnym przypadkiem postępowania wbrew tej zasadzie jest stosowanie metody „copy & paste” lub jej modyfikacji, gdzie stosowana jest metoda „copy & paste” a następnie w skopiowanym kodzie wprowadzane są jedynie kosmetyczne poprawki np. zmiana nazwy jednej ze zmiennych, zamiana plusa na minus. [BE 17] 10. Nie używać w kodzie „magicznych liczb” (ang. magic numbers) (czyli np. pętli: for(int i=23; i<45; i++)). Liczby inne niż 0 i 1 powinny być zadeklarowane jako stałe (const lub enum). [G 65] [S 17] 11. Zmienne powinny być deklarowane jak najbardziej lokalnie (czyli np. w metodzie, w której są używane)[S 18] 12. Dane składowe klasy (pola) nie powinny być zadeklarowane jako publiczne – dostęp do nich powinien być zapewniony przez metody klasy (wyjątki: 1. klasa jest traktowana jako czysta struktura danych, 2. pola typu const, do których musi być zapewniony dostęp z zewnątrz klasy, mogą być zadeklarowane jako publiczne). [EC 20] [G 49] 13. Nie stosować zmiennych globalnych. [BE 43] 14. Wyjątki (ang. exceptions) należy rzucać w wyjątkowych sytuacjach. Nie należy rzucać wyjątków w sytuacjach, które są normalne lub spodziewane (np. koniec pliku) - wyjątki nie powinny być rzucane podczas normalnego przepływu sterowania w programie. 15. Nazwy plików wykorzystywanych przez program nie powinny być „zaszyte” w kodzie, tylko np. wyodrębnione jako stałe pola klasy. 16. Nie używać instrukcji goto. [G 69]. Jeśli użycie goto wydaje się niezbędne, należy umieć je uzasadnić. 17. Kompilator nie powinien wyświetlać żadnych ostrzeżeń (ang. warnings) podczas kompilacji. Poziom ostrzeżeń pokazywanych podczas kompilacji należy ustawić na najwyższy. 18. Nie edytować ręcznie kodu generowanego automatycznie jeśli nie jest to absolutnie konieczne. 1.2.2 Dziedziczenie 19. Nie używać prywatnego dziedziczenia. [BE 32] 20. Unikać wielodziedziczenia (każde użycie należy umieć uzasadnić). 1.2.3 Inne ściśle związane z językiem 21. Jeśli przekazujemy obiekt jako argument metody, to należy stosować przekazywanie przez referencję zamiast przekazywania przez wartość (wyjątek: naprawdę chcemy operować na kopii obiektu). 22. Jeśli chcemy przekazać obiekt jako argument metody, a wskaźnik do danego obiektu nie jest w danej metodzie potrzebny, to należy to robić przez referencję (a nie przez wskaźnik). 23. Jeśli przekazujemy obiekt przez referencję jako argument metody i metoda nie zmienia tego obiektu, to należy stosować modyfikator const. [BE 46] 24. Jeśli metoda nie zmienia stanu obiektu, na rzecz którego jest wywoływana, to należy oznaczyć ją modyfikatorem const. 25. Operator przypisania powinien zwracać referencję na obiekt, na rzecz którego jest wywoływany (return *this)[15 EC] 26. W operatorze przypisania należy sprawdzać, czy nie mamy do czynienia z przypisaniem do siebie ( if 2/4 (this==&other) return *this;). [EC 17] 27. Klasy bazowe (po których dziedziczymy) muszą mieć wirtualne destruktory [EC 14] 28. Jeśli klasa zawiera wskaźniki (i obiekty alokowane dynamicznie), to są 2 możliwości: ● powinien być zdefiniowany operator przypisania i konstruktor kopiujący[BE 37], lub ● metody: operator przypisania i konstruktor kopiujący powinny być zadeklarowane jako prywatne (w przypadku, w którym w programie nie wykonujemy kopiowania obiektów danej klasy). [BE 36] 29. Rzucać (throw) wyjątki przez wartość i łapać (catch) przez referencję [BE 52] 30. Nie należy zakładać, że funkcje systemowe (np. fread, scanf) zawsze zakończą się powodzeniem. 31. W przypadku wykonywania rzutowania, należy preferować operatory rzutowania C++ zamiast tradycyjnych operatorów rzutowania C (wadą tych drugich jest możliwość rzutowania praktycznie dowolnego typu na dowolny inny typ, co często prowadzi do błędów w programie). [MEC 2] Opis operatorów rzutowania C++ : ● static_cast<T> - odpowiednik standardowego operatora rzutowania C. Nie może usunąć atrybutu const z rzutowanego wyrażenia (do tego służy const_cast<T>). ● const_cast<T> - usuwa atrybut const/volatile z rzutowanego wyrażenia ● dynamic_cast<T> - służy do bezpiecznego wykonywania downcasting-u (rzutowania obiektu klasy rodzica na obiekt klasy potomka). Operator nie może być stosowany do typów nie zawierających funkcji wirtualnych (bo służy do nawigowania po hierarchii dziedziczenia). W przypadku nieprawidłowego rzutowania (typ, na który rzutujemy nie jest potomkiem typu rzutowanego): ○ jeśli rzutowany jest wskaźnik, zwracany jest null pointer; ○ jeśli rzutowana jest referencja, rzucany jest wyjątek. Uwaga: downcasting powinien występować w kodzie co najwyżej sporadycznie. ● reinterpret_cast<T> - operator rzutowania, którego efekt działania jest prawie zawsze nieprzenośny (zależny od implementacji), w związku z tym raczej nie należy go stosować. 2 Zalecane 2.1 Elementy filozofii programowania obiektowego 32. Klasa powinna stanowić zamkniętą całość, która jak najmniej „wie” o otaczającym ją programie. 33. Jeśli klasa A dziedziczy po klasie B, to powinna być zachowana relacja „A jest B” (np. Samochód jest Pojazdem). Jeśli obiekt A zawiera obiekt B, to powinna być zachowana relacja „A zawiera B” (np. Samochód zawiera Kierownicę).[BE 24] 34. Podejmując decyzję, czy klasa B powinna zawierać obiekty klasy A, czy dziedziczyć po klasie A, należy stosować następujące kryteria [PZO]: ○ stosować relację zawierania, jeśli w czasie wykonania programu może zajść potrzeba zastąpienia obiektu klasy A obiektem innej klasy; ○ stosować relację dziedziczenia, jeśli może zajść potrzeba, by obiekty klasy B były czasem traktowane jakby były były obiektami klasy A. 35. Interfejs klasy (czyli publiczne metody danej klasy) powinien być prosty. [BE 7] 36. Jeśli dany parametr lub grupa parametrów występuje często jako argument różnych metod, zastanowić się nad stworzeniem oddzielnej klasy zawierającej dany parametr lub grupę parametrów, gdzie wspomniane metody prawdopodobnie staną się metodami nowej klasy. [PZO] 2.2 Deklaracje metod 37. Długie i skomplikowane funkcje/metody powinny być rozbite na mniejsze. [BE 15] 38. W funkcjach/metodach nie stosować długich list argumentów. [BE 16] 39. Nazwa klasy jest domyślna i nie powinna występować w nazwie metody (np. Line::getLength(); zamiast Line::getLineLength();). [G 15] 2.3 Inne 40. Należy stworzyć hierarchię wyjątków. [BE 51] 41. Uważać na przedwczesną optymalizację - „First make it work, then make it fast”. [BE 1] [BE 35] 42. Każda publiczna metoda powinna wykrywać błędy jej wywołania. ● Jeśli można te błędy w tym miejscu naprawić, to trzeba. 3/4 Jeśli nie można (ew. nie wiadomo jak to zrobić), to są 2 możliwości: ○ błąd nie ma prawa zaistnieć (wynika z pomyłki programisty) – wtedy powinien być zastosowany assert, ○ błąd czasami może się pojawić (np. do metody zostały przekazane nieprawidłowe argumenty przez użytkownika klasy) – wtedy należy wyrzucić wyjątek. 43. Jeśli metoda nie korzysta z instancyjnych (tj. niestatycznych) metod i pól klasy, w której się znajduje, to powinna być statyczna. Dzięki temu zabiegowi stosowanie tej metody jest bardziej czytelne. 44. Komentować tylko to, co niezbędne i nieintuicyjne (nie komentować zwykłych getter-ów i setter-ów, czyli metod typu: getAge(), setAge(int age)). ● 2.4 Inne ściśle związane z językiem 45. Należy wywoływać konstruktor kopiujący nadklasy w konstruktorze kopiującym. Tak samo dla operatora przypisania. [BE 38] [BE 39] 46. Powinno się stosować „strażników” #include w plikach nagłówkowych (ale przy użyciu kompilatora Microsoft-u wystarczy dyrektywa #pragma once). [G 40] Literatura Przy odnośnikach podane są numery wskazówek, które są zawarte w poniższych pozycjach literaturowych: ● [BE] Bruce Eckel, Thinking in C++, 2nd edition, vol. I – darmowe wydanie internetowe: http://helion.pl/online/thinking/index.html, 2000 [przy odnośniku podano numer wskazówki z rozdziału B:Programming guidelines] ● [S] Herb Sutter, Andrei Alexandrescu, C++ coding standards. 101 rules, guidelines, and best practices, Addison-Wesley Professional, 2004 ● [EC] Scott Meyers, Effective C++. 50 specific ways to improve your programs and designs, 2nd edition, Addison-Wesley Professional, 1997 ● [MEC] Scott Meyers, More Effective C++. 35 new ways to improve your programs and designs, Addison-Wesley Professional, 1996 ● [G] C++ programming style guidelines - http://geosoft.no/development/cppstyle.html, 2006 ● [PZO] Bertrand Meyer, Programowanie zorientowane obiektowo, Helion, 2005 ● [JCC] Sun Microsystems, Java Code Conventions, (http://java.sun.com/docs/codeconv/CodeConventions.pdf), 1997 4/4