Wykład I - semestr III Kierunek Informatyka Wydział Matematyki Stosowanej Politechniki Śląskiej Gliwice, 2014 c Copyright 2014 Janusz Słupik
Zaliczenie przedmiotu Do zaliczenia przedmiotu niezbędne jest osiągnięcie łącznie 41 punktów, w tym co najmniej 30% punktów z każdej grupy zadań sprawdzających założone dla tego przedmiotu efekty kształcenia oraz zdanie egzaminu. Lista efektów kształcenia w karcie przedmiotu dostępnej na stronie: http://157.158.16.139/js/wyklady/is3/is3.html Punkty: 1 Laboratorium Ocena ogólna z zajęć - 10 punktów Kolokwium praktyczne - 20 punktów Projekt zespołowy - 40 punktów 2 Egzamin - 30 punktów (12 punktów - min.)
Literatura Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides, Wzorce projektowe. Elementy oprogramowania obiektowego wielokrotnego użytku, Helion, Gliwice 2010 Andrei Alexandrescu, Nowoczesne projektowanie w C++. Uogólnione implementacje wzorców projektowych, Helion, Gliwice 2011 David Vandevoorde, Nicolai M. Josuttis C++ Szablony, Helion, Gliwice 2003 Bjarne Stroustrup, Język C++, WNT, Warszawa 2008
Literatura uzupełniająca Nicholas A. Solter, Scott J. Kleper, C++ Zaawansowane programowanie, Helion, Gliwice 2006 Bjarne Stroustrup, Programowanie. Teoria i praktyka z wykorzystaniem C++, Helion, Gliwice 2010 P. J. Plauger, Biblioteka standardowa C++, WNT, Warszawa 1997 J. Viega, M. Messier, C i C++ Bezpieczne programowanie. Receptury, Helion, Gliwice 2005
Tematyka semestru 1 SQLite. 2 Specjalizacje szablonów. 3 Wzorce projektowe. 4 Elementy programowania wielowątkowego. 5 Metody zabezpieczania danych. 6 Metodologia pracy. Systemy zarządzania wersjami. 7 Elementy programowania grafiki 3D. OpenGL. 8 Algorytmy graficzne. 9 Techniki programowania gier.
SQLite
Instalacja Projekt SQLite dostępny jest pod adresem http://www.sqlite.org Ściągamy: sqlite-amalgamation-3071700 Kompilujemy do statycznej biblioteki wpisując w Visual Studio Command Prompt: cl /c /EHsc sqlite3.c lib sqlite3.obj Powstaje plik sqlite3.lib, który kopiujemy do..\vc\lib a plik nagłówkowy sqlite3.h kopiujemy do..\vc\include W programie dopisujemy niezbędną dyrektywę include i podłączamy plik biblioteki statycznej pod projekt albo dopisujemy dyrektywę #pragma comment( lib, "sqlite3.lib" )
Struktura danych Nazwa bazy - Tabela1 - Kolumna1 Kolumna2 Kolumna3... Dane 1 Dane 2 Dane 3... (rekord1) Dane 1 Dane 2 Dane 3... (rekord2) Dane 1 Dane 2 Dane 3... (rekord3)
Pierwszy program SQLite #include <iostream> #include <cstdlib> #include <sqlite3.h> #pragma comment( lib, "sqlite3.lib" ) using namespace std; int main() { //... } return 0;
Pierwszy program SQLite sqlite3 *db; char *zerrmsg = 0; int rc; rc = sqlite3_open("test.db", &db); if( rc ) { cout << "Can t open database: " << sqlite3_errmsg(db) << endl; exit(0); } else cout << "Opened database successfully\n"; // Tutaj pracujemy z bazą sqlite3_close(db);
Zapytanie SQL tworzące tabelę sql = "CREATE TABLE STUDENCI(" \ "ID INT PRIMARY KEY NOT NULL," \ "NAME TEXT NOT NULL," \ "SURNAME TEXT NOT NULL," \ "AGE INT NOT NULL," \ "ADDRESS CHAR(50)," \ "AVERAGE REAL );";
Funkcja zwrotna int odpowiedz( void *NotUsed, int argc, char **argv, char **azcolname ) { int i; for(i=0; i<argc; i++) { cout << azcolname[i] << " = "; cout << (argv[i]? argv[i] : "NULL") << endl; } cout << endl; return 0; }
Wykonanie zapytania rc = sqlite3_exec(db, sql, odpowiedz, 0, &zerrmsg); if( rc!= SQLITE_OK ) { cout << "SQL error: " << zerrmsg << endl; sqlite3_free(zerrmsg); } else cout << "Operation done successfully\n" << endl; Argument czwarty funkcji sqlite3 exec może to być dowolnym wskaźnikiem, który zostanie przekazany do funkcji zwrotnej w jej pierwszym argumencie.
Odrobina SQL sql = "INSERT INTO STUDENCI (ID,NAME,SURNAME,AGE,ADDRESS,AVERAGE)"\ "VALUES (1, Adam, Nowak, 20, Gliwice, 4.5 );"\ "INSERT INTO STUDENCI (ID,NAME,SURNAME,AGE,ADDRESS,AVERAGE)"\ "VALUES (2, Beata, Kowalska, 19, Zabrze, 5.0 ); " \ "INSERT INTO STUDENCI (ID,NAME,SURNAME,AGE,ADDRESS,AVERAGE)"\ "VALUES (3, Jan, Chrobry, 21, Rybnik, 3.5 );"\ "INSERT INTO STUDENCI (ID,NAME,SURNAME,AGE,ADDRESS,AVERAGE)"\ "VALUES (4, Iwona, Waza, 22, Gliwice, 4.04 );";
Odrobina SQL sql = "SELECT * from STUDENCI"; sql = "SELECT * from STUDENCI where ADRESS = Gliwice "; sql = "SELECT NAME,SURNAME from STUDENCI"; sql = "SELECT NAME,SURNAME from STUDENCI where AGE < 21"; sql = "SELECT NAME,SURNAME from STUDENCI"\ " where AGE < 21 and AVERAGE >= 4.5";
Odrobina SQL sql = "UPDATE STUDENCI set AVERAGE = 3.8 where ID=3"; sql = "DELETE from STUDENCI where ID=2"; sql = "DROP TABLE STUDENCI";
Konsola SQLite Kompliacja: cl shell.c sqlite3.c Powstanie: shell.exe
Komendy konsoli SQLite.exit.help.restore test.db.tables //zamknięcie konsoli //pomoc //załadowanie bazy //lista tabel SELECT * FROM STUDENCI; DELETE FROM STUDENCI where ID = 1;.backup test.db //zapisanie bazy do pliku
Przykład W katalogu użytkownika systemu Windows znajduje się ukryty katalog: Dane aplikacji Jeżeli używaliśmy przeglądarki Firefox to możemy skopiować z podkatalogów plik: Mozilla\Firefox\Profiles\19e9r3ha.default\cookies.sqlite do katalogu w którym mamy: sqlite.exe W konsoli SQLite wykonujemy np. polecenia:.restore cookies.sqlite.tables // wynik: moz_cookies SELECT * FROM moz_cookies WHERE host =.onet.pl ;
Ideologia To był (za) krótki wstęp do SQLite. Szczegóły można znaleźć w dokumentacji. Dotychczasowy kod był pisany w stylu C.
Ideologia To był (za) krótki wstęp do SQLite. Szczegóły można znaleźć w dokumentacji. Dotychczasowy kod był pisany w stylu C. Jak to ubrać w obiektowość? Czyli w jaki sposób napisać to bardziej zawile, wkładając w to dużo więcej pracy?
Ideologia To był (za) krótki wstęp do SQLite. Szczegóły można znaleźć w dokumentacji. Dotychczasowy kod był pisany w stylu C. Jak to ubrać w obiektowość? Czyli w jaki sposób napisać to bardziej zawile, wkładając w to dużo więcej pracy? Po co? Aby w przyszłości móc wygodnie i szybko z tego korzystać? Zaprojektujemy klasę w C++ do obsługi bazy danych...
DataBase.h #pragma once #include <iostream> #include <vector> #include <string> #include <sqlite3.h> #pragma comment( lib, "sqlite3.lib" ) typedef std::vector<std::string> Record; class DataBase { public: DataBase( const char * ); ~DataBase( void ); bool open( const char * ); void close( void ); bool is_open( void ); std::vector<record>& query( const char * ); bool query( const char *, std::ostream & ); private: //... };
Zysk - wygoda i szybkość użycia #include <iostream> #include <cstdlib> #include "DataBase.h" int main() { DataBase baza("test.db"); baza.query( "SELECT * FROM STUDENCI", std::cout ); baza.close(); } system("pause"); return 0;
Projektowanie Mamy możliwość tworzenia wielu tabel i przetwarzania danych w nich zawartych przy użyciu zapytań SQL. Czy można zaprojektować taką klasę aby ukryć zapytania SQL w mechanizmach tej klasy? Programista używający tej klasy nie musi znać SQL a. Odpowiedź: TAK, ale pod warunkiem, że uczynimy tę klasę bardziej wyspecjalizowaną (np. sprecyzujemy rodzaje danych i mechanizmy operowania na nich).
Przykładowy problem Zaprojektować klasę, której interfejs publiczny nie wymaga znajomości SQL a, oraz która ułatwi nam zaprogramowanie następujących założeń: - Nasz program gromadzi dane ściśle określonego rodzaju, które jesteśmy w stanie trzymać w jednej tabeli. - Chcemy mieć możliwość prostego modyfikowania pól w zadanym rekordzie. - Chcemy mieć możliwość prostego dodawania i usuwania rekordów. - Chcemy mieć możliwość uzyskania listy rekordów, które spełniają warunek: w ustalonej kolumnie jest ustalona wartość.
SimpleDataBase.h #pragma once #include "DataBase.h" #define DATA_BASE_FILENAME #define UNIQUE_TABLENAME "dane.db" "DANE" class SimpleDataBase : public DataBase { public: SimpleDataBase( std::string ); // Zostanie utworzony lub otwarty plik. // Podajemy nazwy kolumn. Jeśli inne niż istniejące, // to kasujemy obecną tabelę i tworzymy nowe bool is_open( void ); //??? int lastrecordnumber( void ); //...
SimpleDataBase.h - c.d. //... // dodaje rekord, brakujące pola ustawi na NULL bool add( std::vector<std::string> & ); // kasuje wskazany rekord bool erase( int ); std::string get( int, int ); bool set( int, int, std::string& ); void print( std::ostream & ); // drukuje do strumienia rekordy w których wskazana kolumna // ma zadaną wartość void ifprint( std::ostream &, int, std::string ); private: //... };
Problemy Publiczne dziedziczenie prowadzi do możliwości użycia dla obiektu klasy SimpleDataBase metod klasy bazowej. Nie chcemy jednak aby na tym poziomie był dostęp na przykład do metod query i close.
Problemy Publiczne dziedziczenie prowadzi do możliwości użycia dla obiektu klasy SimpleDataBase metod klasy bazowej. Nie chcemy jednak aby na tym poziomie był dostęp na przykład do metod query i close. class SimpleDataBase : private DataBase { //... }; Rodzi to jednak kolejny problem, gdyż metoda is_open również przestaje być dostępna na tym poziomie. Nie chcemy też pisać kolejnej jej implementacji w klasie pochodnej.
Problemy Publiczne dziedziczenie prowadzi do możliwości użycia dla obiektu klasy SimpleDataBase metod klasy bazowej. Nie chcemy jednak aby na tym poziomie był dostęp na przykład do metod query i close. class SimpleDataBase : private DataBase { //... }; Rodzi to jednak kolejny problem, gdyż metoda is_open również przestaje być dostępna na tym poziomie. Nie chcemy też pisać kolejnej jej implementacji w klasie pochodnej. class SimpleDataBase : private DataBase { public: using DataBase::is_open; //... };
Obserwacja Jednoczesne istnienie dwóch obiektów klasy SimpleDataBase prowadzi do tego, iż jeden z nich nie ma dostępu do pliku, a zatem nie może wykonać żadnej operacji do której został stworzony. To poważna wada tego rozwiązania. Należy tak zaprojektować mechanizmy klasy, aby do takich sytuacji nie dochodziło. Wzorzec projektowy: Singleton - gwarantuje, że istnieje jednocześnie tylko jeden obiekt danej klasy, i zapewnia globalny dostęp do niego w całym programie.
Koniec