badziewne Opracowanie na egzamin dyplomowy[1], Opracowanie pytań na egzamin dyplomowy


Opracowanie pytań na egzamin dyplomowy.

    1. Zadania preprocesora

Rusza do pracy przed kompilatorem. Dyrektywy preprocesora oznacza się przez #. Dołącza pliki nagłówkowe. #define pozwala na definiowanie oznaczeń. Mamy możliwość tworzenia makr. Oznaczenia charakterystyczne dla danego komplitora #pragma.

    1. Optymalizacja kodu

Pozawala przyspieszyć wykonanie danego programu kosztem rozmiaru kodu wynikowego lub przenośności.

gcc:

-O - podstawowa, najszybsza

-O2 - mocniejszy, ale jeszcze bezpieczny

-O3 - niebezpieczny

-Os - rozmiar pliku

    1. Metody konstrukcji złożonych deklaracji

  1. czytamy nazwę

  2. posuwamy się w prawo (gdyż tam mogą się znajdować najsilniejsze operatory () i [] )

  3. jeżeli w prawo już nic nie ma albo jest nawias zamykający - zaczynamy czytanie w lewo. Czytamy do końca lub do napotkania nawiasu.

  4. Jeżeli natknęliśmy się na nawias wychodzimy z niego i rozpoczynamy czytanie w prawo (czyli od punktu 2)

typ (*twf[5])(typ,typ) - tablica wskaźników do funkcji typ fun(typ,typ)

int ( * (*fw)(int, char *) )[2] - fw jest wskaźnikiem do funkcji wywoływanej z argumentami (int, char *), a zwracającej wskaźnik do 2 elementowej tablicy typu int.

    1. Instrukcje iteracyjne

nazywane pętlami umożliwiają powtarzanie pewnego zbioru instrukcji do momentu spełnienia warunku wyjścia z pętli

- while (warunek) instrukcja; — warunek sprawdzany na początku

- do instrukcja while(warunek); — warunek sprawdzany na końcu

- for (inicjalizacja; warunek; inkrementacja ) — inicjalizacja jest wykonywana tylko raz przed rozpoczęciem wykonywania pętli, warunek jest sprawdzany przed każdą iteracją, inkrementacja jest wykonywana po każdym obiegu.

- foreach(typ nazwa in kolekcja) — przechodzi kolejno miedzy elementami kolekcji

    1. Instrukcje warunkowe

If, switch, operator warunkowy

    1. Tworzenie i usuwanie zmiennych dynamicznych

Zmienne dynamiczne na stosie

Zmienne dynamiczne na stercie, jawne

Uwaga: W poniższej sytuacji wiązanie typu jest statyczne; dynamiczne jest natomiast wiązanie pamięci.

Zmienne dynamiczne - są to zmienne tworzone przez programistę w pamięci wolnej komputera (na stercie)
W języku „C++ ” do dynamicznego przydzielania pamięci wygodnie jest wykorzystywać operatory new i delete :
Operator new służy do alokacji pamięci (w C podobnie malloc i calloc) obiektom i tablicom obiektów.
Przy alokacji obiektów (ale nie tablic) możliwa jest inicjalizacja.
Operator delete służy do zwalniania przydzielonej pamięci

wskaźnik_na_obiekt = new typ_obiektu [parametry_inicjacyjne];

delete wskaźnik_na_obiekt;

np.

char *wsk;//wskaźnik na zmienną zakową

wsk = new char;//utworzenie nowego obiektu char

delete wsk;//zwolnienie obiektu

int* wsk ; // wskaźnik na zmienną typu całkowitego

wsk = new int ; // utworzenie nowego obiektu (nowej zmiennej int)

*wsk = 100 ; // przypisanie wartości (poprzez wskaźnik)

cout<<( *wsk ); // wydrukowanie zawartości zmiennej dynam.

• • •

delete wsk ; //zwolnienie obiektu

Malloc(size* n), calloc( n, size), realloc(t,size), New free ,delete

- w przeciwieństwie do malloc i free New i delete uruchamiają konstruktory i destruktory. Malloc zwraca void* new zwraca typ*.

    1. Zmienne i zarządzanie pamięcią (słowa kluczowe: auto register static, extern)

