Wprowadzenie do C++Buildera
Rozdział ten stanowi wprowadzenie do C++Buildera, jednego z najczęściej używanych narzędzi do tworzenia aplikacji internetowych, bazodanowych typu desktop i klient-serwer, programowania rozproszonego itp. C++Builder łączy w sobie charakterystyczne cechy środowiska typu RAD (ang. Rapid Applications Development - błyskawiczne tworzenie aplikacji) z funkcjonalnością ANSI C++. Jest narzędziem na wskroś uniwersalnym, przydatnym zarówno dla amatorów programowania, jak i profesjonalnych zespołów projektowych.
Podstawowe informacje na temat zalet C++Buildera i korzyści wynikających z jego zastosowania znajdziesz na stronie http://www.borland.com/bcppbuilder/ w punktach „Features & Benefits” i „New C++Builder Users”.
Zaawansowani programiści mogą rozdział ten pominąć; pozostali znajdą w nim informacje o podstawowych elementach C++Buildera, a także porównanie jego istotnych cech z innymi środowiskami projektowymi i wskazówki dotyczące przenoszenia na jego grunt aplikacji z tychże środowisk.
Zainteresowani Czytelnicy znajdą tu również wprowadzenie do Kyliksa - jednego z najciekawszych produktów ostatnich lat, będącego w istocie adaptacją Delphi i C++Buildera na gruncie Linuksa. Kylix pozwala tworzyć zaawansowane aplikacje w środowisku tego zdobywającego coraz większą popularność systemu z równą łatwością, jak w środowisku Windows. Większość zagadnień dotyczących C++Buildera, omawianych w tej książce, odnosi się także do Kyliksa.
Podstawowe elementy C++Buildera
W podrozdziale tym przedstawimy podstawowe elementy C++Buildera i zilustrujemy ich zastosowanie na przykładzie prostej aplikacji.
Pierwsze spojrzenie
Jeżeli już zainstalowałeś C++Buildera na swoim komputerze i chciałbyś szybko wykonać za jego pomocą coś pożytecznego - nie ma na co czekać; już za chwilę staną się dla Ciebie jasne takie pojęcia, jak: projekt, komponenty VCL, inspektor obiektów i ich rola w środowisku IDE.
Pierwotnie programiści posługiwali się narzędziami dla DOS-a, uruchamianymi z wiersza poleceń. Kiedy MS Windows zdominowały rynek systemów operacyjnych, w naturalny sposób również i rynek kompilatorów zwrócił się w stronę tego środowiska; jednym z najbardziej znanych kompilatorów C dla Windows był kompilator C++ w wersji 3.1. Nie uprościło to bynajmniej samego procesu programowania, a wręcz przeciwnie: na programiście spoczywała odpowiedzialność za całokształt komunikacji z systemem operacyjnym - obsługa komunikatów, utrzymywanie odpowiedniego wyglądu okienek itp. Nawet prosty program powodujący tylko wyświetlanie pustego okienka miał rozmiar kilkustronicowy.
Sytuacja zmieniła się nieco (na korzyść programistów) po opracowaniu przez Borland narzędzia o nazwie ObjectWindows Library (OWL). OWL, zgodne ze standardami kompilatora wersji 3.1, przejmowało na siebie większość nużących szczegółów systemowych, pozwalając programiście skupić się na istocie rozwiązywanego problemu. Zostało ono rozbudowane wraz z wprowadzeniem na rynek nowego kompilatora C++ w wersji 5.0.
Prawdziwym przełomem okazało się pojawienie Delphi, narzędzia z kategorii RAD, będącego de facto czymś na kształt „wizualnego Pascala” - jeżeli trzymać się analogii do Visual Basica czy też Visual C. Niedługo później Borland opracował analogiczne narzędzie oparte na C++, nadając mu nazwę C++Builder. Korzysta ono z tych samych bibliotek co Delphi, ponadto posiada niemal identyczny interfejs użytkownika. W porównaniu z innym popularnym kompilatorem - Visual C++ - charakteryzuje się większą łatwością tworzenia aplikacji i większą przenośnością tych ostatnich pomiędzy różnymi platformami.
Niewątpliwie dobre opanowanie języka programowania (również C++) wymaga kilku lat praktyki. Narzędzia kategorii RAD - np. C++Builder - mogą jednakże uczynić tę naukę bardziej efektywną, ponadto umożliwiają tworzenie nietrywialnych aplikacji już na wczesnym etapie tej drogi, a to za sprawą obiektowych komponentów VCL, skrywających w sobie wiele skomplikowanych możliwości i uwalniających użytkownika od żmudnego programowania.
Przystąpmy więc do konkretów; pominiemy przy tym wiele szczegółów dotyczących instalacji i obsługi C++Buildera, które znaleźć można w dokumentacji, a których wyczerpujący opis wymagałby odrębnej książki. Skoncentrujemy się za to na metodologii tworzenia konkretnej aplikacji.
Po prawidłowym zainstalowaniu C++Buildera i jego uruchomieniu ukaże się zintegrowane środowisko projektowe (IDE - Integrated Development Environment) przedstawione na rysunku 1.1. Składa się ono z trzech zasadniczych okien, za pośrednictwem których możliwe jest manipulowanie kontrolkami i komponentami, ustawianie ich właściwości oraz wpisywanie kodu programu.
Rysunek 1.1. Środowisko zintegrowane C++Buildera 5
Biblioteka VCL, formularze i komponenty
Biblioteka komponentów (ang. VCL - Visual Component Library) stanowi magazyn komponentów wykorzystywanych do tworzenia aplikacji. Poszczególne komponenty biblioteki odpowiadają poszczególnym komponentom gotowego programu - menu, przyciskom, listom wyboru, obrazkom itp. Komponenty te skrywają w sobie znaczną część kodu, przez co programista zwolniony jest w dużej części z kodowania podstawowych funkcji programu. Poszczególne aspekty zachowania się komponentów mogą być regulowane za pomocą ich właściwości (ang. properties) dostępnych za pośrednictwem inspektora obiektów (patrz rys. 1.1), bądź bezpośrednio w kodzie programu. Oprócz wykorzystywania gotowych komponentów zaawansowany programista ma możliwość tworzenia nowych komponentów, stosownie do swych potrzeb.
Formularze
Poszczególne formularze aplikacji odpowiadają poszczególnym oknom działającego programu; gdy rozpoczynasz tworzenie nowej aplikacji, wyświetlony zostaje automatycznie pusty formularz, zwany formularzem głównym - w działającym programie stanie się on jego oknem głównym. Umieszczając na formularzu komponenty VCL oraz ustalając ich położenie i rozmiary, budujemy żądany interfejs użytkownika. Oprócz komponentów wizualnych istnieją również tzw. komponenty niewidoczne - na etapie projektowania są one reprezentowane przez odpowiednie ikony, natomiast w działającym programie nie są bezpośrednio widoczne w interfejsie użytkownika, jakkolwiek spełniają zadania nie mniej ważne od komponentów wizualnych. Przykładami komponentów niewidocznych są zegary (ang. timers), służące do odmierzania ustalonych odcinków czasu.
Paski narzędziowe
Paski przycisków (ang. speedbars) umożliwiają szybki dostęp do najczęściej wykorzystywanych opcji menu, jak: Run (uruchomienie aplikacji), Step Through (praca krokowa), View Form (oglądanie formularza) itp. Użytkownik ma możliwość przystosowywania zawartości pasków narzędziowych do swoich potrzeb.
Konfigurowanie pasków narzędziowych
Jedną z niezaprzeczalnych zalet środowiska IDE jest jego konfigurowalność. Zawartość dowolnego paska narzędziowego może być łatwo zmieniana poprzez dodawanie i usuwanie przycisków - wyjątkiem w tym względzie jest paleta komponentów, nie zawierająca przycisków, lecz (zgodnie z nazwą) komponenty VCL.
C++Builder zawiera następujące paski narzędziowe:
Standard;
View;
Debug;
Custom;
Component Palette (paleta komponentów).
Aby zmienić zawartość dowolnego paska narzędziowego, wykonaj kolejno następujące czynności:
Kliknij prawym przyciskiem myszy gdziekolwiek w wolnym obszarze paska (poza przyciskami).
Z wyświetlonego menu kontekstowego wybierz opcję Customize.
Wyświetlone zostanie okno dialogowe konfiguracji (rys. 1.2); przejdź na jego stronę Commands.
Przeciągnij żądane polecenia z okna dialogowego na pasek.
Jeżeli chcesz usunąć któryś z przycisków, przeciągnij go po prostu poza obszar paska.
Rysunek 1.2. Okno dialogowe konfiguracji paska narzędziowego
Paleta komponentów
Paleta komponentów, znajdująca się bezpośrednio pod menu głównym, stanowi zasobnik komponentów VCL. Komponenty te zorganizowane są w strony, odpowiadające poszczególnym kategoriom. Aby przenieść komponent na formularz, należy kliknąć jego ikonę w palecie komponentów, a następnie kliknąć w miejscu, w którym ma się on docelowo znaleźć. Jak już wcześniej wspomniano, wygląd i szczegóły funkcjonowania komponentów mogą być regulowane za pomocą właściwości, dostępnych za pośrednictwem inspektora obiektów lub bezpośrednio w kodzie programu.
Zdarzenia i ich obsługa
Tym, co decyduje o dynamice działającego programu, są zdarzenia (ang. events). Aby wyjaśnić ich istotę, zbudujemy prostą aplikację w postaci pojedynczego przycisku na formularzu (rys. 1.3). Przyciski są niezwykle często spotykanymi elementami interfejsu użytkownika, który za ich pośrednictwem poleca aplikacji wykonanie określonych czynności.
Komponent odpowiadający przyciskowi umieszczony jest w palecie na stronie Standard pod nazwą Button; jego ikona przedstawia przycisk z napisem OK. Aby przenieść ów komponent na formularz, należy kliknąć najpierw tę ikonę, a następnie odpowiednie miejsce formularza.
Rysunek 1.3. Prosta aplikacja - pojedynczy przycisk na formularzu
Tak przygotowaną aplikację można już skompilować i uruchomić, lecz nie okaże się ona szczególnie użyteczna: klikanie w przycisk nie będzie powodowało żadnych widocznych efektów. Dzieje się tak dlatego, iż zdarzenie generowane w wyniku kliknięcia przycisku nie jest w żaden sposób obsługiwane. Darujmy sobie w tym momencie ścisłą definicję „zdarzenia”; na użytek tworzonej aplikacji można przyjąć, iż w wyniku wystąpienia zdarzenia wywoływana jest odpowiednia funkcja, zwana funkcją obsługi zdarzenia (ang. event handler). Aby przypisać funkcję obsługującą kliknięcie przycisku, zakończmy uruchomioną aplikację, powracając do etapu projektowania i kliknijmy ów przycisk dwukrotnie (lewym przyciskiem myszy); C++Builder wyświetli wówczas fragment kodu aplikacji z wygenerowanym szkieletem odpowiedniej funkcji obsługi:
void __fastcall TForm1::Button1Click(Tobject *Sender)
{
}
Ten sam efekt można wywołać, wybierając komponent Button1 w inspektorze obiektów, przechodząc na stronę Events i klikając dwukrotnie pozycję odpowiadającą zdarzeniu OnClick. Jak łatwo zauważyć, nazwa funkcji składa się z nazwy odpowiedniego komponentu (Button1) i nazwy zdarzenia (Click).
Po kliknięciu prawym przyciskiem myszy okna edytora kodu i wybraniu z menu kontekstowego pozycji Open Source/Header File C++Builder wyświetli następujący kod:
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
void __fastcall Button1Click(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Powyższy kod generowany jest automatycznie i jego szczegóły nie są w tej chwili szczególnie istotne; zamieściliśmy go tutaj raczej w celu pokazania, w jak dużym stopniu C++Builder potrafi wyręczyć programistę. Powróćmy jednak do naszej funkcji obsługi, obecnie jeszcze pustej. Sprawmy, by kliknięcie przycisku na formularzu powodowało zakończenie działania programu - w tym celu kliknijmy w oknie edytora kodu zakładkę Unit1.cpp i uzupełnijmy rzeczoną funkcję obsługi następująco:
void __fastcall TForm1::Button1Click(Tobject *Sender)
{
ShowMessage("Witam!To jest aplikacja testowa. Naciśnij OK");
Close();
}
Teraz należy skompilować i uruchomić aplikację - można to zrobić na jeden z trzech sposobów:
naciskając klawisz F9;
wybierając opcję Run z menu Run;
klikając przycisk (na pasku narzędziowym) w kształcie zielonego trójkącika.
Kliknięcie przycisku na formularzu w uruchomionym programie spowoduje teraz wywołanie funkcji TForm1::Button1Click, której treść jest raczej intuicyjnie jasna nawet dla początkującego programisty: otóż najpierw wyświetlone zostanie okienko ze standardowym komunikatem (polecenie „Naciśnij OK.” odnosi się do przycisku w tymże oknie, nie do naszego przycisku na formularzu - przyp. tłum.), po którego zamknięciu wywołana zostanie metoda Close() naszego formularza głównego, zamykająca ów formularz i tym samym kończąca wykonanie programu.
Pierwsze programy
Pokażemy teraz, jak łatwo jest zbudować niewielkim wysiłkiem całkiem przyzwoity program - oczywiście jeżeli ma się pod ręką C++Buildera. Wybierz z menu głównego opcję File|New Application; C++Builder stworzy nowy projekt i wyświetli pusty formularz główny.
Przejdź na stronę Additional w palecie komponentów i kliknij komponent Image (rys. 1.4):
Rysunek 1.4. Komponent Image
Kliknij następnie formularz - komponent Image zaprezentuje się w postaci czarnej ramki. Przejdź do inspektora obiektów, znajdź właściwość Stretch i ustaw ją na true.
Następnie przejdź na stronę Dialogs palety komponentów (być może konieczne okaże się przewinięcie palety w lewo) i kliknij najpierw komponent OpenDialog (rys. 1.5), a następnie kliknij w pobliżu lewego górnego narożnika formularza, gdzie powinna pojawić się ikona tego komponentu.
Rysunek 1.5. Komponent OpenDialog
W inspektorze obiektów, wskazującym komponent OpenDialog1, znajdź właściwość Filter i wpisz w jej pole wartości następujący tekst:
Bitmapy|*.BMP
W ten oto sposób stworzyłeś komponent dialogowy, za pomocą którego będzie możliwy wybór plików z rozszerzeniem .BMP.
Kolej teraz na dwa przyciski, z których pierwszy inicjował będzie akcję wczytywania pliku, drugi natomiast posłuży do zakończenia pracy aplikacji. Komponent przycisku Button znajduje się w palecie na stronie Standard (rys. 1.6); w czasie jego klikania przytrzymaj klawisz Shift - spowoduje to, iż komponent pozostanie zaznaczony i każdorazowe kliknięcie w obszarze formularza powodować będzie umieszczenie na nim kolejnego przycisku, do czasu kliknięcia w białą strzałkę na początku palety. Dla naszej aplikacji potrzebne są dwa przyciski - C++Builder automatycznie nada im nazwy Button1 i Button2.
Rysunek 1.6. Komponent Button
Wypadałoby nadać pierwszemu z przycisków tytuł adekwatny do roli, jaką ma on pełnić w aplikacji - w tym celu kliknij go, a następnie w inspektorze obiektów przypisz właściwości Caption napis Wczytaj.
Kolejnym komponentem naszej aplikacji będzie pasek statusu, na którym wyświetlana będzie nazwa wczytanego ostatnio pliku. Przejdź do strony Win32 w palecie komponentów i kliknij komponent StatusBar (rys. 1.7); gdy klikniesz formularz, pasek stanu automatycznie usadowi się przy jego dolnej krawędzi.
Rysunek 1.7. Komponent StatusBar
Używając inspektora obiektów ustaw właściwość SimplePanel paska StatusBar1 na true.
Należy teraz sprawić, by przycisk opatrzony tytułem „Wczytaj” istotnie powodował wczytanie pliku bitmapy; jak zapewne pamiętasz, jest to kwestia właściwego obsłużenia generowanego przez ów przycisk zdarzenia OnClick. Kliknij go dwukrotnie i uzupełnij wyświetlony szkielet funkcji zdarzeniowej do następującej postaci:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (OpenDialog1->Execute())
Image1->Picture->LoadFromFile(OpenDialog1->FileName);
StatusBar1->SimpleText = OpenDialog1->FileName;
}
Wyświetlone okno edytora kodu przysłoniło formularz, ale to żaden kłopot - za pomocą klawisza F12 można łatwo przełączać się pomiędzy formularzem i odpowiadającym mu modułem kodu źródłowego; można w tym celu użyć również stosownego przycisku na pasku narzędziowym (rys. 1.8).
Rysunek 1.8. Przycisk przełączający pomiędzy edytorem kodu a formularzem
Pozostaje jeszcze obsłużyć przycisk Button2. Używając inspektora obiektów, nadaj mu tytuł Zamknij, następnie klikając go dwa razy uzupełnij jego funkcję zdarzeniową w następujący sposób:
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Close();
}
Uruchom teraz aplikację, naciskając klawisz F9. Kliknięcie przycisku „Wczytaj” spowoduje otwarcie okna dialogowego, za pomocą którego wybrać można plik, który wyświetlony zostanie za pomocą komponentu Image. Na użytek naszej aplikacji wykorzystamy bitmapę Instalator.bmp wyświetlaną podczas instalacji Windows 98 (rys. 1.9).
Rysunek 1.9. Wyświetlenie grafiki załadowanej do komponentu Image
Przyjrzyjmy się teraz nieco dokładniej temu, co tak naprawdę dzieje się w działającej aplikacji. Jej centralnym punktem jest prostokąt, w którym wyświetlana jest zawartość wczytanej bitmapy. Wyświetlanie pliku *.BMP nie jest bynajmniej sprawą trywialną: poza poszczególnymi pikselami może ona (chociaż nie musi) zawierać również informacje o palecie, przy użyciu której będzie wyświetlana, ponadto plik noszący rozszerzenie *.BMP niekoniecznie musi mieć format charakterystyczny dla bitmapy, co oczywiście należy zasygnalizować jako błąd. „Tradycyjne” zaprogramowanie tych czynności wymagałoby kilkustronicowego kodu, podczas gdy C++Builder załatwia całą sprawę za pomocą jednego gotowego komponentu.
Sam wybór pliku do wyświetlenia również jest czynnością na swój sposób skomplikowaną - mamy wszak do czynienia z przeszukiwaniem hierarchicznej struktury dysków i folderów, nie są ponadto uwzględniane pliki inne niż te noszące rozszerzenie .BMP (decyduje o tym właściwość Filter komponentu OpenDialog). Wybór konkretnego pliku jest w środowisku Windows czynnością na tyle powszechną, iż została ona zautomatyzowana w postaci jednego z tzw. powszechnych narzędzi dialogowych; komponent OpenDialog stanowi obudowaną postać tegoż narzędzia, przystosowaną do platformy C++Buildera.
Właściwe reakcje aplikacji na klikanie przycisków „Wczytaj” i „Zamknij” zapewnione są dzięki odpowiedniemu oprogramowaniu funkcji zdarzeniowych. Zdarzenia C++Buildera stanowią „obudowę” mechanizmów bardziej prymitywnych - komunikatów Windows - i są od nich nieporównanie bardziej wygodne w użyciu; zamiast (znowu) kilkustronicowego kodu mamy jedynie dwie krótkie funkcje obsługi zdarzeń.
Reasumując - gdyby zaprogramować omawianą aplikację w sposób tradycyjny, skończyłoby się na dwudziestokilkustronicowym kodzie; wobec rozmiaru kodu, który faktycznie musieliśmy napisać, zalety C++Buildera stają się wyraźnie widoczne.
Stwórzmy więc jeszcze jedną aplikację. Wybierz z menu File opcję New Application; C++Builder zapyta Cię, czy chcesz zachować istniejącą aplikację (Save changes to project…?) - nie musisz tego robić, gdyż znajduje się ona na dołączonej do książki płycie CD-ROM; kliknij więc przycisk No. Zostanie zainicjowany nowy projekt i wyświetlony czysty formularz.
Rozpocznij od zapisania plików nowego projektu. W tym celu wybierz opcję Save Project As z menu File; C++Builder wyświetli kolejno dwa okna dialogowe związane z dwoma plikami projektu.
Pierwsze ze wspomnianych okien (rys. 1.10) dotyczyć będzie modułu głównego, noszącego rozszerzenie cpp, związanego z formularzem głównym. Jako że generalnie łatwiej panować nad projektami, jeżeli przechowywane są one w oddzielnych katalogach, rozpocznij od utworzenia nowego podkatalogu dla projektu, klikając stosowny przycisk okna; C++Builder utworzy katalog o nazwie Nowy folder - zmień tę nazwę na Composers i przejdź do utworzonego katalogu klikając go dwukrotnie. Następnie zmień nazwę modułu (w wierszu Nazwa pliku:) na Mainform i kliknij przycisk Zapisz.
Rysunek 1.10. Zapisywanie modułu głównego
Zapisywanie pliku projektu (plik ten nosi rozszerzenie .bpr) odbywa się podobnie - z tą różnicą, iż domyślnym katalogiem w oknie dialogowym będzie katalog przed chwilą utworzony; zmień proponowaną nazwę tego pliku (Project2) na bardziej sugestywną, na przykład ComposersProj. Tak naprawdę zapisywane są tutaj dwa pliki - oprócz pliku .bpr tworzony jest również plik .cpp z tą samą nazwą, jednak nie wynika to w żaden sposób z treści dialogu.
Umieść teraz na formularzu dwa komponenty ListBox - znajdują się one na stronie Standard palety komponentów. C++Builder nada im automatycznie nazwy ListBox1 i ListBox2. Konkretna ich lokalizacja na formularzu nie jest szczególnie istotna, wskazane jest jednak, by wyrównane były w poziomie.
Pod komponentami ListBox umieść następnie komponent Edit, również ze strony Standard. C++Builder nada mu automatycznie nazwę Edit1. Przejdź do inspektora obiektów i wyczyść pole związane z jego właściwością Text.
Bezpośrednio przed komponentem Edit1 umieść komponent Label; za pomocą inspektora obiektów zmień jego tytuł (właściwość Caption) na „Nowy:”.
Dwa ostatnie komponenty projektu to przyciski (komponenty Button - C++Builder nada im nazwy Button1 i Button2); za pomocą inspektora obiektów nadaj im tytuły (właściwość Caption) „Dodaj” i „Usuń”.
Ze względów estetycznych możesz zmniejszyć formularz projektu, chwytając kursorem myszy i przesuwając prawą jego krawędź, następnie robiąc to samo z krawędzią dolną. Wygląd formularza projektu z kompletem opisanych przed chwilą komponentów pokazuje rysunek 1.11.
Rysunek 1.11. Kompletny formularz projektu ComposersProj
Kolej teraz na oprogramowanie zdarzeń związanych z poszczególnymi komponentami. Pierwsze ze zdarzeń dotyczyć będzie samego formularza - a dokładniej tego, co automatycznie dzieje się przy jego tworzeniu. Zdarzenie to nosi nazwę OnCreate i w naszym projekcie wykorzystane zostanie do załadowania lewej listy (komponent ListBox1) domyślną zawartością. Kliknij więc gdziekolwiek w wolnym obszarze formularza (nie żaden z komponentów!), przejdź do inspektora obiektów (który w górnym okienku powinien wskazywać Form1 jako wybrany komponent) i na zakładce Events zlokalizuj pozycję odpowiadającą zdarzeniu OnCreate i kliknij ją dwukrotnie; C++Builder wyświetli wówczas następujący szkielet funkcji zdarzeniowej:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
}
który należy wypełnić następującym kodem:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
ListBox1->Items->Add("Cesar Franck");
ListBox1->Items->Add("Charles Marie Widor");
ListBox1->Items->Add("Louis Vierne");
ListBox1->Items->Add("Maurice Durufle");
ListBox1->Items->Add("Jehan Alain");
}
Każdy z wierszy powoduje dodanie jednej pozycji do listy ListBox1; nazwiska kompozytorów są tu oczywiście przykładowe i mogą zostać zmienione bez szkody dla projektu.
Zajmijmy się teraz przyciskami Dodaj i Usuń. Kliknięcie pierwszego z nich spowoduje dodanie do prawej listy (ListBox2) podświetlonej pozycji z lewej listy (ListBox1). Indeks podświetlonej pozycji listy reprezentowany jest przez jej właściwość ItemIndex, wszystkie zaś pozycje zgrupowane są w tablicy będącej pod właściwością Strings właściwości Items; ta ostatnia odpowiedzialna jest za całokształt zarządzania pozycjami listy - na przykład jej metoda Add powoduje dodanie nowej pozycji do listy. Zgodnie z tym funkcja zdarzeniowa związana z lewym przyciskiem (jej szkielet wyświetlony zostanie po dwukrotnym kliknięciu tego przycisku) wyglądać powinna następująco:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
String GetListItem = ListBox1->Items->Strings[ListBox1->ItemIndex];
ListBox2->Items->Add(GetListItem);
}
Usuwanie pozycji z prawej listy (ListBox2) jest nieco mniej skomplikowane - funkcja zdarzeniowa prawego przycisku prezentuje się następująco:
void __fastcall TForm1::Button2Click(TObject *Sender)
{
ListBox2->Items->Delete(ListBox2->ItemIndex);
}
Wpisanie czegokolwiek do okienka komponentu Edit1 i naciśnięcie klawisza Enter spowoduje powielenie zawartości tego okienka w obydwu listach. Aktualna zawartość komponentu Edit ukrywa się pod jego właściwością Text, zaś każdorazowe naciśnięcie klawisza (w sytuacji, gdy komponent ten jest aktywnym komponentem formularza) generuje zdarzenie OnKeyPress. Jednym z parametrów funkcji obsługującej to zdarzenie jest kod naciśniętego klawisza, nietrudno więc wykorzystać tę funkcję do skopiowania zawartości reprezentowanej przez właściwość Text do obydwu list:
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if (Key==13)
{
ListBox2->Items->Add(Edit1->Text);
ListBox1->Items->Add(Edit1->Text);
}
}
Wartość 13 jest kodem klawisza Enter; bardziej elegancko byłoby użyć w tej roli stałej symbolicznej VK_ENTER. Aby uzyskać szkielet powyższej funkcji, należy najpierw kliknąć jednokrotnie komponent Edit1 (by uczynić go komponentem aktywnym), a następnie kliknąć dwukrotnie pozycję odpowiadającą zdarzeniu OnKeyPress na stronie Events inspektora obiektów.
Po wykonaniu opisanych czynności należy zapisać zmienioną zawartość projektu za pomocą opcji Save All menu File, dostępnej również przez klawisz skrótu Shift+Ctrl+S. Tak naprawdę zapisywanie projektu w trakcie jego tworzenia powinno odbywać się jak najczęściej, bowiem w przypadku ewentualnej awarii (np. wyłączenia zasilania) tracimy tylko tę część pracy, którą wykonaliśmy po ostatnim zapisie.
Projekt gotowy jest do uruchomienia - na przykład przez naciśnięcie klawisza F9. Ukaże się wówczas okno główne, w którym lewa lista wypełniona będzie zawartością początkową. Klikając w którąkolwiek z jej pozycji, spowodujemy jej podświetlenie, zaś kliknięcie w przycisk Dodaj spowoduje skopiowanie podświetlonej pozycji do prawej listy. Do usuwania podświetlonych pozycji prawej listy służy przycisk Usuń. Do wprowadzania nowych pozycji służy komponent Edit1 - jeżeli jest on komponentem wybranym, naciśnięcie klawisza Enter spowoduje skopiowanie jego bieżącej zawartości do obydwu list.
Opisaną aplikację zbudować można niemalże w ciągu kilku minut - jeżeli porównać to z czasem niezbędnym do stworzenia podobnej aplikacji w środowisku Visual C++ lub Microsoft Foundation Classes (MFC), ponownie uwidaczniają się zalety C++Buildera jako narzędzia kategorii RAD.
Kilka pytań
Oto kilka pytań związanych z C++Builderem (i ogólnie - z narzędziami typu RAD) zadawanych przez początkujących programistów:
W jaki sposób mogę podejrzeć kod pliku głównego projektu i modułów związanych z poszczególnymi formularzami?
Służy do tego menedżer projektu, którego okno wyświetlone zostaje w wyniku wybrania opcji View|Project Manager lub naciśnięcia klawiszy Ctrl+Alt+F11. Dzięki niemu dostępny jest również (po skompilowaniu projektu) kod źródłowy modułów bibliotecznych. Główny plik źródłowy projektu (ten z nazwą projektu i rozszerzeniem .cpp) można również obejrzeć za pomocą opcji Project|View Source.
W jaki sposób mogę zmieniać właściwości komponentów?
Temu celowi służy inspektor obiektów, dostępny poprzez opcję View|Object Inspector lub przez klawisz skrótu F11. Niektóre właściwości danego komponentu mogą być jednak niedostępne w czasie projektowania i niewidoczne w oknie inspektora obiektów - właściwości te mogą być odczytywane i modyfikowane jedynie bezpośrednio w kodzie programu.
Co mam zrobić, jeżeli operowanie myszą nie pozawala mi na osiągnięcie żądanej precyzji ułożenia komponentów na formularzu?
Przesuwać wybrany komponent można za pomocą klawiszy strzałek, trzymając jednocześnie wciśnięty klawisz Ctrl; umożliwia to pozycjonowanie komponentów z dokładnością do pojedynczych pikseli.
Po uruchomieniu program zawiesił się, a ekran upstrzony został jakimiś dziwnymi okienkami. W jaki sposób mogę przywrócić normalną sytuację?
C++Builder umożliwia zresetowanie działającego programu w dowolnej chwili. Należy w tym celu nacisnąć kombinację Ctrl+F2 lub wybrać z menu głównego opcję Run|Program Reset. Program zostanie wówczas zatrzymany i sterowanie powróci do IDE.
W jaki sposób mogę opatrzyć okno programu swoją własną ikoną?
Należy otworzyć okno opcji projektu - za pomocą opcji Project|Options menu głównego lub kombinacji klawiszy Shift+Ctrl+F11- przejść na kartę Application, nacisnąć przycisk Load Icon i za pomocą wyświetlonego okna dialogowego wybrać stosowny plik ikony. Jeżeli plik ten jeszcze nie istnieje, można go stworzyć, używając edytora obrazów, uruchamianego za pomocą opcji Tools|Image Editor. Po przypisaniu ikony konieczna jest kompletna rekompilacja projektu - wykonuje się ją, wybierając opcję Project|Build All Projects z menu głównego IDE.
W jaki sposób mogę zmienić standardowy tytuł Form1 wyświetlany na pasku tytułowym okna mojego programu?
Tytuł ten ukrywa się pod właściwością Caption formularza projektu, można więc go zmienić za pomocą inspektora obiektów.
Czy istnieje szybszy dostęp do elementów IDE niż nawigowanie wśród opcji menu głównego?
Służą temu przyciski paska narzędziowego (speedbar) umieszczonego standardowo nad inspektorem obiektów. Jeżeli nie jesteś pewien znaczenia danego przycisku, zatrzymaj na nim kursor myszy przez kilka sekund, co spowoduje wyświetlenie treści równoważnej opcji menu w formie podpowiedzi.
W jaki sposób mogę zabezpieczyć się przez przypadkowym przesunięciem komponentu na formularzu w trakcie projektowania aplikacji?
Do zablokowania pozycji i rozmiaru komponentów służy opcja Edit|Lock Controls. Po jej wybraniu komponenty formularza stają się niewrażliwe na klawisze strzałek i ruchy kursora myszy. W dalszym ciągu możliwe jest jednak modyfikowanie położenia i rozmiaru komponentów za pomocą inspektora obiektów. Ponowne wybranie opcji Edit|Lock Controls odblokowuje komponenty.
Co nowego w wersji 5. C++Buildera?
Jak już wcześniej wspominaliśmy, droga rozwojowa C++Buildera spleciona jest w pewnym stopniu z Delphi, tak więc również w przypadku najnowszej jego wersji większość (choć nie wszystkie!) nowości pojawiła się już w ostatniej wersji Delphi - Delphi 5. Nowości te związane są głównie z programowaniem internetowym, aplikacjami rozproszonymi, projektowaniem zespołowym i ogólnie pojętą produktywnością programisty. Większość z nich omówiona zostanie szczegółowo w dalszej części książki.
C++Builder 5 dostępny jest w trzech wersjach: Standard (Std), Professional (Pro) i Enterprise (Ent). Wersja Standard jest oczywiście wersją najuboższą, mimo to jest jednak funkcjonalnym narzędziem do programowania w Windows, zawierającym ponad 85 komponentów, uznany powszechnie kompilator i debugger oraz wiele innych użytecznych możliwości. Wersja Professional posiada ponad 150 komponentów oraz dodatkowe narzędzia bazodanowe, śledzenie wieloprocesowe i specjalizowane narzędzie CodeGuard™. Najbogatsza z wersji - Enterprise - oferuje ponad 200 komponentów związanych m.in. z technologią CORBA, MS SQL Serwerem 7, Oracle8i, technologią MIDAS, programowaniem internetowym itp., a także środki ułatwiające tworzenie aplikacji w wielu wersjach językowych oraz menedżer kontroli kodu źródłowego w warunkach pracy zespołowej (TeamSource).
Z wersji 5. C++Buildera usunięto natomiast PVCS Version Control firmy Merant (dawniej Intersolv).
Kompletny wykaz nowości w każdej z wersji C++Buildera 5 (Standard, Professional, Enterprise) dostępny jest pod adresem http://www.borland.com/bcppbuilder w punkcie „Feature List”, większość z nich opisana jest również w systemie pomocy pod hasłem „What's New”.
Większość z nowości opisywanych w tym rozdziale dotyczy wersji Professional i Enterprise - z wyjątkiem miejsc, gdzie wyraźnie zaznaczono konkretną wersję za pomocą skrótów „Std”, „Pro” i „Ent”; skrót „All” oznacza, iż opisywany mechanizm odnosi się do wszystkich wersji. W następnych rozdziałach książki zrezygnowaliśmy jednak z takiego rozróżniania; informacje o stosowaniu konkretnych mechanizmów w poszczególnych wersjach dostępne są w miejscach przed chwilą wymienionych.
Programowanie internetowe
C++Builder dawno już zdobył sobie uznanie programistów jako poręczne narzędzie do tworzenia aplikacji internetowych. Możliwości C++Buildera w tym względzie zostały w wersji 5. wzbogacone m.in. o: kreator Active Server Page (ASP) Application Wizard, komponenty Internet Express (Ent), nowy komponent przeglądarki WWW i rozszerzenia komponentu WebBroker.
Komponenty Internet Express (Ent) umożliwiają tworzenie aplikacji „cienkiego” klienta sieci WWW, udostępniającego dane z serwerów MIDAS-a i zaplecza bazodanowego (back-end databases) na podstawie języka XML i HTML 4. Aplikacje te są znacznie prostsze i mniejsze niż tradycyjne implementacje „grubego” klienta; nie dokonują bezpośredniego dostępu do bazy danych, nie wymagają więc instalowania BDE (Borland Database Engine) na komputerach klientów.
Komponent WebBroker, obecny dotąd jedynie w wersji Enterprise, teraz dostępny jest również w wersji Professional; został on przystosowany do obsługi HTML 4. Najważniejszą nowością w zakresie programowania internetowego jest natomiast nowy komponent przeglądarki WWW, umożliwiający integrację tworzonej aplikacji z językiem HTML. To, wydawałoby się drobne, rozszerzenie ma jednak niebagatelne skutki praktyczne, między innymi:
możliwość łatwego tworzenia dedykowanych przeglądarek WWW, przystosowanych do specyficznych warunków, na przykład do przeglądania jedynie zasobów firmowego intranetu;
możliwość tworzenia interfejsu użytkownika aplikacji w postaci HTML. Stwarza to znaczne korzyści - „dialogowa” strona aplikacji może być powierzona nieprogramistom, zaś firmowi specjaliści od tworzenia stron WWW mogą być zaangażowani również w tworzenie aplikacji. Możliwe jest także łatwe zróżnicowanie interfejsu granicznego poszczególnych użytkowników, stosownie do ich kwalifikacji czy też posiadanych uprawnień, ponadto dystrybucja nowej wersji interfejsu użytkownika sprowadza się po prostu do ściągnięcia jej z Internetu;
integracja języka HTML z systemem pomocy aplikacji, co czyni tworzenie plików pomocy równie łatwym, jak tworzenie stron WWW.
Możliwości te zostały wykorzystane w kilku znanych produktach, między innymi MS Office 2000 i MS Encarta, ponadto serwisy America Online (AOL) i CompuServe tworzą swoje zaawansowane aplikacje na podstawie kontrolek przeglądarki WWW.
Aplikacje rozproszone
Implementacja specyfikacji CORBA (Ent) rozszerzona została o obsługę Visibrokera ORB 4.00 oraz elementów specyfikacji charakterystycznych dla wersji CORBA 2.3. Z innych tego typu rozszerzeń wymienić należy: Portable Object Adapter i Object By Value oraz usprawnienie edytora biblioteki typów (Visual TypeLibrary Editor), rozszerzenia repozytorium interfejsów (Interface Repository), a także nowe kreatory CORBA.
MIDAS (Ent) rozszerzony został o: obsługę języka XML, bezstanowy komponent DataBroker, nowe komponenty Web Connection, pulę połączeń (server object pooling) i opcje dostarczyciela danych (provider options).
Projektowanie zespołowe
Narzędzie do zespołowego projektowania aplikacji o nazwie TeamSource, dostarczane wraz z wersją Enterprise i dostępne za dodatkową opłatą dla wersji Professional, stanowi interfejs czołowy (front-end) menedżera kontroli wersji, umożliwiającego równoległą pracę wielu programistów nad pojedynczym projektem. Każdy programista pracuje na odrębnej lokalnej kopii kodu źródłowego, zaś zadaniem wspomnianego menedżera jest synchronizacja każdej kopii z głównymi źródłami; narzędzia do wizualnego porównywania modułów ułatwiają rozwiązywanie ewentualnych konfliktów powstających przy tej okazji. TeamSource śledzi historię zmian kodu, umożliwia także zaznaczenie konkretnej postaci źródeł jako jedną z wersji błyskawicznie dostępną na ewentualne późniejsze żądanie.
Mechanizm zaplecza (back-end) mechanizmu zarządzania wersją realizowany jest przez osobną część oprogramowania. C++Builder 5 (Ent) wykorzystuje w tym celu bibliotekę Borland Zlib oraz udostępnia interfejs dla - sprzedawanego teraz oddzielnie - PVCS firmy Merant. Możliwe jest również konstruowanie własnych „wtyczek” (plug-in) dla obsługi przez TeamSource innych programów zaplecza.
Lokalizacja aplikacji
Nowością C++Buildera 5 (Ent) są narzędzia umożliwiające tłumaczenie aplikacji pomiędzy różnymi platformami językowymi oraz równoległe tworzenie kilku wersji językowych: Translation Suite, Translation Repository, RC Translator i DFM Translator; narzędzia te dostępne są również za dodatkową opłatą dla wersji Professional.
Również kreator Resource DLL Wizard został rozszerzony o możliwości łatwej zmiany wersji językowej aplikacji.
Śledzenie
Bodaj jedną z najbardziej spektakularnych nowości C++Buildera 5 jest narzędzie CodeGuard™. Oferuje ono rozbudowane narzędzia śledzenia, umożliwiające łatwe wykrywanie błędów i ich przyczyn, między innymi „wycieków” pamięci oraz gubienia zasobów w postaci np. uchwytów plikowych. Możliwe jest również (All) zaprogramowanie różnorodnych akcji związanych z punktami przerwań (breakpoints) i ich grupowanie, jak również czytelny podgląd instrukcji zmiennoprzecinkowych i MMX.
Programowanie bazodanowe
Najważniejszymi nowościami C++Buildera 5 w zakresie tworzenia aplikacji bazodanowych są: DataModule Designer, InterBase Express i ADO Express.
Projektant modułów bazodanowych Datamodule Designer po raz pierwszy pojawił się w Delphi 5 - jest narzędziem wizualnym, uwidaczniającym w czytelnej postaci relacje (np. master-details) i zależności zachodzące pomiędzy komponentami modułu danych (Datamodule).
InterBase Express umożliwia tworzenie zaawansowanych aplikacji bazodanowych bez użycia BDE. Ulepszona wersja SQL Monitora ułatwia kontrolowanie szczegółów dostępu do danych.
ADO Express to grupa komponentów stanowiących otoczkę technologii MS ActiveX Data Object (ADO) i OLEDB. Komponenty te pozwalają na tworzenie aplikacji obsługujących relacyjne i nierelacyjne bazy danych, arkusze kalkulacyjne, różnorodne systemy plików i pocztę elektroniczną. Aplikacje stworzone na bazie ADO Express nie korzystają z BDE.
Dopełnieniem bazodanowych możliwości C++Buildera 5 jest najnowsza wersja Interbase (5.6) oraz obsługa MS SQL Servera 7 i Oracle8i.
Produktywność programisty
C++Builder 5 nie pozostaje w tyle również pod względem środków zwiększających produktywność programisty. Wielowątkowa kompilacja w tle (All) umożliwia kontynuowanie pracy nad projektem w czasie, gdy kompilator wykonuje swoją pracę. Możliwe jest również zaaranżowanie dwóch różnych postaci IDE (Customizable Desktop Settings) osobno dla śledzenia programu, osobno dla „normalnego” uruchomienia - przełączanie pomiędzy nimi odbywa się automatycznie.
Znacznym ułatwieniem w procesie stopniowego budowania projektu są komentarze o specjalnej postaci (ang. To-Do), rozpoczynające się znakami //TODO: i przypominające użytkownikowi o rzeczach, które pozostały jeszcze do zrobienia w kontekście określonego miejsca w kodzie projektu. Do zarządzania nimi służy specjalny menedżer dostępny poprzez opcję View|To-Do List.
Dodano również kilka kreatorów, między innymi: Windows 2000 Client Logo Application Wizard (All), Console Application Wizard (All), Control Panel Applet Wizard oraz dwa proste kreatory związane z językiem C i C++.
Rozszerzono także obsługę Visual C++, m.in. w zakresie Microsoft Foundation Classes (MFC) 6.0 i Active Template Library (ATL) 3.0. Całości dopełniają ułatwienia w rodzaju podziału na właściwości na kategorie (All) i wizualizacja właściwości graficznych (All) w inspektorze obiektów, programowalne mapowanie klawiszy w edytorze kodu (All) i import serwerów automatyzacji jako komponentów.
Zgodność i unowocześnianie aplikacji
C++Builder 5 pojawił się oficjalnie na rynku 22 marca 2000 roku. W chwili gdy piszemy te słowa, znanych jest kilka problemów związanych ze zgodnością istniejących projektów z poprzednich wersji oraz narzędzi niezależnych producentów; wykryto również kilka błędów C++Buildera 5 w tym względzie - oficjalny ich wykaz znajduje się pod adresem http://www.borland.com/bcppbuilder/ w punktach „Updates and Patches” i „Investigate Bugs”. Stosowna łata nie została dotąd opublikowana.
Wskazówki dotyczące radzenia sobie ze znanymi błędami znajdują się również w pliku README.TXT w głównym folderze krążka instalacyjnego C++Buildera.
Unowocześnianie wersji C++Buildera
C++Builder 5 może współistnieć z poprzednimi wersjami nawet na tym samym dysku, lecz pliki współużytkowane przez wszystkie wersje (jak np. BDE) muszą zostać unowocześnione do wersji najnowszej. To samo odnosi się do współistnienia C++Buildera 5 i Delphi.
Jeżeli nie zamierzasz używać poprzednich wersji C++Buildera, odinstaluj je przed instalacją wersji 5.; stosowne wskazówki w tym względzie znajdują się w plikach INSTALL.TXT i README.TXT w głównym folderze krążka instalacyjnego.
Obsługa istniejących projektów
Gdy wczytujesz do C++Buildera 5 projekt stworzony w którejś z poprzednich wersji, projekt ten jest automatycznie uaktualniany do najnowszej wersji. Plik główny projektu (.bpr) uaktualniany jest do formatu XML, zaś zawarte w nim informacje o wymaganych dotąd bibliotekach systemowych zastępowane są informacjami o bibliotekach wymaganych w wersji 5. Czynione są również pewne drobne modyfikacje w głównym pliku .cpp projektu.
Zdarza się jednak, iż projekt zawiera fragmenty kodu, które muszą być obsługiwane przez konkretną wersję C++Buildera i nie będą pracować z nowszą jego wersją; dotyczy to szczególnie komponentów pochodzących od niezależnych dostawców. Takie „wersjonowanie” kodu wykonywane jest tradycyjnie przy użyciu klauzul #ifdef VERXXX; problem w tym, iż projekt przeznaczony dla wersji C++Buildera 4 (VER125) nie zna jeszcze symbolu VER130 (zdefiniowanego w wersji 5.), tak więc sekcja uzależniona od klauzuli #ifdef VER125 musi zostać podzielona na dwie sekcje zależne od symboli (odpowiednio) VER125 i VER130, o ile oczywiście ma być dokonane rozróżnienie pomiędzy wersjami 4. i 5. (oprócz istniejącego już odróżnienia wersji 4. od wersji wcześniejszych).
Jeżeli nie posiadasz kodu źródłowego dla jakiegoś komponentu lub pakietu, uzyskanie od producenta nowszej jego wersji (lub, być może, kodu źródłowego) jest warunkiem koniecznym uaktualnienia projektu.
Tworzenie projektów zgodnych z poprzednimi wersjami C++Buildera
Jest oczywiste, iż projekt mający bezproblemowo współpracować z poprzednimi wersjami C++Buildera, nie może odwoływać się do mechanizmów w tych wersjach nieobecnych, w szczególności tych, które pojawiły się dopiero w wersji 5.
Dotyczy to głównie formatu, w jakim przechowywane są pliki formularzy (*.dfm); w wersji 5. jest to (domyślnie) format tekstowy, w wersjach poprzednich - binarny. Aby w wersji 5. wymusić zapisywanie danego formularza w formacie binarnym, należy odznaczyć opcję Text DFM w jego menu kontekstowym (uruchamianym kliknięciem prawego przycisku myszy w jego wolnym obszarze). Aby spowodować binarny zapis wszystkich tworzonych w przyszłości formularzy, należy odznaczyć opcję New Forms as Text na stronie Preferences okna opcji środowiska (dostępnego za pośrednictwem opcji Tools|Environment Options).
Plik projektu (.bpr) w wersji 5. zapisywany jest w formacie XML; w wersjach poprzednich był to charakterystyczny format makefile. Aby zapisać bieżący projekt w tym formacie, należy wybrać opcję Export Makefile z menu Project - spowoduje to utworzenie pliku o nazwie tożsamej z nazwą projektu i rozszerzeniu .mak. Należy następnie zmienić rozszerzenie tego pliku na .bpr oraz dokonać kilku niezbędnych zmian w jego zawartości; zmiany te polegają na wpisaniu odpowiedniej wersji C++Buildera (w dyrektywie VERSION) i plików biblioteki VCL (w dyrektywach LIBRARIES, SPARELIBS, PACKAGES), a także modyfikacji opcji w dyrektywie CFLAG1.
Jakkolwiek dyrektywy „To-Do” są nowością wersji 5., nie kolidują z wersjami poprzednimi, gdzie traktowane są jak zwykłe komentarze.
Format pliku symbolicznego *.tds różni się pomiędzy wersjami C++Buildera; wczytanie projektu utworzonego w innej wersji może spowodować wyświetlenie komunikatu Error reading symbol file. Nie stanowi to żadnego problemu, gdyż w wyniku ponownej kompilacji projektu plik ten zostanie utworzony na nowo, już w poprawnym formacie.
Inne problemy ze zgodnością
W wersji 5. C++Buildera zostały zmienione definicje niektórych klas, ich metod i właściwości, co oczywiście stanowi pewien problem przy unowocześnianiu projektów. Oto najważniejsze ze wspomnianych zmian - kompletny ich wykaz znajduje się w systemie pomocy:
TPoint nie jest już strukturą (klasą bez konstruktora); inicjowanie wartości tego typu posiada obecnie formę rzutowania dwóch wartości całkowitych na typ TPoint
TPoint station = { TPoint(1,1) };
w miejsce dotychczasowego inicjowania struktury
TPoint station = { {1,1} };
które obecnie spowoduje błąd kompilacji.
Zmieniła się lista parametrów konstruktora klasy TPropertyEditor - jego deklaracja wygląda teraz tak:
TPropertyEditor (const di_IFormDesigner ADesigner, int AropCount)
Pojawiła się nowa klasa o nazwie TComponentList, służąca do przechowywania listy komponentów. Jest ona zdefiniowana w pliku cntnrs.hpp, jej szczegóły opisane są w systemie pomocy.
Metoda sprintf() klasy AnsiString nadpisuje obecnie dotychczasową zawartość łańcucha, zamiast dołączać nowy łańcuch na jej końcu; dołączanie takie realizowane jest teraz przez metodę cat_sprintf.
Komponent THTML (firmy Netmasters) został zastąpiony przez komponent TCppWebBrowser. Związane z tym wskazówki odnośnie unowocześniania projektu zawarte są w systemie pomocy.
Zmieniły się niektóre elementy MIDAS-a; szczegóły opisane są w systemie pomocy.
Migracja ze środowiska Delphi
Podrozdział ten stanowi krótkie porównanie równoważnych konstrukcji Delphi i C++Buildera. Jest on przeznaczony dla programistów Delphi, którzy rozpoczynają pracę z C++Builderem, nie powinien być jednak traktowany jako kompendium programowania w C++. Przed przystąpieniem do szczegółów należy przypomnieć niezwykle istotny fakt - w przeciwieństwie do Delphi (i Pascala w ogóle) C++Builder wrażliwy jest na wielkość liter; może to być początkowo uciążliwe dla programistów przyzwyczajonych do obojętności Delphi w tym zakresie.
Komentarze
Komentarze C++Buildera posiadają nieco inną składnię niż komentarze Delphi; różnice te prezentujemy w tabeli 1.1.
Tabela 1.1. Porównanie komentarzy Delphi i C++Buildera
Delphi |
C++Builder |
{ Komentarz w Delphi } (* Inny komentarz w Delphi *) // komentarz jednolinijkowy |
/* Komentarz w C++Builderze */
// komentarz jednolinijkowy |
Zmienne
Podobnie jak w Delphi, zmienne w C++Builderze muszą być zadeklarowane przed użyciem. Tabela 1.2 zawiera porównanie typów zmiennych dostępnych w obydwu środowiskach, wraz ze wskazaniem ich rozmiarów w bajtach.
Tabela 1.2. Typy zmiennych Delphi i C++Buildera
Typ |
Rozmiar (w bajtach) |
Delphi |
C++Builder |
Liczba całkowita ze znakiem |
1 |
ShortInt |
char |
|
2 |
SmallInt |
short, |
|
4 |
Integer, |
int, long |
|
8 |
Int64 |
__int64 |
Liczba całkowita bez znaku |
1 |
Byte |
BYTE, |
|
2 |
Word |
unsigned short |
|
4 |
Cardinal, |
unsigned long |
Liczba zmiennoprzecinkowa |
4 |
Single |
float |
|
8 |
Double |
double |
|
10 |
Extended |
long double |
Variant |
16 |
Variant, |
OleVariant, VARIANT |
Znak |
1 |
Char |
char |
|
2 |
WideChar |
WCHAR |
Łańcuch dynamiczny |
- |
AnsiString |
AnsiString |
Łańcuch dynamiczny znaków dwubajtowych |
- |
WideString |
WideString |
Łańcuch z zerowym ogranicznikiem |
- |
PChar |
char * |
Łańcuch znaków dwubajtowych z zerowym ogranicznikiem |
- |
PWideChar |
LPCWSTR |
Wskaźnik amorficzny |
4 |
Pointer |
void * |
Wartość logiczna (boolowska) |
1 |
Boolean |
bool |
Więcej informacji na temat typów zmiennych C++Buildera znajdziesz w systemie pomocy.
Stałe
W C++Builderze stałe mogą być deklarowane na dwa sposoby. Pierwszy, tradycyjny, wykorzystuje dyrektywę preprocesora #define:
#define myconstant 100
Nowszy i bezpieczniejszy sposób polega na użyciu słowa kluczowego const:
const int myconstant = 100;
Operatory
Punkt ten zawiera opis znaczenia poszczególnych operatorów C++Buildera i wskazówki odnośnie przekształcania równoważnych operatorów Delphi. Na początek porównanie operatorów występujących w obydwu środowiskach.
Tabela 1.3. Porównanie operatorów Delphi i C++Buildera
Typ operatora |
Operator |
Delphi |
C++Builder |
przypisanie |
przypisz |
:= |
= |
|
dodaj i przypisz |
|
+= |
|
odejmij i przypisz |
|
-= |
|
pomnóż i przypisz |
|
*= |
|
podziel i przypisz |
|
/= |
|
oblicz resztę z dzielenia i przypisz |
|
%= |
|
oblicz koniunkcję bitową i przypisz |
|
&= |
|
oblicz alternatywę bitową i przypisz |
|
|= |
|
oblicz bitową różnicę symetryczną i przypisz |
|
^= |
|
przesuń bity w lewo i przypisz |
|
<<= |
|
przesuń bity w prawo i przypisz |
|
>>= |
porównanie |
równy |
= |
== |
|
nierówny |
<> |
!= |
|
większy |
> |
> |
|
większy lub równy |
>= |
>= |
|
mniejszy |
< |
< |
|
mniejszy lub równy |
<= |
<= |
arytmetyczny |
dodaj |
+ |
+ |
|
odejmij |
- |
- |
|
pomnóż |
* |
* |
|
podziel zmiennoprzecinkowo |
/ |
/ |
|
podziel stałoprzecinkowo |
div |
/ |
|
oblicz resztę z dzielenia |
mod |
% |
logiczny |
koniunkcja |
and |
&& |
|
alternatywa |
or |
|| |
|
zaprzeczenie |
not |
! |
bitowy |
koniunkcja |
And |
& |
|
alternatywa |
Or |
| |
|
różnica symetryczna |
Xor |
^ |
|
negacja |
Not |
~ |
|
przesunięcie w lewo |
Shl |
<< |
|
przesunięcie w prawo |
Shr |
>> |
wskaźnikowy |
wskazanie |
^typ |
typ * |
|
adresowanie |
wskaźnik^ |
*wskaźnik |
|
adres zmiennej |
@zmienna |
&zmienna |
|
referencja |
var |
typ& |
klasowy |
deklaracja klasy |
class |
class |
|
deklaracja struktury |
record |
struct |
|
kwalifikator zakresu |
. |
:: |
|
bezpośrednie odwołanie kwalifikowane |
. |
. |
|
pośrednie odwołanie kwalifikowane |
|
-> |
inny |
inkrementacja |
Inc() |
++ |
|
dekrementacja |
Dec() |
-- |
|
ogranicznik łańcucha |
' |
" |
Operatory przypisania
W przeciwieństwie do Delphi, w C++ możliwe jest połączenie różnych operatorów z operatorem przypisania. Na przykład poniższa instrukcja
x = x + 2;
może być zapisana jako
x += 2;
Operatory inkrementacji i dekrementacji
W C++ inkrementacja i dekrementacja może być połączona z przypisaniem na jeden z dwóch sposobów: prefiksowy, kiedy to inkrementacja lub dekrementacja odbywa się przed przypisaniem i postfiksowy, kiedy odbywa się po dokonaniu przypisania:
x = ++y; // inkrementacja prefiksowa
x = y++; // inkrementacja postfiksowa
Oto konkretny przykład:
int x=5, y=5;
x = ++y;
// teraz x=y=6
x = y++;
// teraz x=6, y=7
Operator warunkowy
Operator ten nie występuje w Delphi. Jest on operatorem trójargumentowym, zwracającym wartość drugiego lub trzeciego wyrażenia, zależnie od wartości logicznej pierwszego:
(wyr1) ? (wyr2) : (wyr3)
Tak więc instrukcja:
return ((a > b) ? c : d )
równoważna jest następującej:
if (a>b) return (c);
else return (d);
Operatory wskaźnikowe
Operator * używany jest zarówno do deklarowania wskaźników, jak i do adresowania za pomocą wskaźnika. Obydwie te sytuacje są oczywiście bezbłędnie rozróżniane przez kompilator. Spójrz na poniższy przykład:
int x, y = 8;
int* ptr = &y; // deklaracja zmiennej ptr i zainicjowanie jej
// adresem zmiennej y
x = *ptr; // odwołanie się do adresu zawartego w zmiennej ptr
Oto równoważna konstrukcja w Delphi:
var
x, y : Integer;
ptr: ^Integer;
begin
y := 8;
ptr := @y;
x := ptr^;
end;
Tak więc operatory * i & C++Buildera równoważne są operatorom (odpowiednio) ^ i @ Delphi. Operator & nazywany jest operatorem adresu, zwraca bowiem adres zmiennej, będącej jego operandem.
Operator & wykorzystywany jest również do deklarowania referencji. Referencja jest szczególnym rodzajem odwołania się do zmiennej - może być traktowana jako równoważny jej obiekt i jest użyteczna przy przekazywaniu parametrów do funkcji. Odpowiednikiem referencji jest w Delphi przekazywanie parametrów (do procedur i funkcji) przez zmienną (notabene zwane także przekazywaniem przez referencję), w pewnym sensie więc słowo kluczowe var (w Delphi) może być traktowane jako odpowiednik operatora &.
Operatory new i delete
Deklaracja zmiennej może mieć zawsze postać podobną do poniższej:
char buffer[255];
Przydział pamięci dla zmiennej następuje wówczas na stosie; zmienna taka nazywana jest zmienną lokalną, gdyż istnieje tylko do zakończenia realizacji zawierającej ją funkcji. Podstawowym problemem w przypadku takich zmiennych jest ograniczona pojemność stosu.
Alternatywne podejście polega na alokowaniu zmiennych na stercie, na przykład:
char* buffer;
buffer = new char;
lub krócej:
char* buffer = new char;
Zmienna utworzona w ten sposób istnieje aż do momentu jawnego jej zwolnienia za pomocą operatora delete:
delete buffer;
Operatory klasowe
Dostęp do elementów definicji klasy może odbywać się w dwojaki sposób: bezpośrednio albo pośrednio. Wyjaśnimy to zagadnienie w dalszej części rozdziału.
Przepływ sterowania w programie
Podobnie jak Delphi, również i C++Builder oferuje różnorodne konstrukcje dla realizacji skoków warunkowych i pętli - są nimi:
instrukcje if - else;
instrukcje switch;
pętle for;
pętle while i do-while.
C++ posiada również instrukcje break i continue, które zaadaptowane zostały kiedyś na gruncie Turbo Pascala i nadal obecne są w Delphi. Przykłady skoków warunkowych w Delphi i C++Builderze przedstawia tabela 1.4.
Tabela 1.4. Porównanie instrukcji skoków i pętli Delphi i C++Buildera
Instrukcja |
Delphi |
C++Builder |
if-else |
if (i = val) then begin |
if (i == val) |
switch |
case <wyrażenie> of |
switch (<wyrażenie>) { |
for |
for i := val1 to val2 do
for i := val1 downto val2 do |
for(i=val1;i<=val2;i+=inc) {instrukcja1;...;} |
While |
while i = val do |
While (i == val) {instrukcja1;...;} |
do-while |
repeat |
do |
Oto kilka dodatkowych wskazówek, związanych z instrukcjami sterującymi C++Buildera:
Zazwyczaj każdy wariant (case) instrukcji switch kończony jest instrukcją break; przy braku takiej instrukcji sterowanie przejdzie automatycznie do następnego wariantu. Jakkolwiek działanie takie może być zamierzone przez programistę, to jednak często jest przejawem powszechnie popełnianego błędu, szczególnie przez programistów przyzwyczajonych do Delphi.
W roli wyrażenia selekcyjnego instrukcji switch może wystąpić tylko wyrażenie typu porządkowego - nie może nim być np. łańcuch.
Konstrukcja for(;;) równoważna jest konstrukcjom while(true) i while(1) i oznacza nieskończoną pętlę; wyjście z takiej pętli może nastąpić tylko w wyniku wykonania instrukcji break.
Funkcje i procedury
Podobnie jak w Delphi, funkcje C++Buildera muszą być deklarowane („prototypowane”) przed użyciem; w przeciwieństwie jednak do Delphi nie istnieje w C++ żadne specjalne słowo kluczowe na oznaczenie funkcji (w rodzaju function czy procedure). Przykłady deklaracji funkcji w Delphi i C++Builderze przedstawia tabela 1.5.
Tabela 1.5. Porównanie deklaracji funkcji w Delphi i C++Builderze
Delphi |
C++Builder |
procedure Add; |
void Add(); |
procedure Add(x, y: Integer); |
void Add(int x, y); |
function Add: Integer; |
int Add(); |
function Add(x, y: Integer): Integer; |
int Add (int x, y); |
Każdy program w C++Builderze posiada „główną” funkcję main(), od której rozpoczyna się jego wykonanie; dla programów w środowisku GUI funkcja ta nazywa się WinMain(). Składniowo funkcja główna programu nie różni się od innych funkcji - posiada parametry i zwraca wartość - nie może być jednak wywoływana w sposób bezpośredni. Przykład funkcji głównej w oknie edytora kodu przedstawia rysunek 1.12.
Rysunek 1.12. Funkcja główna przykładowego projektu
Treść funkcji ujęta jest w nawiasy { }; odpowiadają one „nawiasom pionowym” begin…end w Delphi. Do przekazania wartości zwrotnej służy instrukcja return; stanowi ona pewną analogię do słowa kluczowego Result w Delphi - z tą jednak różnicą, iż wykonanie instrukcji return kończy jednocześnie wykonywanie całej funkcji.
Klasy
Delphi i C++Builder realizują tę samą koncepcję enkapsulacji, polegającej na zamknięciu pól i operujących na tych polach funkcji w ramach jednostki zwanej klasą. Język C++ odziedziczył ponadto z języka C pojęcie struktury, definiowanej za pomocą słowa kluczowego struct; różni się ona od klasy tym, iż wszystkie jej pola i funkcje są publiczne, tj. dostępne na zewnątrz struktury bez ograniczenia, podczas gdy pola i funkcje składowe (metody) klasy są domyślnie prywatne.
Podobnie jak w Delphi, każda klasa C++Buildera charakteryzuje się następującymi cechami:
posiada konstruktor (jeden lub więcej) i destruktor;
umożliwia selektywne określenie dostępności poszczególnych pól i metod;
udostępnia wskaźnik do swej bieżącej instancji pod postacią słowa kluczowego this, stanowiącego odpowiednik wskaźnika self z Delphi.
Użyteczną możliwością klas C++Buildera, nieobecną w Delphi, jest wielokrotne dziedziczenie (ang. multiple inheritance) - klasa pochodna może być wyprowadzona z więcej niż jednej klasy bazowej; poniższy przykład wyjaśnia bliżej, o co chodzi:
// brak dziedziczenia:
class MyClass { // brak dziedziczenia
// elementy deklarowane tutaj są z założenia prywatne
private
// deklaracje prywatne
protected
// deklaracje „chronione”
public
// deklaracje publiczne
}; // deklaracja klasy kończona jest średnikiem
// pojedyncze dziedziczenie:
class MyClass: BaseClass1
{ ... };
// wielokrotne dziedziczenie:
class MyClass: BaseClass1, BaseClass2, BaseClass3
{ ... };
Konstruktory i destruktory
Każda klasa posiada konstruktor i destruktor; jeżeli programista nie zadeklaruje żadnego konstruktora (destruktora), kompilator automatycznie wygeneruje konstruktor (destruktor) domyślny.
Podstawowym zadaniem konstruktora jest przydzielenie pamięci dla instancji klasy i zainicjowanie jej pól; zadaniem destruktora jest natomiast zwolnienie pamięci zajętej przez tę instancję.
Klasa może zawierać kilka konstruktorów, lecz tylko jeden destruktor; konstruktor i destruktor podobne są do „zwykłych” funkcji składowych klasy, posiadają jednak kilka cech szczególnych:
nie zwracają żadnego wyniku (nawet typu void);
nazwa konstruktora tożsama jest z nazwą klasy, zaś nazwa destruktora powstaje przez poprzedzenie nazwy klasy znakiem tyldy (~) - na przykład:
class A {
public:
A(); // konstruktor
~A(); //destruktor
}
destruktor nie może posiadać parametrów;
konstruktory i destruktory nie mogą być wywoływane jak „zwykłe” funkcje.
Dostęp do pól i funkcji składowych klasy
Spójrz na poniższą definicję prostej klasy:
class Rabbit {
private
int speed=20;
};
Poniższe deklaracje tworzą dwie instancje tej klasy. Pierwsza z nich (Rabbit1) lokowana jest na stosie, druga (Rabbit2) - na stercie:
Rabbit Rabbit1;
Rabbit* Rabbit2 = new Rabbit;
Odwołania do pola speed obydwu instancji będą miały postać następującą:
Rabbit.speed=30;
(*Rabbit2).speed=30;
Zmienna Rabbit2 jest tak naprawdę wskaźnikiem do instancji, dlatego w jej przypadku odwołanie to ma postać bardziej skomplikowaną. Aby ułatwić odwołania do elementów klasy w przypadku reprezentowania jej instancji przez wskaźniki, wprowadzono w C++ operator odwołania pośredniego (->); drugą z powyższych instrukcji można więc napisać w równoważnej postaci:
Rabbit2->speed=30;
Wskaźnik this
Podobnie jak wskaźnik Self w Delphi, this reprezentuje (w treści funkcji składowej) instancję, na rzecz której funkcja ta jest aktywowana. Oto przykłady jego użycia:
__fastcall TForm1::TForm1(Tcomponent* Owner): TForm(Owner)
{
TLabel *Label1 = new TLabel(this);
Label1->Parent = this;
Label1->Caption = this->Width; // to samo, co Form1->Width
}
Dyrektywy preprocesora
Dyrektywy preprocesora stanowią specjalne polecenia dla kompilatora. Wiersz zawierający dyrektywę rozpoczyna się od symbolu #. Niektóre dyrektywy mogą znajdować się w dowolnym miejscu modułu, inne natomiast - jak np. #pragma argsused - muszą znajdować się przed definicjami funkcji, których dotyczą. Przyjrzyjmy się bliżej dwu najczęściej spotykanym dyrektywom: #define i #include.
Dyrektywa #define
Jak pokazano to nieco wcześniej w tym rozdziale, dyrektywa #define może być wykorzystywana do definiowania stałych, na przykład:
#define myconstant 100
Za pomocą dyrektywy #define można również definiować makroinstrukcje. Makroinstrukcja podobna jest do funkcji i może posiadać parametry, na przykład:
#define MAX(A,B) ((A)>(B))?(A): (B)
#define MIN(A,B) ((A)>(B))?(B): (A)
Oto sposób użycia jednej z ww. makroinstrukcji:
int x, y, max;
x = 5;
y = 6;
max = MAX(x,y); // C++Builder rozróżnia wielkość liter
Dyrektywa #include
Użycie dyrektywy #include wiąże się nierozerwalnie z plikami nagłówkowymi. W większości przypadków wszystkie deklaracje stałych, zmiennych i funkcji umieszczane są w plikach nagłówkowych, zaś w plikach źródłowych *.cpp znajduje się jedynie część implementacyjna modułu.
Na czas kompilacji dyrektywa #include zostaje zastąpiona zawartością pliku, którego nazwa specyfikowana jest jako argument. Specyfikacja ta może mieć jedną z dwóch postaci, skutkujących odmiennym algorytmem poszukiwania pliku nagłówkowego (w strukturze katalogów). W przypadku dyrektywy #include <nazwa> poszukiwanie prowadzone jest jedynie w obrębie katalogów wymienionych w polu Include path na karcie Directories/Conditionals opcji projektu; nieznalezienie pliku traktowane jest jako błąd. Dyrektywa #include "nazwa" skutkuje natomiast poszukiwaniem pliku nagłówkowego kolejno:
w katalogu, w którym znajduje się plik zawierający odnośną dyrektywę #include;
w katalogach zawierających inne pliki, zawierające dyrektywy #include, odnoszące się do tego samego pliku nagłówkowego;
w katalogu bieżącym;
w ścieżce określonej przez dyrektywę /I kompilatora.
Typy plików
Tabela 1.6 przedstawia wykaz różnych typów plików związanych z realizacją projektów Delphi i C++Buildera.
Tabela 1.6. Typy plików Delphi i C++Buildera
Delphi |
C++Builder |
Przeznaczenie pliku |
DPR |
BPR |
Główny plik projektu |
PAS |
CPP |
Plik kodu źródłowego modułu (w C++Builderze również główny plik źródłowy projektu) |
DFM |
DFM |
Plik binarny opisujący kompletną strukturę formularza wraz z komponentami |
PAS |
H lub HPP |
Plik nagłówkowy |
RES |
RES |
Skompilowany plik zasobów |
DCU |
OBJ |
Skompilowany plik modułu |
BPG |
BPG |
Plik opisujący grupę projektów |
DPK |
BPK |
Plik zawierający listę modułów pakietu |
- |
BPI |
Plik importowy biblioteki tworzony dla każdego pakietu |
BPL |
BPL |
Biblioteka czasu wykonania (runtime); fizycznie jest to biblioteka DLL wzbogacona o elementy specyficznie dla Delphi (C++Buildera) |
- |
LIB |
Statyczna biblioteka |
~* |
~* |
Kopie zapasowe rozmaitych plików |
- |
MAK |
Plik tekstowy zawierający scenariusz budowania binarnej postaci projektu na podstawie jego plików źródłowych. |
MAP |
MAP |
Plik tekstowy zawierający informacje symboliczne dla śledzenia niskopoziomowego |
- |
TDS |
Plik zawierający informacje symboliczne dla Turbo Debuggera. |
Z wymienionymi kategoriami plików związane są ponadto następujące reguły:
Pliki wymienione na pierwszych pięciu pozycjach tworzone są przy pierwszym zapisywaniu projektu.
Pliki *.CPP i *.HPP C++Buildera stanowią odpowiedniki sekcji INTERFACE i IMPLEMENTATION plików *.PAS w Delphi.
Pliki *.OBJ tworzone są w wyniku kompilacji modułu lub pakietu.
Plik *.BPK tworzony jest przy zapisywaniu pliku źródłowego pakietu.
Pliki *.BPI, *.BPL i *.LIB tworzone są w wyniku kompilacji lub instalacji pakietu.
W wersjach 4. i 5. C++Buildera plik *.MAK zastąpiony został przez plik *.BPR; otwarcie pliku *.MAK w tych wersjach spowoduje automatyczną konwersję wczytanej zawartości do postaci właściwej plikowi *.BPR.
Plik *.TDS tworzony jest wówczas, gdy włączona jest opcja Debug Information na karcie Compiler opcji projektu.
Mocne i słabe strony C++Buildera
W podrozdziale tym omówimy nieco dokładniej podstawowe cechy C++Buildera, mające bezpośredni związek z tworzeniem i rozwijaniem projektów programistycznych. Dla większości Czytelników tej książki C++Builder będzie zapewne preferowanym narzędziem projektowym, wydaje się zatem celowe opisanie tych jego aspektów, które decydują o jego użyteczności jako narzędzia RAD - z jednoczesnym uwzględnieniem tych, które zdają się sprawiać niejakie kłopoty.
Wizualna rzeczywistość - naprawdę błyskawiczne tworzenie aplikacji
Dla programistów bazujących na języku C++, potrzebujących narzędzi do projektowania aplikacji posiadających bogaty i funkcjonalny interfejs użytkownika, C++Builder ze swym rozbudowanym IDE, biblioteką VCL i repozytorium zdaje się nie mieć alternatywy. Nawet średnio zaawansowany programista zdolny jest przy użyciu C++Buildera stworzyć nietrywialną aplikację w rodzaju wieloplikowego edytora tekstów w niespełna godzinę - poza oczywistymi korzyściami praktycznymi daje to przecież niebagatelną satysfakcję, ponadto możliwość kontrolowania różnych aspektów zachowania się aplikacji już na etapie jej projektowania znacznie zmniejsza liczbę niezbędnych prób i błędów.
Wersja 5. C++Buildera niesie ze sobą dodatkowe nowości, czyniące proces projektowania jeszcze łatwiejszym. Przykładowo komponent TFrame („ramka”) umożliwia pracę z niezależnymi oknami podobnymi do formularzy, które to okna mogą być później umieszczane na rzeczywistych formularzach lub nawet w palecie komponentów. Wizualizacja właściwości „graficznych” w inspektorze obiektów czyni ich konfigurowanie jeszcze łatwiejszym, zaś podział właściwości i zdarzeń na kategorie pozwala szybciej znajdować żądane pozycje, przez ukrycie kategorii rzadko wykorzystywanych.
Rysunek 1.13 przedstawia środowisko IDE z projektem wykorzystującym trzy „ramki” TFrame (dwie znajdują się już na formularzu) w czasie dokonywania wyboru odpowiedniej postaci kursora w inspektorze obiektów.
Rysunek 1.13. Przykładowy projekt w IDE C++Buildera 5
Microsoft Visual C++, mimo przymiotnika „visual” w swej nazwie, nie oferuje podobnej metody projektowania typu WYSIWYG - i właśnie na tym polu w największym stopniu ustępuje on C++Builderowi. Użytkownicy Visual C++ mają do wyboru dwie realne alternatywy w stosunku do „klasycznego” programowania w Windows. Podstawą pierwszej z nich jest edytor zasobów i biblioteka MFC (Microsoft Foundation Classes), druga natomiast polega na tworzeniu bibliotek DLL przeznaczonych do użytku przez aplikacje stworzone w środowiskach lepiej przystosowanych do programowania wizualnego, jak na przykład Visual Basic.
Pod względem pierwszej ze wspomnianych opcji Visual C++ wyraźnie nie dorównuje C++Builderowi - mimo iż funkcjonowanie edytora zasobów przypomina nieco funkcjonowanie projektanta formularzy (Form Designer), nie jest on jednak w stanie konkurować z tym ostatnim pod względem swobody rozmieszczania kontrolek na formularzu i operowania ich właściwościami. Ponadto edytor zasobów zdolny jest tworzyć wyłącznie standardowe kontrolki, podczas gdy projektant formularzy potrafi wykorzystać całą rozbudowaną bibliotekę VCL, być może o zawartości liczonej w tysiącach komponentów.
Jakby tego było mało, C++Builder obsługuje MFC - być może nie tyle ze względu na te rzadkie przypadki, kiedy użytkownik chciałby go użyć, ile raczej ze względów zgodności (w kontekście istniejącego kodu). Visual C++ nie posiada natomiast żadnych środków ułatwiających migrację z platformy C++Buildera.
Druga ze wspomnianych opcji - tworzenie bibliotek DLL do wykorzystania w innym środowisku - jest o tyle interesująca, iż realizuje w praktyce oddzielenie całej „maszynerii” aplikacji od jej interfejsu użytkownika. C++Builder stanowi jednak także połączenie dwóch zestawów funkcji - efektywnego kompilatora C++ oraz użytecznego projektanta GUI; w dobrze zaprojektowanej aplikacji oddzielenie jej „maszynerii” od interfejsu użytkownika da się zrealizować równie efektywnie.
Dotrzymywanie kroku - zgodność ze standardami
Przez wiele lat obecności na rynku narzędzi programistycznych firma Borland nieodmiennie dotrzymywała kroku standardom ANSI/ISO pod względem zgodności swych kompilatorów z owymi standardami. C++Builder, którego podstawą jest wersja 5.5 modelu 32-bitowego kompilatora C++ (BCC32), nie jest w tym względzie wyjątkiem: szczegóły obecnej wersji języka sformalizowane zostały w ostatniej specyfikacji ANSI/ISO - różni się ona od poprzednich wersji w kilku istotnych kwestiach, między innymi w temacie wzorców (templates) i przestrzeni nazw (namespaces).
Nie ma to być może większego znaczenia dla małych, monolitycznych aplikacji, konstruowanych wyłącznie za pomocą projektanta formularzy, jednakże dla dużych, poważnych aplikacji, tworzonych z myślą o przyszłej skalowalności, tak rygorystyczna zgodność kompilatora ze standardami, jak w przypadku wspomnianego BCC32, jest czynnikiem koniecznym długoterminowego powodzenia projektu.
Niestety, wymóg zgodności ze standardami nie zawsze daje się w pełni pogodzić z inną - skądinąd ze wszech miar pożądaną - tendencją rozwoju narzędzi programistycznych, mianowicie ciągłym usprawnianiem procesu kompilacji. Tłumaczy to poniekąd fakt, iż niektóre aplikacje stworzone w poprzednich wersjach C++Buildera nie dały się skompilować w wersji 5., głównie ze względu na zmiany specyfikacji pod kątem wspomnianych przed chwilą wzorców i przestrzeni nazw. Co prawda przystosowywanie aplikacji do nowych standardów z pewnością jest pouczającym doświadczeniem, jednakże konieczność jego dokonywania kłóci się po prostu z ekonomiczną stroną działalności programistycznej, skąd wniosek - nie można bezkrytycznie polegać na najlepszych nawet kompilatorach pod względem absolutnej zgodności we wszystkich szczegółach.
Wyraźnym, tym razem zamierzonym, odstępstwem od zgodności ze standardami są natomiast elementy właściwe tylko platformie C++Buildera, wprowadzone do języka w związku z biblioteką VCL. Jednym z przykładów może być słowo kluczowe __closure, umożliwiające konstruowanie wskaźników do funkcji składowych klas na podstawie instancji klas, nie zaś klas jako takich; efektu tego nie da się w prosty i klarowny sposób osiągnąć w zgodzie z obowiązującą specyfikacją ANSI/ISO.
Osobiste preferencje
Niezależnie od rekomendacji producentów i najpochlebniejszych nawet opinii użytkowników, nie ma produktów idealnych. Trudno zadowolić wszystkich, tak więc to, co dla jednych wydaje się zaletą, przez innych może być bezlitośnie krytykowane. Ponadto niezależnie od osobistych upodobań w każdym oprogramowaniu mogą ujawnić się bezdyskusyjne błędy - wszak zostało ono stworzone przez omylnych w końcu ludzi, przy ograniczonych nakładach finansowych i w ograniczonym czasie. Takie są realia życia zawodowego programistów.
C++Builder stworzony został jako narzędzie projektowe, nie zaś panaceum na wszelkie kłopoty programistyczne; nie ma więc nic więc nadzwyczajnego w tym, iż w pewnych obszarach zastosowań może on okazać się idealny, podczas gdy jego przydatność w innych dziedzinach będzie raczej ograniczona. Ma to również związek z osobistymi upodobaniami - dla programistów przyzwyczajonych do filozofii Microsoftu wystarczającym argumentem może okazać się proste stwierdzenie, iż Borland to nie Microsoft…
Zalety i wady C++Buildera - konkluzje
Każda dyskusja na temat zalet i wad czegokolwiek pozostaje dyskusją jałową, jeśli nie wesprzeć jej konkretami; w przypadku C++Buildera konkretami takimi są z pewnością jego cechy projektowe i wydaje się naturalne, iż to właśnie do nich - a nie do narzędzia enigmatycznie postrzeganego jako całość - odnosić można wszelkie opinie wartościujące. Poniższa lista z pewnością daleka jest od kompletności, zawiera jednak najistotniejsze cechy C++Buildera.
Tak więc do tych elementów C++Buildera 5, które zdobyły sobie największe uznanie programistów, należą między innymi:
niezwykle funkcjonalny projektant formularzy;
przydatność zarówno do szybkiego tworzenia aplikacji, jak i tradycyjnego programowania w C++, dzięki umiejętnemu połączeniu zgodnego z najnowszymi standardami kompilatora ze środowiskiem projektowania wizualnego;
biblioteka komponentów VCL z możliwością wzbogacania jej o nowe, gotowe komponenty i narzędziami do tworzenia własnych komponentów;
rozszerzenia języka wykraczające co prawda poza standardy, lecz dostarczające funkcjonalnych konstrukcji programistycznych;
wzorce projektów i repozytorium obiektowe umożliwiające wielokrotne wykorzystanie stworzonego kodu oraz pozwalające na szybkie tworzenie projektów;
szeroka obsługa mechanizmów bazodanowych dzięki BDE oraz dedykowanym komponentom;
ogromne możliwości w zakresie tworzenia aplikacji internetowych;
łatwa migracja z innych środowisk, między innymi z Visual C++;
otwarta droga do przenoszenia aplikacji na platformę Linuksa.
Wśród najczęściej wymienianych niedostatków C++Buildera wymienia się:
łatwość operowania mechanizmami projektanta formularzy może łatwo doprowadzić do chaosu w aplikacji źle zaprojektowanej lub wręcz pozbawionej projektu;
ze względu na znaczące zmiany kompilatora w wersji 5. przenoszenie złożonej aplikacji z wersji poprzednich może okazać się nieopłacalne;
migracja na platformę Visual C++ jest zazwyczaj bardzo trudna;
ze względu na dominującą pozycję Microsoftu na rynku systemów operacyjnych wybór narzędzia programistycznego Borlanda może mieć długofalowe konsekwencje o różnorakim charakterze.
Przygotowania do Kyliksa
Kylix jest roboczą nazwą projektu Borlanda, mającego na celu stworzenie środowiska dla systemu operacyjnego Linux; w przełożeniu na realia techniczne chodzi tu po prostu o zaadaptowanie Delphi i C++Buildera na platformę Linuksa. W wyniku tego tworzenie wizualnych aplikacji w środowisku Linuksa ma się stać równie łatwe, jak tworzenie ich w Windows przy użyciu Delphi i C++Buildera.
Istnieje wiele podobieństw pomiędzy Kyliksem a C++Builderem (i Delphi), są też i znaczące różnice, będące głównie konsekwencją różnic pomiędzy samymi systemami Windows i Linux - należy o tym pamiętać, planując w przyszłości przenoszenie tworzonych obecnie aplikacji w Windows na platformę Linuksa.
Informacje zawarte w tym podrozdziale pochodzą zarówno z publicznych anonsów Borlanda, jak i z dostępnych dyskusji na temat Kyliksa, mają więc charakter po trosze spekulatywny; w efekcie może się okazać, iż w ukończonym produkcie brak będzie niektórych zapowiadanych szczegółów.
Podobieństwa pomiędzy Kyliksem a C++Builderem
Podstawowa struktura Kyliksa jest pod wieloma względami bardzo podobna do C++Buildera. Mimo generalnej różnicy pomiędzy interfejsem Windows a rozmaitymi dostępnymi interfejsami graficznymi dla Linuksa, IDE obydwu produktów są niemal identyczne - zawierają inspektora obiektów, przeglądarkę klas, paletę komponentów i edytor kodu. Rysunek 1.14 prezentuje wygląd projektu Delphi załadowanego do wczesnej (pre-release) wersji Kyliksa; w charakterze interfejsu graficznego dla Linuksa wykorzystano tu K Desktop Environment (KDE).
Rysunek 1.14. Środowisko IDE Kyliksa pod Linuksem używającym KDE
Jako że Delphi i C++Builder współdzielą znaczną część dostępnych funkcji, można się spodziewać, iż wprzęgnięcie mechanizmów związanych z Kyliksem będzie miało w obydwu tych produktach charakter niemal identyczny. W efekcie nowości w ramach jednego z produktów z pewnością będą miały swe odpowiedniki w najbliższej wersji drugiego.
Różnice pomiędzy Kyliksem a C++Builderem
Zasadnicza różnica pomiędzy Linuksem a Windows nie może oczywiście pozostać bez wpływu na Kyliksa, przynajmniej w początkowym okresie jego istnienia. Dotyczy to w pierwszym rzędzie charakterystycznych dla Windows technologii: COM, DCOM, COM+, ActiveX i oczywiście samego Win32 API. Kilka lat temu Microsoft we współpracy z Digitalem i innymi znaczącymi firmami przystąpił jednak do opracowywania DCOM w wersji dla serwerów uniksowych; jej ukazanie się wydaje się teraz tylko kwestią czasu.
Można mieć ponadto nadzieję, iż wywołania funkcji Win32 będą miały swe odpowiedniki w Linuksie - w postaci odrębnej, przezroczystej warstwy, albo w postaci równoważnych funkcji systemowych; ten drugi wariant jest znacznie mniej korzystny z punktu widzenia przenoszenia gotowych aplikacji.
Biblioteka komponentów nosić będzie nazwę Borland Component Library for Cross Platform (CLX - w skrócie „clicks”). Poszczególne komponenty CLX pogrupowane zostaną zapewne w kategorie (analogicznie do C++Buildera i Delphi), zaś ich warstwa wizualna zrealizowana zostanie w oparciu o technologię Qt firmy TrollTech, którą to technologię uznał Borland za najodpowiedniejszą dla środowiska linuksowego. Qt instalowane jest wraz z KDE, w przypadku innych interfejsów graficznych - jak np. Gnome - musi być instalowane oddzielnie. Prawdopodobnie komponenty kategorii CLX pojawią się również w wersji C++Buildera dla Windows - budowanie aplikacji z ich użyciem ułatwi późniejsze jej przenoszenie na platformę Linuksa; z punktu widzenia sprawności zaleca się jednak używanie „tradycyjnych” komponentów VCL dla aplikacji, w stosunku do których nie planuje się migracji z platformy Windows.
Podsumowanie
W rozdziale tym przedstawiliśmy podstawy C++Buildera na użytek programistów początkujących lub tych, którzy znają już Delphi i chcą rozpocząć pracę z C++Builderem. Staraliśmy się pokazać jego wyjątkową funkcjonalność jako narzędzia projektowego, jak również jego ulepszenia i rozszerzenia, które pojawiły się w wersji 5. Zajęliśmy się problemem przenoszenia aplikacji zarówno z poprzednich wersji C++Buildera, jak również z (do) innych środowisk projektowych bazujących na C++, głównie Visual C++. Przedstawiliśmy również zarys realizowanej właśnie przez Borland adaptacji Delphi i C++Buildera w środowisku Linuksa, znanej pod roboczą nazwą Kylix.
Większość z poruszonych tu tematów omówimy szczegółowo w następnych rozdziałach książki.
Oczywiście w ramach odpowiadających sobie wersji C++ i Delphi; porównanie drogi rozwojowej Delphi i C++Buildera (określane niekiedy mianem „żabiego skoku”) znajduje się w rozdziale 21. książki Delphi 4 dla każdego (Helion, Gliwice 1999) - przyp. tłum.
Pewną analogią referencji w C++ może być w Delphi także „nakładanie” na siebie zmiennych za pomocą dyrektywy absolute - przyp. tłum.
ang. WYSIWYG - What You See Is What You Get - „co widzisz, to otrzymasz”; potoczne określenie cechy programu graficznego (tu: C++Buildera), którego ostateczne wyniki (tu: wygląd działającej aplikacji) są identyczne lub niemal identyczne z ich symboliczną reprezentacją w postaci roboczej (tu: wyglądem i ułożeniem komponentów aplikacji w czasie jej projektowania) - przyp. tłum.
2 Część I ♦ Podstawy obsługi systemu WhizBang (Nagłówek strony)
2 H:\Książki\!Marcin\C++ Builder 5. Vademecum profesjonalisty\2 po korekcie jezykowej\r01-02.doc
Image
OpenDialog
Button
StatusBar
Nowy podkatalog
Nazwa modułu
Pasek narzędziowy
Menu główne
Paleta komponentów
Inspektor obiektów
Formularz
Edytor kodu