Table of Contents
Rozwijanie programu w języku C++ w środowisku Eclipse i CDT
Część pierwsza: tworzymy program od podstaw
Nowy projekt
Utwórz nowy projekt i nazwij go Shapes. Pliki projektu zostaną umieszczone w standardowym folderze roboczym (<workspace>/Shapes).
Dodaj klasę Shape
File→New→C++ class
Dodaj trzy metody (tzn. wpisz w edytorze)…
class Shape { public: Shape(); virtual ~Shape(); virtual void write(std::ostream&os)=0; virtual double area(){return 0;}; virtual void move(int dx,int dy){} };
Aby usunąć ikonę błędu przy virtual void write
zacznij pisać #include <i
i naciśnij CTRL+SPACE.
Klasa Line
Dodaj klasę Line
dziedziczącą po Shape
: File→New→C++ class . Wybierz klasę bazową…
Na rysunku jest Circle
- będzie następna…
Następnie dodaj atrybuty i metody w pliku nagłówkowym, jak w przykładzie….
class Line: public Shape { public: Line(); virtual ~Line(); int x1,y1,x2,y2; void write(std::ostream&os); void move(int dx,int dy); };
- Wybierz Source→Implement methods i zaznacz, że chcesz dodać metody
write
imove
i wpisz kod do wygenerowanych szkieletów funkcji
#include "Line.h" Line::Line() { // TODO Auto-generated constructor stub x1=y1=x2=y2=0; } Line::~Line() { // TODO Auto-generated destructor stub } void Line::write(std::ostream& os) { os<<"<line> <x1>"<<x1<<"</x1> <y1>"<<y1<<"</y1> <x2>"<<x2<<"</x2> <y2>"<<y2<<"</y2> </line>"<<std::endl; } void Line::move(int dx, int dy) { x1+=dx; y1+=dy; x2+=dx; y2+=dy; }
W tym momencie warto przekompilować: Project→Build Project. Jeśli są jakieś błędy - zastanów się dlaczego i usuń je…
Klasy Circle i Rectangle
W analogiczny sposób dodaj klasy Circle
i Rectangle
z atrybutami i metodami, jak poniżej…
#include "Shape.h" #include <math.h> class Circle: public Shape { public: Circle(); virtual ~Circle(); int x; //center x int y; //center y int r; //radius void write(std::ostream&os); virtual double area(){return M_PI*r*r;}; virtual void move(int dx,int dy){x+=dx;y+=dy;} };
#include "Shape.h" class Rectangle: public Shape { public: Rectangle(); virtual ~Rectangle(); int x1,y1,x2,y2; void write(std::ostream&os); double area(); void move(int dx,int dy); };
Fabryka kształtów
Zdefiniujemy klasę, która będzie generowała kształty na żądanie (i na dodatek zapamiętywała do nich wskaźniki, aby nie bawić się w zwalnianie pamięci).
W programie będzie potrzebna tylko jedna instancja klasy, zastosujemy więc wzorzec projektowy Singleton.
Dodaj więc nową klasę ShapeFactory
i uzupełnij jej nagłówek w podany poniżej sposób.
#include "Shape.h" #include <list> class ShapeFactory { ShapeFactory(); virtual ~ShapeFactory(); static ShapeFactory _theFactory; std::list<Shape*>store; public: static ShapeFactory&get(){return _theFactory;} Shape*createLine(int x1,int y1,int x2,int y2); Shape*createCircle(int x,int y,int r); Shape*createRectangle(int x1,int y1,int x2,int y2); Shape*createRandomShape(); // tworzy losowo wybrany wektor z losowymi atrybutami };
Następnie, jak poprzednio wybierz opcję Source→Implement methods i uzupełnij kod zgodnie ze wzorcem. W kodzie brakuje #include
i implementacji niektórych metod. Pisząc je, możliwie często posługuj się podpowiedziami (zacznij pisać i użyj CTRL+SPACE).
#include "ShapeFactory.h" #include <stdlib.h> #include <time.h> ShapeFactory ShapeFactory::_theFactory; ShapeFactory::ShapeFactory() { // TODO Auto-generated constructor stub srand (time(NULL)); } ShapeFactory::~ShapeFactory() { // TODO Auto-generated destructor stub for(std::list<Shape*>::iterator i=store.begin();i!=store.end();++i){ if(*i!=0)delete *i; } } Shape* ShapeFactory::createLine(int x1, int y1, int x2, int y2) { Line*s= new Line(); s->x1=x1; s->x2=x2; s->y1=y1; s->y2=y2; store.push_back(s); return s; } Shape* ShapeFactory::createCircle(int x, int y, int r) { //... } Shape* ShapeFactory::createRectangle(int x1, int y1, int x2, int y2) { //... } Shape* ShapeFactory::createRandomShape() { int choice = rand()%3; switch(choice){ case 0: return createLine(rand()%300,rand()%300,rand()%800,rand()%900); case 1: return createCircle(rand()%900,rand()%900,rand()%300); case 2: return createRectangle(rand()%300,rand()%300,rand()%800,rand()%900); } return 0; }
Testujemy
W pliku Shapes.cpp wprowadź następującą fukcję main()
i uruchom program…
int main() { for(int i=0;i<100;i++){ Shape*s=ShapeFactory::get().createRandomShape(); s->write(cout); } return 0; }
Dla wytrwałych i purystów
Jeśli lubisz opakowywać atrybuty metodami dostępu typu set()
i get()
, wybierz Source→Generate Getters and Setters. Nie jest to jednak refaktoryzacja. Funkcja nie potrafi zmienić atrybutów na prywatne i odpowiednio zmodyfikować wywołań.
Część druga: korzystamy z gotowej biblioteki
Pobierz kod źródłowy biblioteki zlib ze strony http://www.zlib.net/. Jest też w wersji przyciętej do kluczowych plików dostępna tu: zlib.zip. Rozpakuj zawartość w podręcznym katalogu.
Utwórz projekt zlib
Zaimportuj pliki
Wybierz opcję File→Import→General[File system] i zaznacz pliki *.c oraz *.h z głównego katalogu, w którym rozpakowane zostało archiwum z zlib. Zostaną one skopiowane do folderu projektu.
Skompiluj
Wybierz Project→Build Project. Wówczas w podkatalogu Debug powinien zostać utworzony plik biblioteki o nazwie libzlib.a.
- Nazwę można ustalić w Project→Properties→C/C++ Build→Settings→Build Artifact
- Aby utworzyć wersję Release należy zmienić aktywną konfigurację.
Część trzecia: łączymy Shapes z zlib
Konfiguracja
Musimy:
- Powiadomić środowisko, że projekt Shapes jest zależny od artefaktu projektu zlib
- Ustawić katalog include tak, aby można było korzystać z funkcji zdefiniowanych w bibliotece zlib
- Poinformować linker, gdzie znajduje się plik biblioteczny.
Wszystkie opcje są dostępne po wybraniu Project→Properties
Zależności między projektami
Includes
Wskazanie pliku biblioteki
Zmieniamy funkcję main
Teraz w funkcji main()
będziemy losowo generować kształty i zapisywać je do skompresowanego pliku w formacie gz.
#include .... #include <sstream> #include "zlib.h" int main(){ gzFile out = gzopen("shapes.xml.gz","wb3"); const char header[]="<?xml version=\"1.0\"?>\n<shapes>\n"; gzwrite(out,header,strlen(header)); for(int i=0;i<100000;i++){ ostringstream os; Shape*s=ShapeFactory::get().createRandomShape(); s->write(os); gzwrite(out,os.str().c_str(),os.str().length()); //cout<<os.str(); } const char trailer[]="</shapes>"; gzwrite(out,trailer,strlen(trailer)); gzclose(out); return 0; }
Po wykonaniu programu powinno powstać archiwum takie, jak to (1.1MB). Jeśli na laboratoryjnym komputerze program działa nieco za długo, proponuję zmniejszyć liczbę wygenerowanych obiektów…