AUTO: Wszystkie zmienne deklarowane wewnątrz funkcji są lokalne dla danej funkcji. Żadna inna funkcja nie ma do nich bezpośredniego dostępu. Każda zmienna lokalna funkcji, zaczyna żyć w chwili wywołania funkcji, a znika po jej zakończeniu.

Z tego typu obiektami wiąże się słowo kluczowe auto : auto int x;

Deklarator auto jest prawie zawsze nadmiarowy. Jest on rzadko używany, ponieważ obiekty są definiowane jako automatyczne przez domniemanie.

Modyfikator nakazujący kompilatorowi utworzenie zmiennej na stosie aplikacji (przez prostą inkrementację lub dekrementację wskaźnika stosu). Kiedyś nazywane było to automatycznym tworzeniem zmiennej. Stąd wzięła się nazwa tego modyfikatora.

Modyfikator auto kiedyś nie pozwalał kompilatorowi umieścić zmiennej w rejestrze, tak aby istniała pewność co do posiadania przez zmienną adresu w pamięci RAM. Ta ostatnia właściwość jest niezbędna jeżeli mają być do takiej zmiennej tworzone wskaźniki czyli można wykorzystać wobec niej operator &.

Dzisiaj modyfikator auto ze względu na jego domyślne stosowanie dla wszystkich zmiennych agregatowych, złożoność współczesnych kompilatorów, ich możliwości analizy oraz optymalizacji kodu - ma znaczenie wyłącznie historyczne. Użycie tego modyfikatora nie jest nadmiarowe jeżeli nie wyspecyfikuje się typu zmiennej - ponieważ domyślnie przyjmuje się wtedy typ int, a jeden modyfikator deklaracji zmiennej jest wymagany. Np.: auto indeks = 7;

REGISTER: Deklaracja register jest deklaracją auto, ale niesie więcej informacji. Informuje kompilator, że zmienna będzie często używana i należy ją umieszczać jeżli można w rejestrach przez co znacznie zwiększa sie szybkość progamu.

Kompilator może tę deklarację zignorowac jeżli np. ilość zmiennych zadeklarowanych jako register jest większa niż liczba rejestrów lub gdy w programie pobierany jest adres tej zmiennej.

Deklaracje tej zmiennej ma postać: register int x;

Nie możemy się odwoływać do zmiennej z modyfikatorem register o jej adres. Rejestr nie jest adresem pamięci, gdy usilnie próbujemy dowiadywać się jednak o jej adres, kompilator umieści zmienną w pamięci (gdzie może nam podać adres).

Jednak współczesne kompilatory na tyle optymalizują kod, że same wybierają, które zmienne mają być umieszczone w rejestrze procesora, a które nie.

Więc w praktyce to, że damy modyfikator register nie daje nam pewności, że zmienna będzie przechowywana w rejestrze.

STATIC: to zmienne deklarowane przy pomocy słowa kluczowego static. Można ją stosować do zmiennych globalnych i lokalnych. Są wstępnie inicjowane zerami .

Nie można zadeklarować argumentów formalnych funkcji static lub extern.

Specyfikator static można stosować do nazw obiektów i funkcji oraz unii anonimowych;

Lokalne zmienne statyczne są tak samo lokalne dla funkcji, jak zmienne automatyczne. Jednak w przeciwieństwie do automatycznych nie pojawiają się i nie znikają razem z wywołaniem funkcji, lecz istnieją między jej wywołaniami. Czyli przy powtórnym wejściu do funkcji zmienna ma wartość taką jaka miała przy opuszczaniu funkcji.

Globalne zmienne ze specyfikatorem static ogranicza dostępność zmiennej do modułu gdzie ją zdefiniowano.

EXTERN: Oznacza, że deklaracja nie jest deklaracją w sensie fizycznym, a jedynie odwołaniem do deklaracji znajdującej się w innej jednostce kompilacji (module, pliku, bibliotece - przyp. autora). Jednym słowem, jest to sposób na poinformowanie kompilatora, by nie szukał danej zmiennej globalnej w aktualnym pliku.

Specyfikatory extern i static wzajemnie się wykluczają, dlatego nie zaleca się tworzenia deklaracji zawierających obydwa te słowa jednocześnie. Ponadto zabroniona jest inicjalizacja zmiennej zadeklarowanej z użyciem modyfikatora extern!

