Monthly Archives: April 2011


Dobre nawyki w programowaniu – const corectness
April 7, 2011, 6:05 PM in Programming, Technology

Język C++ pierwszy raz zobaczyłem na oczy w 3 klasie gimnazjum, czyli jakieś 6 lat temu. Od tego czasu coś tam sobie grzebałem w tym języku, mniej lub bardziej. Jednak tak poważniej zacząłem programować od jakichś 3 lat. Wcześniej nie zwracałem uwagi na optymalność kodu, prędkość działania, chociaż o czytelność kodu zawsze dbałem ;) Dużo dały mi tutaj studia, które mimo że tak naprawdę nie nauczyły mnie niczego nowego, to zmusiły do refleksji nad moim stylem pisania. Postanowiłem pisać bardziej profesjonalnie.

Jedną z zasad poprawnego pisania kodu w C++ jest tzw. zasada “const corectness” która mówi o tym gdzie powinno się stosować słowo kluczowe const, a gdzie nie. Oczywiście ktoś może powiedzieć, że const oznacza że coś ma nie zmieniać wartości, więc jeśli mam stałą zmienna to dam przed nią const i po problemie. Nie jest to podejście złe, ale dość naiwne, ponieważ const ma także inne zastosowania.

Może jeszcze zanim zacznę wymieniać zastosowania const, wytłumaczę czym różni się zapis

  1. const int PI = 3.14;

od

  1. #define PI 3.14

Oczywiste jest, że różnią one się sposobem w jaki deklarujemy stałą, bo w pierwszym przypadku mamy zmienną o stałej wartości, a w drugim przypadku informację dla preprocesora, że należy wszystkie wystąpienia “PI” zamienić na “3.14”. Dla kompilatora jednak jest to duża różnica, w pierwszym przypadku kompilator widzi że ma do czynienia ze zmienną o stałej wartości i może tej informacji użyć, w drugim przypadku kompilator widzi tylko “3.14”, tak jakby użytkownik tam wpisał tą liczbę.

Skoro to już wytłumaczone, to może przejdę do zastosowań const. Const można stosować przed zmienną, po znaku * w deklaracji wskaźnika, na liście argumentów funkcji oraz w przy funkcjach w programowaniu obiektowym.

Spójrzmy na te trzy linijki kodu:

  1. const int *ptr;
  2. int *const ptr;
  3. const int *const ptr;

W pierwszym przypadku mamy const przed nazwą zmiennej, czyli ptr jest zmiennym wskaźnikiem na stałego inta, możemy zmienić obiekt na który będzie wskazywał ptr, ale nie samą wartość obiektu.
W drugim przypadku mamy const po znaku *, czyli mamy stały wskaźnik na zmienny int, oznacza to, że nie możemy zmienić obiektu na który on wskazuje, możemy jednak dalej zmieniać wartość tego obiektu.
W trzecim przypadku mamy połączenie obu powyższych linijek, mamy stały wskaźnik na stały obiekt. Nie zmienimy ani wskaźnika, ani wartości obiektu.

Zdaję sobie sprawę, że różnice są bardzo subtelne, dlatego pokaże przykład który powinien rozwiać niepewności:

  1. int *ptr; // zwykły wskaźnik na inta
  2. *ptr = 0; // zmieniam wartość inta na który wskazuje wskaźnik
  3. ptr = 0; // zmieniam adres int na który wskazuje wskaźnik
  4.  
  5. const int *constInt; //wskaźnik na stałego inta
  6. *constInt = 0; // chcę zmienić wartość stałego inta – nie przejdzie
  7. constInt = 0; // zmieniam adres wskaźnika – nie ma problemu
  8.  
  9. int *const constPtr; //stały wskaźnik na zmiennego inta
  10. *constPtr = 0; // zmieniam wartość inta – nie ma problemu
  11. constPtr = 0; // chciałem zmienić adres wskaźnika – nie da rady
  12.  
  13. const int *const constIntPtr; // stały wskaźnik na stałego inta
  14. *constIntPtr = 0; // nie da rady
  15. constIntrPtr = 0; // kit :( (a może :) bo przecież tak napisaliśmy kod)

No to teraz opowiem o używaniu const na liście argumentów funkcji. Normalnie gdy deklarujemy funkcję:

  1. void doSomething(int data);

