C++ Core Guidelines #2: [P: Philosophy] Filozofia (część 1).

C++ Core Guidelines: Filozofia – część 1.

Zaczniemy od naprawdę ogólnych zasad. Źródłem większości przykładów jest oryginalny dokument C++ Core Guidelines.

Rozdział Filozofia jest podzielony na kilka wpisów – kolejne z nich będą publikowane systematycznie. A więc…

P1. Wyrażaj swoje pomysły bezpośrednio w kodzie.

Kompilatory, podobnie jak większość programistów, nie czytają komentarzy. Sam kod musi być w takiej sytuacji maksymalnie konkretny, co pozwoli na jego łatwy odbiór przez człowieka i zapewni bardzo wysoki poziom odporności na błędy, które może łatwo wyłapać kompilator.

Przykład 1:

Definiujesz klasę Date i tworzysz metodę, która zwraca miesiąc ? Niech właśnie to obrazuje twój kod. Niech metoda będzie zadeklarowana jako stała. Month może być w tym przykładzie np. silnym typem wyliczeniowym, zawierającym zdefiniowane miesiące.

Co osiągnęliśmy ? Metoda bardzo dokładnie opisuje swoje działanie, a kompilator eliminuje cały zestaw potencjalnych błędów, związanych ze zwracanymi wartościami albo nadmiarowym kodem, który chciałby zmienić stan obiektu.

Przykład 2:

Załóżmy, że istnieje funkcja f, która wygląda jak powyżej. Jedną z jej głównych części jest kod, który sprawdza, pod jakim indeksem w wektorze znajduje się wartość wprowadzona przez użytkownika. Oczywiście na pierwszy rzut oka nie można stwierdzić, co to za instrukcje. Trzeba poświęcić czas i uwagę…

Po analizie okazuje się, że kod jest bezsensownym powieleniem czynności, która jest tak powszechna, że już dawno zasłużyła na swoje miejsce w bibliotece standardowej języka. Nagłowek <algorithm> zawiera definicję funkcji std::find, która robi dokładnie to, co powyższe kilka linijek (zapewne robi to też lepiej).

Finalnie mamy:

Kod jest krótszy, odporny na błędy i niesamowicie jasny. Użyliśmy tutaj implementacji podstawowego algorytmu z biblioteki standardowej oraz słowa kluczowego auto.

Konkluzja: Programista powinien znać bibliotekę standardową, przynajmniej na podstawowym poziomie. Powinien wiedzieć, kiedy użycie gotowych elementów ma sens i to wykorzystywać.

P.2 Pisz kod zgodny ze standardem ISO C++.

Język programowania C++ jest technologią ustandaryzowaną. Oznacza to, że istnieje oficjalny zestaw wytycznych, które powinny być brane pod uwagę podczas tworzenia oprogramowania w tym języku. Te same zasady są podstawą, na której bududowane są kompilatory na różne platformy.

Standard ISO C++ definiuje semantykę języka oraz reguły jego działania w określonych sytuacjach. Wszystko po to, aby zapewnić deklarowaną uniwersalność i przenośność kodu pomiędzy różnymi platformami. Dlatego zaleca się korzystane z kompilatorów, które oficjalnie wspierają i weryfikują kod źródłowy pod względem poprawności i zgodności z ISO C++. Powinno się też unikać sytuacji w kodzie źródłowym, które są określone jako Undefined Behaviour (UB) – standard nie definiuje, jakiego wyniku powinniśmy w takiej sytuacji oczekiwać.

Istnieją oczywiście sytuacje, w których programiści poruszają się poza obszarami zdefiniowanymi przez standard albo muszą zdefiniowane zasady ograniczyć (np. zrezygnować z dynamicznej alokacji pamięci). Często sami twórcy kompilatorów wbudowują w nie rozszerzenia, które nie są częścią oficjalnych wytycznych i wspierają np. jedynie konkretny system operacyjny. Trzeba w takich sytuacjach pamiętać, że niestandardowy kod najlepiej odseparować i utrzymywać niejako osobno od jego pozostałej części. Pozwoli to na uniknięcie problemów i ograniczenie kosztów utrzymania oraz ułatwi ewentualną migrację do implementacji zgodnej z ISO C++.

P.3 Staraj się pisać kod, który jasno wyraża swoje intencje.

Kod źródłowy powinien być oczywisty. Jeśli potrafimy od razu stwierdzić, że kod realizuje to, co powinien – jest bardzo dobrze. Brzmi to może nieco dziwnie i podobnie do punktu P.1, ale spójrzmy na przykład.

Przykład:

Zasadniczo poprawny składniowo kod w C++. Ale…

Intencją jest tak naprawdę operowanie na kolejnych elementach – nic więcej. Powyższy kod tego jednak jasno nie pokazuje.

Zamiast tego mamy jawnie zdefiniowany indeks przed pętlą, co umożliwia bezsensowną zmianę tej wartości, a także niepotrzebnie wydłuża czas  życia zmiennej (będzie dostępna również w zakresie za pętlą). Takie szczegóły implementacyjne, związane z prostą iteracją nie są nam do niczego potrzebne – zaciemniają tylko obraz i utrudniają zrozumienie, co tak zaprawdę realizuje kod źródłowy.

Porównajmy to z linijkami poniżej:

Korzystając z zakresowej pętli for, ukrywamy kod związany z iteracją po elementach. Pętla operuje na referencji do, stałych lub nie, elementów. W tym momencie kod jest jasny, a dostęp do elementów jawnie, bądź nie, zabezpieczony.

Dużo lepszym rozwiązaniem jest też użycie gotowych algorytmów w połączeniu z wyrażeniami lambda, co pokazują kolejne dwie linie. Wnioski są podobne.

Oczywiście niektóre konstrukcje języka są bardziej opisowe i zrozumiałe, a inne mniej. Powinniśmy jednak cały czas poznawać nowe mechanizmy i bibliotekę standardową, żeby móc stale upraszczać i rozwijać nasz kod – to jest właśnie filozofia nowoczesnego języka C++.

C++ Core Guidelines: Filozofia – część 2.

0 Comments

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">