Jeżeli extern poprzedza deklarację nie zainicjalizowanej zmiennej (globalnej lub lokalnej) albo stałej, oznacza to wówczas, że deklarowany obiekt nie zawiera się w danym pliku, a w innej jednostce kompilacji (może to być inny moduł, nagłówek, biblioteka statyczna lub dynamiczna itp.):

Jeżeli specyfikator ten poprzedza deklarację stałej zainicjalizowanej, oznacza to, że taka stała posiada łączność zewnętrzną (więc z kolei extern przed stałą niezainicjalizowaną importuje taką stałą). W odróżnieniu od zmiennych, które można powstrzymać przed eksportowaniem symbolu przez specyfikator static, stałe globalne wymagają extern, żeby eksportować symbol na zewnątrz.

Wewnątrz funkcji deklaracje ze specyfikatorem extern również wskazują, że pamięć dla deklarowanych obiektów będzie zarezerwowana gdzie indziej. Jeżeli deklaracja obiektu wewnątrz bloku (pętli, funkcji itp. - przyp. autora) nie zawiera specyfikatora extern, wówczas obiekt ten nie ma łączności i jest unikalny w funkcji. W przeciwnym wypadku, gdy w zasięgu otaczającym dany blok obowiązuje zewnętrzna deklaracja tego samego obiektu, wówczas ma on taka samą łączność, jak w deklaracji zewnętrznej i odnosi się do tego samego obiektu.

Nierzadko można natrafić na deklarację prototypu funkcji, której typ wartości zwracanej poprzedzony jest specyfikatorem extern. Oznaczać to może chociażby deklarację funkcji, której ciało (oraz pierwotna deklaracja) znajduje się w innym module. Opcjonalnie można stosować ten specyfikator przed deklaracjami prototypu funkcji w tym samym module, zwłaszcza, gdy ciało danej funkcji umieszczona zostało poniżej funkcji głównej.

Jak jednak zostało wspominane, nie jest to zabieg konieczny, a co za tym idzie, większość kompilatorów ignoruje ten specyfikator przed nazwą funkcji (każda deklaracja funkcji posiada ten kwalifikator domyślnie). Często jest to jednak wymóg w przypadkach, gdy w skład projektu wchodzą m.in. pliki asemblerowskie. Dzięki deklaracji z użyciem extern można się odwoływać do funkcji napisanych w assemblerze, a nigdzie wcześniej niezadeklarowanych (trochę więcej na ten temat w tej części artykułu).

Znaczenie zgoła inne niż dotychczas słowo kluczowe extern ma w języku C++ dla objętych specjalnym blokiem fragmentów kodu. Dzięki wspomnianemu blokowi możliwe jest jawne określenie języka nadającego reguły kompilacji dla danego bloku.

Język C może być bardzo łatwo konsolidowany (łączony) z wieloma innymi językami, które kompilowane są bezpośrednio do kodu maszynowego (m.in.: Assembler, Fortran oraz C++). Ponadto dzięki specjalnym bibliotekom można go łączyć z językami bardzo wysokiego poziomu (takimi jak np. Python czy też Ruby)

    1. Dynamiczne struktury danych

Stos

Stos (ang. Stack) - liniowa struktura danych, w której dane dokładane są na wierzch stosu i z wierzchołka stosu są pobierane (bufor typu LIFO, Last In, First Out; ostatni na wejściu, pierwszy na wyjściu).

Kolejka

Przeciwieństwem stosu jest kolejka, bufor typu FIFO (ang. First In, First Out; pierwszy na wejściu, pierwszy na wyjściu), w którym dane obsługiwane są w takiej kolejności, w jakiej zostały dostarczone

Lista

lista - rodzaj kontenera - dynamiczna struktura danych, używana w informatyce. Składa się z podstruktur wskazujących na następniki i/lub poprzedniki.

Typowa lista jest łączona jednostronnie - komórki zawierają tylko odnośnik do kolejnej komórki. Innym przypadkiem jest lista dwustronna, gdzie komórki zawierają także odnośnik do poprzednika.

