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