|
|
Linia 68: |
Linia 68: |
| </div> | | </div> |
| </div> | | </div> |
|
| |
| ==Test==
| |
|
| |
| <quiz type="exclusive">
| |
| Której cechy język obiektowy nie musi posiadać?
| |
| <wrongoption reply="Źle">abstrakcyjne typy danych</wrongoption>
| |
| <wrongoption reply="Źle">dynamiczne wiązanie wywołań metod z metodami</wrongoption>
| |
| <wrongoption reply="Źle">dziedziczenie</wrongoption>
| |
| <rightoption reply="Dobrze">podprogramy rodzajowe</rightoption>
| |
| </quiz>
| |
|
| |
| <quiz type="exclusive">
| |
| Jakie ograniczenie na przedefiniowywanie metod trzeba narzucić
| |
| w języku silnie typowanym?
| |
| <wrongoption reply="Źle">przedefiniowana metoda musi być bezparametrowa</wrongoption>
| |
| <wrongoption reply="Źle">przedefiniowana metoda musi być typu void</wrongoption>
| |
| <rightoption reply="Dobrze">przedefiniowana metoda musi zachować taki sam protokół</rightoption>
| |
| <wrongoption reply="Źle">nie trzeba narzucać żadnych ograniczeń</wrongoption>
| |
| </quiz>
| |
|
| |
| <quiz type="exclusive">
| |
| Rozstrzyganie odwołań do bytów o takiej samej nazwie mających definicje w dwóch klasach bazowych odbywa się w C++ za pomocą:
| |
| <rightoption reply="Dobrze">operatora \:\: (dwa dwukropki)</rightoption>
| |
| <wrongoption reply="Źle">operatora . (kropka)</wrongoption>
| |
| <wrongoption reply="Źle">tego nie da się zrobić</wrongoption>
| |
| <wrongoption reply="Źle">dziedziczenie wielokrotne nie jest w C++ dozwolone</wrongoption>
| |
| </quiz>
| |
|
| |
| <quiz type="exclusive">
| |
| W języku C++ obiekty zaalokowane na stosie dealokowane są:
| |
| <rightoption reply="Dobrze">niejawnie</rightoption>
| |
| <wrongoption reply="Źle">za pomocą delete</wrongoption>
| |
| <wrongoption reply="Źle">za pomocą free</wrongoption>
| |
| <wrongoption reply="Źle">w C++ nie ma takich obiektów</wrongoption>
| |
| </quiz>
| |
|
| |
| <quiz type="exclusive">
| |
| Językiem, w którym stosowane jest zawsze dynamiczne wiązanie
| |
| wywołań z metodami, jest:
| |
| <wrongoption reply="Źle">C++</wrongoption>
| |
| <wrongoption reply="Źle">C\#</wrongoption>
| |
| <wrongoption reply="Źle">Java</wrongoption>
| |
| <rightoption reply="Dobrze">Smalltalk</rightoption>
| |
| </quiz>
| |
|
| |
| <quiz type="exclusive">
| |
| Językiem, w któym klasa może być samoistna (tzn. nie mieć nadlasy), jest:
| |
| <rightoption reply="Dobrze">C++</rightoption>
| |
| <wrongoption reply="Źle">C\#</wrongoption>
| |
| <wrongoption reply="Źle">Java</wrongoption>
| |
| <wrongoption reply="Źle">Smalltalk</wrongoption>
| |
| </quiz>
| |
|
| |
| <quiz type="exclusive">
| |
| W języku C++ metody, które mają być wiązane dynamicznie, deklaruje się za pomocą:
| |
| <wrongoption reply="Źle">operatora -> (strzałka)</wrongoption>
| |
| <wrongoption reply="Źle">słowa abstract</wrongoption>
| |
| <wrongoption reply="Źle">słowa dynamic</wrongoption>
| |
| <rightoption reply="Dobrze">słowa virtual</rightoption>
| |
| </quiz>
| |
|
| |
| <quiz type="exclusive">
| |
| Który nagłówek poprawnie deklaruje w C++ metodę abstrakcyjną?
| |
| <wrongoption reply="Źle">virtual void p();</wrongoption>
| |
| <rightoption reply="Dobrze">virtual void p() \=0;</rightoption>
| |
| <wrongoption reply="Źle">void p() \=0;</wrongoption>
| |
| <wrongoption reply="Źle">abstract void p();</wrongoption>
| |
| </quiz>
| |
|
| |
| <quiz type="exclusive">
| |
| Klasy "lekkie", deklarowane jako struct, alokowane na stosie i nie
| |
| pozwalające na dziedziczenie występują w:
| |
| <wrongoption reply="Źle">C++</wrongoption>
| |
| <rightoption reply="Dobrze">C\#</rightoption>
| |
| <wrongoption reply="Źle">Javie</wrongoption>
| |
| <wrongoption reply="Źle">we wszystkich wymienionych tu językach</wrongoption>
| |
| </quiz>
| |
|
| |
| <quiz type="exclusive">
| |
| Który element nie występuje w JavaScripcie?
| |
| <rightoption reply="Dobrze">klasy</rightoption>
| |
| <wrongoption reply="Źle">obiekty złożone z par (nazwa własności, wartość)</wrongoption>
| |
| <wrongoption reply="Źle">operator new</wrongoption>
| |
| <wrongoption reply="Źle">zmienne</wrongoption>
| |
| </quiz>
| |
Zadanie 1
Co wypisuje poniższy program w języku C++? Odpowiedz, a potem skompiluj program i sprawdź... Uwaga — do poprawnego skompilowania programu potrzebne będzie dołączenie pliku nagłówkowego (#include <iostream>) i deklaracja przestrzeni nazw (using namespace std;).
class A
{
public:
virtual void f() { cout << ”A.f”; g(); }
void g() { cout << ”A.g”; h(); }
virtual void h() { cout << ”A.h”; }
};
class B: public A
{
public:
virtual void f() { cout << ”B.f”; g(); }
void g() { cout << ”B.g”; h(); }
virtual void h() { cout << ”B.h”; }
};
void main()
{
A x = B();
A* y = new B();
x.f();
y–>f();
}
Wskazówka: Pamiętaj, że dynamiczne wiązanie wywołań z metodami następuje tylko wtedy, gdy metoda jest zadeklarowana jako virtual, a instancja obiektu użyta do wywołania jest zaalokowana na stercie.
Zadanie 2
Jak zmieni się wynik programu z zadania 1, jeśli usuniemy deklarację virtual przy funkcji f w klasie A? Które deklaracje virtual można usunąć bez żadnej zmiany zachowania programu?
Wskazówka: Ważna jest deklaracja virtual przy metodzie w klasie bazowej. W klasie pochodnej nie jest konieczna, chyba że wiązanie dynamiczne ma prowadzić jeszcze dalej w dół hierarchii klas. Zwyczajowo jednak umieszcza się virtual i tu, i tu — dla zwiększenia czytelności kodu. Warto też pamiętać, że użycie virtual nie pociąga „obowiązku” zredefiniowania danej metody w klasie pochodnej.
Zadanie 3
Wykorzystanie dynamicznego wiązania wywołań z metodami oznacza, że często będziemy odwoływali się do obiektów z klas pochodnych za pomocą zmiennej (wskaźnika) z klasy bazowej. Jakie są tego konsekwencje dla dealokacji pamięci w językach, w których jest ona jawna? Czy z alokacją jest podobnie?
Wskazówka: Dealokacja obiektu za pomocą wskaźnika klasy bazowej spowoduje wykonanie destruktora z klasy bazowej, który zapewne nie zajmie się pamięcią zaalokowaną przez obiekt z klasy pochodnej. Rozwiązaniem jest zadeklarowanie w klasie bazowej destruktora wirtualnego (być może pustego). Dzięki temu wywołanie destruktora za pomocą wskaźnika klasy bazowej spowoduje wykonanie destruktora z klasy pochodnej. Ten problem nie występuje przy alokacji, gdyż przy tworzeniu obiektu zawsze podajemy jego typ — a zatem wywoływany jest właściwy konstruktor.
Zadanie 4
Napisz program w Javie, dzięki któremu będzie można zobaczyć, czy i kiedy następuje wywołanie metody finalize. Powinien on dać się uruchomić w różnych wariantach, w których będzie potrzebował mniej lub więcej pamięci.
Zadanie 5
Przyjrzyj się dwom poniższym klasom. Czy taki układ to dobry pomysł?
class Silnik {...};
class Samochód : Silnik {...};
Wskazówka: Dziedziczenie powinno pojawiać się tam, gdzie chcemy zdefiniować coś bardziej wyspecjalizowanego. Klasyczne dziedziczenie odpowiada przecież relacji bycia podtypem, czyli relacji „obiekt z podklasy
jest obiektem z klasy bazowej”. W przykładzie z silnikiem i samochodem ta relacja nie zachodzi: obiekt z klasy Samochód ewidentnie nie jest szczególnym przypadkiem obiektu z klasy Silnik. Powiedzielibyśmy natomiast, że (obiekt z klasy) Samochód
ma (obiekt z klasy) Silnik, a w takim razie napisalibyśmy zapewne tak:
class Samochód {
Silnik s;
...
};
Zadanie 6
Popatrz teraz na takie dwie klasy i porównaj ten przykład z sytuacją z poprzedniego zadania.
class Samochód {...};
class Ciężarówka : Samochód {...};
Wskazówka: Te dwie sytuacje stanowią przejrzystą ilustrację różnicy między dziedziczeniem a enkapsulacją...