Popularna jest także lista zwana słownikową, która zazwyczaj jest wariacją listy jednostronnej. Z reguły stosuje się ją tam, gdzie elementy listy zawierają kilka pól z danymi, a kolejny element może rozszerzać pojęcie (definicję poprzedniego). Przykładem jest prosty translator tekstu, zrealizowany jako lista, gdzie każdy z elementów zawiera dane wyraz i definicja wyrazu - może się okazać, że definicja danego wyrazu ma swoje rozwinięcie (definicję) w pewnym innym elemencie, wówczas tam kieruje się dodatkowy łącznik.

lista jednokierunkowa - w każdym elemencie listy jest przechowywane odniesienie tylko do jednego sąsiada (następnika lub poprzednika).

lista dwukierunkowa - w każdym elemencie listy jest przechowywane odniesienie zarówno do następnika jak i poprzednika elementu w liście. Taka reprezentacja umożliwia swobodne przemieszczanie się po liście w obie strony.

lista cykliczna - następnikiem ostatniego elementu jest pierwszy element, a poprzednikiem pierwszego ostatni. Po liście można więc przemieszczać się cyklicznie. Nie ma w takiej liście charakterystycznego ogona (ani głowy), często rozpoznawanego po tym, że jego następnik jest pusty (NULL).

lista z wartownikiem - lista z wyróżnionym elementem zwanym wartownikiem. Jest to specjalnie oznaczony element niewidoczny dla programisty wykorzystującego listę. Pusta lista zawiera wtedy tylko wartownika. Zastosowanie wartownika znacznie upraszcza implementację operacji na listach.

tablice wskaźników

wskaźniki do funkcji

tablica wskaźników na funkcję

    1. Oszczędzanie pamięci.

Unie anonimowe

union {

int a;

float b;

char c;

} ;

Tak zdefiniowana unia jest tzw. unia anomimową. Takie unie same nie mają nazwy, jak też nie ma nazwy jedyny egzemplarz tej unii.

Do składników tej unii odwołujemy bezpośrednio poprzez nazwę składowej.

Są dwa sposoby oszczędzania pamięci:

1.Używanie tej samej pamięci do przechowywania w różnym czasie różnych obiektów - unie

2. Umieszczenie w jednym bajcie więcej niż jednego obiektu - pola bitowe

Jest to zbiór przylegających do siebie bitów, znajdujących się w jednej jednostce pamięci zwanej słowem.

np: unsigned int odczyt :1; unsigned int zapis :3;

Pola bitowe musi to być typu całkowitego int, signed int lub unsigned int.

Każdemu polu jest przydzielona liczba bitów wynikająca z deklaracji, ale nie więcej niż 16 dla jednego pola bitowego (Borland 3.1).

Pola bitowe mogą się znaleźć tylko w strukturach, uniach i klasach.

struct dostep {

unsigned int odczyt :2;

unsigned int zapis :2;

unsigned int przeglad :4

unsigned int brak :1;

};

- stosować sizeof (zwraca długość w bajtach)

- pobierać adresu

- definiować tablic i adresów na pola bitowe

- stosować makra offsetof (podaje położenie pola w strukturze, w bajtach od początku struktury)

- pola bitowe można definiować tylko w strukturach, uniach i klasach.

- deklarować referencji do pól bitowych

    1. Arytmetyka wskaźników

Wskaźnik - zmienna przechowująca adres jakiegoś obiektu oraz informację o jego typie (wyjątek: void)

- dodanie liczby do wskaźnika powoduje jego przesunięcie po pamięci o tyle miejsc danego typu

- nazwa zmiennej jest adresem początku pamięci

- nazwa tablicy jest wskaźnikiem na adres ale nie można go przesuwać (tab++), za to można się za jego pomocą odwoływać ( *(tab+3) )

- przy odwoływaniu się do wskaźnika za pomocą zapisu tablicowego nie jest sprawdzana legalność takiego odwołania

- odjęcie od siebie 2 wskaźników ustawionych na elementy tej samej tablicy daje w rezultacie odległość między nimi

- wskaźniki można porównywać (jeżeli 2 wskaźniki są sobie równe wówczas wskazują ten sam obiekt)

- porównanie wskaźników operatorami <> >= <= ma sens tylko dla wskaźników ustawionych na ta samą tablicę

- aby przekazać tablicę do funkcji jako tylko-do-odczytu stosujemy jako parametr formalny funkcji wskaźnik const