do funkcji zostanie wysłana kopia zmiennej, dlatego zmiany na niej nie będą widoczne poza tą funkcją, aby tego uniknąć wysyłamy zmienne przez referencje:

  1. void doSomething(int &data);

Co jednak jeśli nie chcemy by nasza zmienna mogła być zmieniana, ale jest to duży obiekt i zrobienie jego kopii zajęłoby za dużo czasu/zasobów? Const przybywa na pomoc! Jeśli zdeklarujemy naszą funkcję jako:

  1. void doSomething(const int &data);

do funkcji zostanie wysłana stała referencja, czyli nie będziemy mogli zmienić nic w tej zmiennej i nie będzie robiona kopia.

Ostatnie zastosowanie const o jakim dzisiaj wspomnę to const w funkcjach przy programowaniu obiektowym. Weźmy taki przykład:

  1. class Point
  2. {
  3.   public:
  4.   ()
  5.     void setX(const int &x) { m_x = x; }
  6.     int x() const { return m_x; }
  7.  
  8.   private:
  9.     int m_x;
  10.     int m_y;
  11. };

Jak widać zgodnie z poprzednim punktem funkcja setX przyjmuje stałą referencję, ale w tym przykładzie uwagę należy zwrócić na const przy funkcji x(). W taki sposób użyte const mówi kompilatorowi, że w tej funkcji nie będziemy zmieniać wartości pól klasy (w tym przypadku m_x i m_y). Jest to także użyteczne dla programisty, jeśli przez przypadek będziemy jednak chcieli coś zmienić w klasie z tej funkcji, kompilator nam na to nie pozwoli, użyteczne ;)

I to na tyle na temat używania const w C++. Może niedługo znowu napiszę coś o pisaniu profesjonalnego kodu. ;)


Problem z grą Batman: Arkham Assylum
April 7, 2011, 4:46 PM in Technology

Kilka tygodni temu na Steamie była promocja na grę Batman Arham Assylum więc kupiłem ją sobie. Ściągnęło się wszystko bez problemu, ale gdy chciałem uruchomić grę pojawił się błąd:

Problem signature:
 Problem Event Name: CLR20r3
 Problem Signature 01: BmLauncher.exe
 Problem Signature 02: 1.0.4058.31110
 Problem Signature 03: 4d541d90
 Problem Signature 04: System.Drawing
 Problem Signature 05: 2.0.0.0
 Problem Signature 06: 4ca2bad4
 Problem Signature 07: 7af
 Problem Signature 08: 70
 Problem Signature 09: System.ArgumentException
 OS Version: 6.1.7601.2.1.0.256.1
 Locale ID: 1033

Przeszukałem cały internet wzdłuż i wszerz i nie udało mi się znaleźć rozwiązania tego problemu, forum producenta ani Steama nie okazało się pomocne. Dopiero gdy zacząłem szukać po kodzie błędu okazało się że ten problem nie dotyczy tylko tej gry, ale jest to problem bardziej ogólny. Problem dotyczy braku czcionki wymaganej przez aplikację. Jakiś czas temu bawiłem się czcionkami w systemie i musiałem usunąć którąś. Postanowiłem więc skopiować czcionki z obrazu instalacyjnego Windowsa. Oczywiście pliki na płycie nie są tak po prostu dostępne, są one trzymane w pliku wim. Na szczęście Windows ma wbudowany program do montowania tych plików. Oto co należy zrobić:

Włączamy Windows PowerShell, jeśli mamy Windows 7, Vistę SP1 to znajduje się on w “Start > Wszystkie programy > Akcesoria > Windows PowerShell”, w przeciwnym wypadku – przydałby się update ;)

Gdy mamy już uruchomionego naszego PowerShella (którego kolor tła może się średnio dobrze kojarzyć)

  1. dism /Mount-Wim /wimfile:Z:\sources\install.wim /index:1 /MountDir:C:\win7wim /ReadOnly

Gdzie Z:\ to dysk w którym znajduje się płyta Windowsa, a C:\win7wim to folder gdzie ma zostać zamontowany plik .wim.

Po zamontowaniu w folderze C:\win7wim zobaczymy strukturę folderów która wygląda tak jak świeżo zainstalowany system, teraz możemy skopiować czcionki z tego folderu do folderu w naszym systemie i cieszymy się działającym Batmanem ;)

Chociaż może zanim zaczniemy grać możemy chcieć odmontowac plik .wim, robimy to tak:

  1. dism /Unmount-Wim /MountDir:C:\win7wim /Discard