|
Sponsored link
phi-lab software
|
C++. Wstep.
Zasady kodowania.
Zasady kodowania to zespol regul, uzywanych podczas pisania kodu zrodlowego programu.
Okreslaja one np. konwencje nazewnicze, jakie stosujemy w nadawaniu nazw zmiennym,
glebokosc wciec czy sposob rozmieszczenia nawiasow.
Oczywiscie, mozna pisac bez zadnych regul, byle kod byl poprawny pod wzgledem wymogow jezyka C++ i program wynikowy tez bedzie dzialal. Z tym tylko, ze gdy program jest w miare duzy, nawet autor kodu bedzie mial po jakims czasie potezne problemy aby zrozumiec, o co mu chodzilo, gdy go kiedys pisal. Chyba nie ma sensu opisywać w jakim nastroju bedzie inny programista, ktory bedzie musial ten kod zmieniac lub rozszerzac. W takiej sytuacji lepiej byloby dla autora nie byc w zasiegu wzroku skazanca.
Oczywistym jest w takim razie, ze jakies reguly powinnismy stosowac.
W duzych grupach programistycznych jest to po prostu wymog formalny.
W powaznych firmach software'owych swiezo zatrudniony programista pierwsze co
dostaje do reki, to spisany dekalog regul kodowania obowiazujacych w firmie.
Zeby byla jasnosc. Nie ma jakiegos jedynie slusznego i uniwersalnego systemu
kodowania.
Co firma, to inny zestaw regul. Co ksiazka, to inne reguly sa
stosowane w przytaczanych przykladach.
I dobrze. Nie ma znaczenia jaki system regul bedziemy stosowac.
Wazne jest, abysmy w ogole uzywali jakichs regul i abysmy byli w tym
konsekwentni az do bolu.
Reguly przedstawione ponizej sa rezultatem moich wlasnych doswiadczen i przemyslen.
Ale bede tez prezentowac inne, alternatywne, czesto uzywane reguly.
W dalszej czesci tego dokumentu, opisujacej konkretne reguly, bede uzywal slowa 'powinien'. Oczywiscie 'powinien' w/g mnie. Reszta swiata ma prawo miec na ten temat inne zdanie.
1.4.1.Zasady nazywania plikow.
Zalozmy, ze tworzymy klase o nazwie 'Widget'.
Deklaracja tej klasy powinna sie znajdowac w pliku 'Widget.h',
natomiast definicja w 'Widget.cpp'.
1.4.2.Rozmiary.
Definicja jednej funkcji nie powinna przekraczac 60 linii tekstu. Ta regula takze wynika z regul czytelnosci kodu. Jezeli funkcja staje sie zbyt dluga, nalezy wyodrebnic z niej podzadania i umiescic je w innych, dedykowanych funkcjach.
1.4.3.Naglowek pliku.
/******************************************************************** * Project Sytem zarzadzania plikami BSCommander * (c) copyright * Organisation Beesoft.org * File ViewWindow.cpp * Author Piotr Pszczolkowski (piotr@beesoft.org) * Date 31.12.2010 * About bla bla bla.... *-------------------------------------------------------------------- * History: * 01.01.2011 - dodanie funkcji 'count' w celu zliczania lizakow, * 12.02.2001 - zmiana typu zmiennej 'number' z 'int' na 'double'. * ... * ... ********************************************************************/Oczywiscie, taki (lub podobny) naglowek stosujemy gdy piszemy program dla siebie (swojej firmy/organizacji). W przypadku gdy piszemy program na zewnatrz moze nam zostac narzucony inny, stosowny naglowek. Np. piszac program dla srodowiska Open Source na licencji GPL stosowny naglowek powinien miec postac:
/******************************************************************** * Copyright (C) 2005 Piotr Pszczolkowski *------------------------------------------------------------------- * This file is part of BsC (Beesoft Commander). * * BsC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * BsC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with BsC; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *******************************************************************/
1.4.4.Pliki naglowkowe (include).
Powiedzmy, ze istnieje juz klasa 'CWidget'.
Moze sie zdarzyc, ze takze implementacje wielu innych klas
(np. CButton) moga potrzebować informacji o tym, jak zbudowna jest klasa 'CWidget'.
Bedzie to mialo miejsce, gdy w tych klasach klasa 'CWidget' bedzie uzywana.
Oznacza to za kazdym razem wczytywanie pliku 'CWidget.h'.
Koniecznosc taka moze wystepować setki razy. A to jest czaaaaassss.....
I dlatego plik naglowkowy musi miec na poczatku i na koncu odpowiednie
instrukcje preprocesora:
#ifndef INCLUDED_WIDGET_H #define INCLUDED_WIDGET_H ... #endif // INCLUDED_WIDGET_HCoz to oznacza?
I kolejna sytuacja.
Zalozmy, ze po raz pierwszy chcemy wlaczyc 'CButton.h'.
Ale sytuacja jest taka, ze na poczatku tego pliku znajduje sie dyrektywa wlaczajaca 'CWidget.h'.
I co sie stanie? Ano normalnie przy wczytywaniu 'CButton.h' zostanie wczytany 'Widget.h'
(choc byc moze nie bedzie amalizowany, ale tak czy inaczej bedzie wczytany).
I to pomimo tego, ze 'Widget.h' zostal juz wczytany przy stu innych okazjach.
Rozwiazanie jest nastepujace:
#ifndef INCLUDED_BUTTON_H #define INCLUDED_BUTTON_H #ifndef INCLUDED_WIDGET_H #include "Widget.h" #endif // INCLUDED_WIDGET_H ... #endif // INCLUDED_BUTTON_HTeraz nawet przy pierwszym wczytaniu 'CButton.h', plik 'CWidget.h' zostanie wczytany tylko i wylacznie wtedy, gdy jeszcze tego wczesniej nie zrobiono. Jezeli wczesniej inne pliki wymusily wczytanie 'CWidget.h', to nawet przy pierwszym wczytywaniu 'CButton.h' dyrektywa wlaczajaca 'CWidget.h' zostanie zignorowana.
I ostatnia sytuacja. Dotyczaca nie naszych plikow naglowkowych. Czesto wlaczamy do naszych plikow pliki naglowkowe biliotek takich np. jak STL czy Qt. Bez sensu jest przed kazdym wlaczeniem dziesiatkow nieznanych plikow sprawdzac, jak jest okreslona definicja wlaczajaca. Wyjscie jest nastepujace: tworzymy wlasne definicje.
#ifndef INCLUDED_VECTOR #include <vector> #define INCLUDED_VECTOR #endif // INCLUDED_VECTORTeraz bez wzgledu na to, ile razy w naszym programie umiescimy dyrektywe wlaczajaca plik naglowkowy <vector> zostanie wczytany tylko i wylacznie jeden raz.
I ostania uwaga. Wszystkie te sztuczki wpisujemy tylko i wylacznie w plikach naglowkowych.
W plikach 'cpp' piszemy grzecznie wszystkie potrzebne nam wlaczenia bez zadnych sprawdzen.
1.4.5.Jedna instrukcja w jednej linii.
register( obj2 ); i++; // Zle, latwo przeoczyc druga instrukcje char* buffer1, buffer2; // Zle, buffer2 nie bedzie wskaznikiem ale znakiem
1.4.6.Komentarze.
Standard C++ dopuszcza dwa rodzaje komentarzy.
Przejety z C komentarz w postaci /* ... */.
int CWidget::get_counter()
{
++d_counter;
/*
* To jest komentarz w stylu C.
* Wszystko co tu sie znajduje nie bedzie brane pod
* uwage przez kompilator, nie stanie sie czescia kodu
* wynikowego naszego programu.
* To jest tylko i wylacznie informacja dla programisty.
* Jak widac moze znajdowac sie w wielu liniach.
*/
return d_counter;
}
oraz wlasny jednoliniowy komentarz //:
int CWidget::get_count()
{
++d_counter; // Komentarz w stylu C++. Zakres waznosci tylko do konca linii.
return d_counter;
}
Osobiscie nie jestem zwolennikiem nadmiernej liczby komentarzy
w kodzie programu. Oczywiscie powinny byc, ale w naprawde waznych,
zeby nie powiedziec krytycznych, miejscach programu.... ++d_counter; // zwiekszam licznik o jeden ...wola po prostu o zemste do nieba. Programista, ktory stosuje takie komentarze powinien byc w trybie natychmiastowym rozstrzelany przez swojego szefa.
Czyli: komentarze TAK, ale tam gdzie to jest naprawde niezbedne, a nie gdzie popadnie. Sensowne jest skomentowanie klasy w osobnym dokumencie, zawierajacym szczegoly implementacyjne, opisy zastosowanych algorytmow i diagramy UML.
I ostatnia uwaga. Stosuj wszedzie gdzie sie da komentarze C++. Komentarz C zostaw sobie jako mechanizm wylaczania czesci kodu w czasie testow programu. Wynika to z faktu, ze standardowo komentarze C nie moga sie zagniezdzac.
1.4.7.Nazwy zmiennych
... int rows_number; int cols_number; int current_index; CView* files_tree; ...Jest to stara i dobra metoda zapisu jeszcze z czasow starego dobrego C. Stosowana powszechnie do dzisiaj, m.in. przez team tworzacy jadro Linuksa.
Inna, bardzo powszechna, jesli nie powszechniejsza, jest metoda zapisu przejeta z Javy. Zgodnie z nia, wyrazy tworzace nazwe zmiennej nie dzieli sie podkreslnikiem, ale zapisuje sie je duzymi literami (pierwsza litera zazwyczaj, choc tez nie zawsze, pozostaje mala):
... int rowsNumber; int colsNumber; int currentIndex; CView* filesTree; ...Metoda jest o tyle dobra, ze nazwy zmiennych sa krotsze.
Czytelnosc znaczenia nazwy jest istotna gdy nie widzimy deklaracji zmiennej. W ramach ciala funkcji wymog ten nie jest juz tak istotny. Na pierwszy rzut oka widac deklaracje i latwo sie domyslec znaczenia zmiennej:
...
// wersja nr.1
for( int rows_counter = 0; rows_counter < rows_number; ++rows_counter ) {
...
}
// wersja nr.2
for( int i = 0; i < rows_number; ++i ) {
}
...
Wedlug mnie lepsza jest wersja druga.
Z kontekstu wynika jakie znaczenie ma zmienna sterujaca petla 'for'.
Nadawanie jej nazwy opisowej jest nadmiarowe, wydluza tylko kod.
Nazwy zmiennych skladowych klas (class members).
Aby na pierwszy rzut oka moc rozroznic czy zmienna jest zmienna skladowa
klasy, czy nie, nalezy dodawac na jej poczatku przedrostek 'd_'.
class CButton : public CWidget
{
...
private:
int d_width;
int d_height;
std::string d_txt;
...
};
Jest szkola wywodzaca sie z Microsoftu nadajaca przedrostek m, np 'm_width' lub 'mWidth'.Nazwy parametrow wywolania funkcji.
Kazda funkcja, takze konstruktory, moga miec (choc nie musza) tzw. parametry wywolania,
Dodatkowo funkcja poprzez instrukcje return moze zwracac tylko JEDNA wartosc.
Zarowno w funkcjach jak i konstruktorach czasami moga wystepowac konflikty nazewnicze pomiedzy parametrami
wywolania a zmiennymi skladowymi klasy.
Dlatego tez:
...
CButton::CButton( const CWidget& in_parent )
{
d_parent = in_parent;
// gdyby nie przedrostek trzeba by kombinowac
// z nazwa parametru. A tak wszystko jasne.
}
...
bool CButton::disp_image(
const int in_x, // przyslana wartosc tylko do odczytu
const int in_y, // przyslana wartosc tylko do odczytu
handle& out_handle, // tu jest adres do ktorego cos mamy wpisac
string& inout_label ) // ta zmienna co przynosi, ale takze mamy cos do niej wpisac
{
}
...
W powyzszym przykladzie, w konstuktorze widac logike takich przedrostkow.
Dla obiektu macierzystego (parent) nie trzeba wymyslac innych nazw dla parametrow
wywolania i innych dla zmiennych klasy. Oba parametry nazywaja sie 'parent'.
I jest to oczywiste i czytelne. Dzieki przedrostkom nie ma konfliktu nazw.1.4.8.Stosowanie nawiasow klamrowychi.
...
int CWidget::get_width()
{
...
}
...
while( true == loop ) {
...
...
}
1.4.9.Wciecia.
| Contact: piotr@beesoft.org |
(C) 2006-2008 beesoft.org Last modification date: 2008-06-29 |
Visitis counter: |