- wskaźnikowi można nadać konkretny adres ( wsk = 000000) lub utworzyć go na konkretnym adresie ( wsk = adr_ptr new typ )

- wskaźnik można ustawić na:

+adres: wsk = & obiekt; wsk = 3007370

+wskaźnik: wsk = wsk;

+tablicę: wsk = tab; wsk = &tab[3] ;

+funkcję: wsk = fun;

+obiekt bez nazwy: wsk = new typ(arg);

+string: wsk = „str”;

-tablica wskaźników: typ *tabw[num];

-wskaźnik do funkcji: int (*wfun)(…); - deklaracja

wfun = fun; - przypisanie

(*wfun)(...) - użycie

    1. Sposoby przekazywania parametrów do funkcji

-Argumenty / parametry formalne - deklaracja/definicja funkcji;

-Argumenty / parametry aktualne - wywołanie funkcji;

Przez wartość: tworzone są kopie (na stosie) argumentów aktualnych.

Przez Referencję: przekazywany (i odkładany na stosie) jest adres do zmiennej

Przez wskaźnik: wskaźnik będący parametrem aktualnym jest ustawiany na podany adres

    1. Funkcje rekurencyjne

Rekurencja, zwana także rekursją (ang. recursion, z łac. recurrere, przybiec z powrotem) to w logice, programowaniu i w matematyce odwoływanie się np. funkcji lub definicji do samej siebie.

double silnia (unsigned n)

{

if (n > 0)

return ( n * silnia (n -1));

else

return 1.0;

}

    1. Typ void

Pusty typ, wskaźnik, na który i z którego można rzutować bez utraty danych. Nie niesie informacji o typie.

    1. Pochodne typy danych

Wyliczeniowy typ danych

enum nazwa { jeden, dwa };

Struktury

struct nazwa {

typ1 nazwa1;

typ2 nazwa2;

};

Unie

union nazwa {

typ1 nazwa1;

typ2 nazwa2;

};

Pola bitowe

typ [identyfikator] : długość;

Tablice

typ nazwa[liczba];

Wskaźniki

typ *nazwa;

typ **nazwa;

typ_zwracany (*nazwa_wsk_do_funkcji)(typ nazwa_parametru1,typ nazwa_parametru2,...);

Klasa,

dziedziczenie

    1. Paradygmaty programowania.

<z ważniaka>

chodzi raczej o zbiór mechanizmów, jakich programista używa, pisząc program, i o to, jak ów program jest następnie wykonywany przez komputer. Zatem paradygmat programowania to ogół oczekiwań programisty wobec języka programowania i komputera, na którym będzie działał program.

programowanie imperatywne

Programowanie imperatywne to najbardziej pierwotny sposób programowania, w którym program postrzegany jest jako ciąg poleceń dla komputera

Ściślej, obliczenia rozumiemy tu jako sekwencję poleceń zmieniających krok po kroku stan maszyny, aż do uzyskania oczekiwanego wyniku.

Stan maszyny należy z kolei rozumieć jako zawartość całej pamięci oraz rejestrów i znaczników procesora.

Ten sposób patrzenia na programy związany jest ściśle z budową sprzętu komputerowego o architekturze von Neumanna, w którym poszczególne instrukcje (w kodzie maszynowym) to właśnie polecenia zmieniające ów globalny stan.

Języki wysokiego poziomu — takie jak Fortran, Algol, Pascal, Ada lub C — posługują się pewnymi abstrakcjami, ale wciąż odpowiadają paradygmatowi programowania imperatywnego.

Przykładowo, instrukcje podstawienia działają na danych pobranych z pamięci i umieszczają wynik w tejże pamięci, zaś abstrakcją komórek pamięci są zmienne.

programowanie obiektowe

W programowaniu obiektowym program to zbiór porozumiewających się ze sobą obiektów, czyli jednostek zawierających pewne dane i umiejących wykonywać na nich pewne operacje

Ważną cechą jest tu powiązanie danych (czyli stanu) z operacjami na nich (czyli poleceniami) w całość, stanowiącą odrębną jednostkę — obiekt.

Cechą nie mniej ważną jest mechanizm dziedziczenia, czyli możliwość definiowania nowych, bardziej złożonych obiektów, na bazie obiektów już istniejących.

Zwolennicy programowania obiektowego uważają, że ten paradygmat dobrze odzwierciedla sposób, w jaki ludzie myślą o świecie

Nawet jeśli pogląd ten uznamy za przejaw pewnej egzaltacji, to niewątpliwie programowanie obiektowe zdobyło ogromną popularność i wypada je uznać za paradygmat obecnie dominujący.

programowanie funkcyjne

Tutaj program to po prostu złożona funkcja (w sensie matematycznym), która otrzymawszy dane wejściowe wylicza pewien wynik

Zasadniczą różnicą w stosunku do poprzednich paradygmatów jest brak stanu maszyny: nie ma zmiennych, a co za tym idzie nie ma żadnych efektów ubocznych.

Nie ma też imperatywnych z natury, tradycyjnie rozumianych pętli (te wymagają np. zmiennych do sterowania ich przebiegiem).

Konstruowanie programów to składanie funkcji, zazwyczaj z istotnym wykorzystaniem rekurencji. Charakterystyczne jest również definiowanie funkcji wyższego rzędu, czyli takich, dla których argumentami i których wynikami mogą być funkcje (a nie tylko „proste” dane jak liczby lub napisy).

programowanie w logice (programowanie logiczne)

Na program składa się zbiór zależności (przesłanki) i pewne stwierdzenie (cel)

Wykonanie programu to próba udowodnienia celu w oparciu o podane przesłanki.

Obliczenia wykonywane są niejako „przy okazji” dowodzenia celu.

Podobnie jak w programowaniu funkcyjnym, nie „wydajemy rozkazów”, a jedynie opisujemy, co wiemy i co chcemy uzyskać.

Programowanie strukturalne

O programowaniu strukturalnym wspominamy z kronikarskiego obowiązku, jest to bowiem bardzo dobrze znany i powszechnie stosowany „podparadygmat” programowania imperatywnego, czy ściślej — proceduralnego. Chodzi w nim o tworzenie programów z kilku dobrze zdefiniowanych konstrukcji takich jak instrukcja warunkowa if-then-else i pętla while, za to bez skoków (go to). Powinno to sprzyjać pisaniu programów przejrzystych, łatwych w rozumieniu i utrzymaniu.

Programowanie sterowane zdarzeniami

Chodzi o programowanie, w którym zamiast zasadniczego nurtu sterowania mamy wiele drobnych programów obsługi zdarzeń, uruchamianych w chwili wystąpienia odpowiedniego zdarzenia. Zdarzenia mogą być wywoływane przez urządzenia wejścia-wyjścia (np. naciśnięcie klawisza, ruch myszką) lub przez same programy obsługi zdarzeń. Oprócz zbioru programów obsługi zdarzeń potrzebny jest też zarządca, który będzie je uruchamiał.

Programowanie współbieżne

Tym razem mówimy o „nadparadygmacie”, gdyż chodzi o wykonywanie wielu zadań obliczeniowych w tym samym czasie. Istotą problemu jest koordynacja zadań, które komunikują się ze sobą i korzystają ze wspólnych zasobów, a tym samym są od siebie zależne. Pojęcie programowania współbieżnego jest ogólniejsze od programowania równoległego i od programowania rozproszonego. Równoległość oznacza równoczesne wykonywanie zadań przez wiele procesorów; współbieżność obejmuje ponadto podział czasu jednego procesora między wiele zadań — czyli to, z czym mamy do czynienia w praktycznie każdym systemie operacyjnym. O programowaniu rozproszonym mówimy, gdy mamy wiele procesorów połączonych siecią (ale nie wieloprocesorowy komputer).

    1. Koncepcja abstrakcyjnego typu danych.

Specyfikacja zbioru danych i operacji, które mogą być na nich przeprowadzone. Niezależne od implementacji. Stos, lista, kolejka, Mapa , Drzewo.

Abstrakcyjne typy danych

Typ abstrakcyjny to konstrukcja języka programowania, w której definiujemy typ (w dotychczasowym rozumieniu) oraz operacje na nim w taki sposób, że inne byty w programie nie mogą manipulować danymi inaczej niż za pomocą zdefiniowanych przez nas operacji.

Istotą rzeczy jest tu oddzielenie części „prywatnej” typu (czyli szczegółów reprezentacji danych i implementacji poszczególnych operacji) od części „publicznej” (tego, co można wykorzystywać w innych miejscach programu).

Ta koncepcja stała się podstawą rozwoju programowania obiektowego: instancje abstrakcyjnych typów danych (czyli konkretne wartości z typów, zwane obiektami) można postrzegać jako samodzielne byty, które współdziałają poprzez wykonywanie udostępnianych sobie operacji.

    1. Paradygmat programowania obiektowego.

Język obiektowy musi posiadać trzy podstawowe cechy:

Abstrakcyjne typy danych.

Dziedziczenie.

Dynamiczne wiązania wywołań metod z metodami (ściślej: z definicjami metod).

Co to znaczy "obiektowy" (object-oriented)

Model obiektowy -- cechy

Orientacja na obiekty

Tożsamość obiektów

Złożone typy danych

Abstrakcyjne typy danych

Osłonowanie (encapsulation)

Polimorfizm

Dziedziczenie (inheritance)

Smalltalk, C++, Java, C#, Ada, JavaScript

    1. Przeciążanie funkcji i operatorów w języku C++.

Nadanie funkcjom tej samej nazwy, ale przyjmującymi inne zestawy parametrów. Podobnie z operatorami.

Typ_zwacany operator@ (Arg, …)

Polimofizm

Przeciążanie

W niektórych językach dopuszcza się możliwość definiowania w tym samym zakresie widoczności funkcji o identycznych nazwach, lecz różnych sygnaturach. Tę możliwość

wykorzystuje się zwykle w odniesieniu do funkcji o takiej samej (podobnej) semantyce, lecz różnych typach argumentów. Omawiany sposób definiowania funkcji nazywa się przeciążaniem nazw funkcji. Wybór właściwej funkcji ze zbioru wszystkich funkcji o przeciążonej nazwie odbywa się na podstawie analizy jej parametrów aktualnych i ma miejsce

na etapie kompilacji programu.

Jeśli w programie zdefiniowano dwie funkcje o tym samym identyfikatorze i jednakowych sygnaturach, to:

- kompilator sygnalizuje błąd, w wypadku, gdy funkcje są różnych typów,

- funkcja zdefiniowana później w porządku tekstowym przesłania tę wcześniejszą, w przeciwnym wypadku.

W sytuacji, gdy dla poprawnie przeciążonej funkcji nie da się dopasować (analiza list parametrów formalnych i aktualnych!) żadnej z jej wersji do przedmiotowego wywołania,

kompilator sygnalizuje błąd,

Operatory

W języku C++ oprócz nazw funkcji można przeciążyć także większość standardowych operatorów (możliwość ta nie dotyczy operatorów: ., .*, →*, ::, ?:, sizeof). Nie wolno

natomiast definiować własnych operatorów.

Przeciążanie operatorów odbywa się przy zachowaniu następujących reguł:

- definicje operatorów unarnych, operatora indeksacji [] oraz wszelkich operatorów przypisania (=, +=, *=, itd.) można przeciążyć za pomocą funkcji składowej klasy,

- definicje pozostałych dozwolonych operatorów binarnych można przeciążyć przy użyciu funkcji składowej klasy lub funkcji zaprzyjaźnionej z klasą,

- w nowej definicji przeciążonego operatora przynajmniej jeden z jego argumentów jest obiektem przedmiotowej klasy; w szczególności, w definicji sformułowanej przy

użyciu funkcji składowej klasy - lewy argument operatora (pierwszy, ukryty parametr funkcji) musi być obiektem przedmiotowej klasy,

- przeciążony operator zachowuje dotychczasowe: arność,

priorytet oraz łączność.

Nową definicję operatora @ można zadać za pomocą

funkcji składowej klasy, o nagłówku postaci:

- typ_funkcji operator @ (typ_arg arg), w wypadku, gdy

@ jest operatorem binarnym,

- typ_funkcji operator @ (), w wypadku, gdy @ jest operatorem unarnym.

Domyślnym argumentem operatora @ jest w obu powyższych wypadkach przedmiotowy obiekt (*this).

W celu przeciążenia unarnego operatora postfiksowego

(np. x++), należy funkcję określającą przeciążenie zdefiniować jako funkcję o postaci:

typ_funkcji operator @ (int arg),

z dodatkowym argumentem typu int, o znaczeniu wyłącznie rozpoznawczym.

    1. Kapsułkowanie danych i funkcji-członków klasy w C++.

Private, protected

    1. Sposoby inicjalizacji ukrytych danych klasy w C++.

Lista inicjalizacyjna konstruktora. Konstruktor, funkcje zaprzyjaźnione?

    1. Sposoby rozwiązania problemu wycieki pamięci w C++.

Brak kontroli. W destruktorze. Delete.

public:

~player();

Player:: ~player()

{

}

    1. Mechanizmy dostępu do składowych klasy tworzonych statycznie i dynamicznie.

->, . klasa::pole

    1. Definicje systemu rozproszonego i systemu równoległego.

Grupa węzłów autonomicznych, połączonych ze sobą za pomocą sieci komputerowej. Na każdym węźle implementowane są składniki systemu. Funkcjonuje warstwa pośrednia, która pozwala składowym działać w taki sposób, że widzi ten sam system jako jednolite zintegrowany system komputerowy. System równoległy implementacja składników systemu jest wykonywana na komputerze o architekturze równoległej.

    1. Wymagania stawiane systemom rozproszonym.

Proces projektowania oprogramowania rozpoczyna się fazą pojęciową tzn. opracowania wymagań dla systemu zgodnie z funkcjonalnymi i niefunkcjonalnymi właściwościami systemu.

Wymagania funkcjonalne mogą być jasno i ściśle określone w analizie zorientowanej obiektowo.

Wymagania niefunkcjonalne odnoszą się do jakości systemu. Trudniej je sformalizować i utożsamić z funkcjami konkretnych składników. Tymi wymaganiami są:

  1. Współdzielenie zasobów. System rozproszony umożliwia współdzielenie zasobów umieszczonych na różnych komputerach w sieci. Zasoby to sprzęt, oprogramowanie oraz dane. Podział zasobów stosuje się w celu zwiększenia efektywnego wykorzystania kosztownych zasobów.

  2. Otwartość systemu mierzy się łatwością rozszerzenia go o nowe nie należące do niego zasoby. Systemy rozproszone są otwarte i zwykle zawierają sprzęt i oprogramowanie różnych producentów.

  3. Heterogeniczność jest spowodowana użyciem różnych języków programowania, systemów operacyjnych, technologii i platform sprzętowych.

  4. Skalowalność. W zasadzie systemy rozproszone powinny być skalowalne w tym sensie, że można zwiększyć ich możliwości przez dodanie nowych zasobów w celu spełnienia nowych wymagań stawianych systemowi. Architektura systemu jest skalowalna jeżeli posiada właściwości adaptacji do zwiększonego obciążenia bez względu na to czy obciążenie istnieje czy nie.

  5. Odporność na błędy. Odporność na błędy oznacza, że system kontynuuje pracę nawet wtedy, gdy pewne jego części zostaną zablokowane.

  6. Przezroczystość. Polega na ukryciu przed użytkownikiem rozproszonej natury systemu. Celem projektu systemu może być zapewnienie użytkownikom całkowicie przezroczystego dostępu do usług i wykluczenie potrzeby jakiejkolwiek informacji o rozproszeniu systemu

    1. Wymiary przezroczystości systemów rozproszonych.

Przezroczystość polega na ukryciu przed użytkownikiem rozproszonej natury systemu. Celem projektu systemu może być zapewnienie użytkownikom całkowicie przezroczystego dostępu do usług i wykluczenie potrzeby jakiejkolwiek informacji o rozproszeniu systemu. Wyróżnia się następujące wymiary przezroczystości systemów rozproszonych:

0x08 graphic
0x01 graphic

Wymiary przezroczystości nie są niezależne! Wymiary przezroczystości systemów rozproszonych są częścią międzynarodowego standardu otwartego projektowania rozproszonego (International Standard on The Open Distributed Design) ISO/EC, 1996.

    1. Modele statyczne i dynamiczne modelu metaobiektowego systemu rozproszonego.

    1. Typy warstwy pośredniej w systemach rozproszonych bazujących na paradygmacie programowania obiektowego.

    1. Najgłówniejsze składniki architektury CORBY.

    1. Składniki architektury DCOMU.