Imię i nazwisko: PYTANIA I ODPOWIEDZI Nr 0 EGZAMIN Język C++ 27 czerwca 2011 1. Mamy obiekt w, który jest typu std::vector<double>. Posortuj obiekt w według kryterium większy niż to znaczy, napisz linijkę z odpowiednim algorytmem i predykatem, który posortuje całą zawartośd tego wektora według kryterium większy niż ( > ). std::sort( w.begin(), w.end(), std::greater<double>() ); 2. Poniższą strukturę, reprezentującą obiekt funkcyjny, proszę zapisad w postaci wyrażenia lambda (C++0x), wykonującego to samo: struct Funktor { T1 operator()( T1 const& a, T2 b ) { return fun(a, b); [] ( T1 const& a, T2 b ) { return fun(a, b); 3. Proszę napisad, jakiego typu będzie zmienna x, y, z, jeśli (C++0x): auto x = new auto ( 1 ); // dedukcja z prawej strony daje: new int(1); std::vector<char> v; for ( auto y : v ) { auto& z = v; x jest typu int* y jest typu char z jest typu std::vector<char>& 4. Mamy fragment kodu (C++0x), jak niżej. Co praktycznie oznacza w niej zapis = default class Foo { public: Foo() = default; Foo( Foo const& ); // Zapis ten oznacza, że pomimo zdeklarowanego jawnie konstruktora kopiującego chcemy, aby w razie potrzeby, został wygenerowany niejawny domyślny konstruktor. 5. Proszę wymienid podstawowe cechy iteratora dwukierunkowego (biderctional). Jakie znane ci kontenery są obsługiwane przez taki typ iteratorów? Iterator ten dziedziczy z operatorów wejściowego, wyjściowego oraz do przodu, zatem można nim czytad i zapisywad. Można poruszad się do przodu i do tyłu o jeden, stosowany w algorytmach wieloprzejściowych. Kontenery, które go używają (wymieniam tylko te, o których mowa była na wykładzie) to list, set, multiset, map, multimap.
6. W klasie Base mamy konstruktor (jak niżej), uzupełnij w klasie Derived kod tak, żeby skorzystad z dziedziczenia (C++0x) konstruktorów: class Base { public: Base (int n ); class Derived { public: using Base::Base; 7. Mamy cztery klasy (C++0x): class A { public: explicit A(); virtual ~A(); class C { public: ~C(); class B { public: B(); B(B const&); class D { public: D(); Wymieo wszystkie, w których nie zostanie (w razie potrzeby) automatycznie wygenerowany konstruktor przenoszący: Niejawny move constructor nie zostanie wygenerowany w klasie A, B, C ponieważ warunkiem generowania jest niezdefiniowanie destruktora, konstruktora kopiującego (a także jakichkolwiek operatorów przypisania). Zatem pozostaje jedynie klasa D, w której może byd taki konstruktor przenoszący wygenerowany. 8. Napisz składnię, tworzącą do wskaźnika dynamicznie tablicę 4-elementową i zainicjalizuj ją (według składni jednolitej inicjalizacji, C++0x) dowolnymi wartościami: int* ptr = new int[4] { 1,3,5,7 // wartości inicjujące przykładowe, byle były int 9. Klasa Foo posiada publiczny konstruktor domyślny, konstruktor kopiujący, kopiujący operator przypisania i destruktor. Uważnie policz i napisz, ile razy każda z tych funkcji będzie wywołana w programie? int main() { Foo a1; // tu działa konstruktor Foo a2 = a1; // tu działa konstruktor kopiujący Foo *pa = new Foo; // tu działa konstruktor *pa = a1 = a2; // tu dwa razy działa operator= // na koniec niszczone są tylko obiekty ze stosu, a1 i a2, czyli dwa razy destruktor konstruktor 2 konstruktor kopiujący 1 operator= 2 destruktor 2
10. Mamy następujące klasy oraz fragment programu: class A { class B : virtual public A { class C : virtual public A { class D : public B, public C { static D d1; D D::d1 = 0; int main() { D d; Proszę napisad w jakiej kolejności będą uruchamiane konstruktory podczas wykonania powyższego kodu: W powyższym kodzie tworzone są dwa obiekty typu D jeden statyczny i jeden lokalny w funkcji main. Kolejnośd konstruktorów jest, począwszy od klasy wirtualnie dziedziczonej: A B C D A B C D (dwie serie, bo dwa obiekty). OK, jak ktoś napisze tylko A B C D to też punkt. 11. Proszę kółeczkami zaznaczyd trzy błędy w poniższym fragmencie kodu: static struct A { a1; class B :: A { // podwójny dwukropek to błąd static class C { c1; // brak średnika na koniec definicji klasy B::C B::c1; // brak kwalifikatora zakresu gdzie jest obiekt c1 12. Najczęściej w programie piszę się deklarację użycia przestrzeni nazw std. A jak należy napisad, jeśli tylko chciałbym udostępnid wybrane obiekty, a konkretnie cout i endl? using std::cout; using std::endl; 13. Statyczna zmienna globalna jest łączona wewnętrznie, widzialna tylko w zakresie jednego pliku, co zapobiega kolizjom nazw. W jaki inny sposób można osiągnąd ten sam efekt? Inny, bardziej polecany sposób, to zamknąd taką zmienną w nienazwanej przestrzeni nazw: namespace { /* tu nasza zmienna */ 14. Jaką wartośd otrzymamy z takiego wyrażenia (operator & ma wyższy priorytet niż ^ ): 1 & 2 ^ 3 // najpierw warto sobie zapisać te liczby w postaci binarnej, przynajmniej // dwa ostatnie bity: 1 = 01, 2 = 10, 3 = 11 1 & 2 to koniunkcja bitowa pomiędzy 01 i 10, co daje 00 operator ^ to różnica wykluczająca (xor) pomiędzy 00 i 11, co daje wynik 11, czyli dziesiętnie wynik to 3 15. Wśród poniższych wskaż błędne: a. T * & b. T & * // nie istnieje wskaźnik do referencji c. T const * const d. const T **
16. Mamy poniżej cztery deklaracje funkcji. Proszę zaznaczyd wszystkie, dla których, w dowolny sposób, można użyd jako argumentu tablicę int tab[3]; a. void fun1( int* p ); b. void fun2( int p[] ); c. void fun3( int p[][3] ); d. void fun4( int (&)[3] ); Wszystkie funkcje pozwalają na użycie tablicy tab jako argumentu, choć fun3 wymaga przedstawienia go w postaci adresu ( wskaźnika do ): fun1(tab), fun2(tab), fun3(&tab), fun4(tab). 17. Jaka wartośd zostanie wypisana w wyniku działania poniższego kodu: int f( int, int = 3 ); int f( int = 2, int ); int main() { int f( int, int = 4 ); // w podbloku poprzednie domyślne wartości tracą ważność cout << f(3) << endl; // to wywołanie jest tożsame z f(3,4) int f( int a, int b ) { return a+b; Wynik to oczywiście 7 18. Mamy następujące dwie funkcje: const char* get1( char& ); const char* get2( char& ); Proszę zdefiniowad 2-elementową tablicę zawierającą wskaźniki na takie funkcje oraz zainicjalizowad ją powyższymi funkcjami: const char* ( *tablica[2] )( char& ) = { get1, get2 19. Poniżej wypisano deklaracje globalnych funkcji. Wskaż wszystkie, które są błędne: a. void fun( int ) const; // const niedopuszczalny w funkcjach globalnych b. extern static void fun( int& ); // extern i static wzajemnie się wykluczają c. void fun( void(*)() ); d. int main(); 20. Dla klasy Foo napisz klasyczną definicję kopiującego operatora przypisania, chodzi o części stale w nim występujące, nie o szczegóły konkretnej implementacji: Foo& Foo::operator=( const Foo& s ) { if ( &s!= this ) { // tu coś zrób return *this;
21. Oto interesujący nas fragment klasy: class Foo { int* ptr; // tu będzie pojedynczy zasób na stercie float *tabptr; // tu będzie dynamicznie zrobiona tablica unique_ptr<int> uptr( new int ); // uptr jako obiekt na stosie, sam się poprawnie usunie // Napisz jak powinien wyglądad destruktor: Foo::~Foo() { delete ptr; delete [] tabptr; 22. Oto fragment klasy: class Foo { std::string s; Proszę w tej klasie napisad operator konwersji na typ int (jako metodę stałą), tak żeby zwracana była długośd (wielkośd) obiektu s : Foo::operator int() const { return s.size(); 23. W klasie są następujące konstruktory (C++0x): Foo( std::initializer_list<int> ); /* 1 */ Foo( std::initializer_list<short> ); /* 2 */ Foo( int, int = 0 ); /* 3 */ Foo( short = 0, short = 0 ); /* 4 */ Który będzie użyty w następujących wywołaniach: Foo(); // będzie użyty 4-ty Foo ( 0 ); // będzie użyty 3-ci Foo { 0 // będzie użyty 1-wszy 24. Proszę wymienid te z operatorów, które w wersji przeciążonej mogą byd napisane tylko jako metody składowe klasy: = () [] ->
25. Mamy prostą klasę (do samej klasy już nic nie dopisujemy): class Foo { int a, b; public: int const& geta() const { return a; int const& getb() const { return b; Proszę zdefiniowad (przeciążyd) operator<< tak, żeby możliwe było wysyłanie obiektu klasy Foo do strumienia standardowego: Na przykład tak: std::ostream& operator<< ( std::ostream& o, const Foo& s ) { return o << s.geta() <<, << s.getb(); 26. Dla klasy: class A { int n; proszę zdefiniowad (przeciążyd) jako metody składowe tej klasy, operatory & (wyłuskania adresu) oraz! (negacji logicznej): A* A::operator& () { return this; bool A::operator! () { return!n; 27. Mamy klasy dziedziczące z siebie, proszę kółkiem zakreślid, w jakim polu dostępu są kolejne zmienne w najniżej położonej klasie C: class A { int a; protected: int b; public: int c; class B : protected A { public: using A::b; class C : B { public: using B::c; W klasie C zmienna: a jest niedostępna / prywatna / chroniona / publiczna b jest niedostępna / prywatna / chroniona / publiczna c jest niedostępna / prywatna / chroniona / publiczna
28. Mamy wskaźnik: int const *ptr1 = new int(7); Proszę napisad poprawne rzutowanie wskaźnika ptr1 na inny, poniższy wskaźnik ptr2: int * const ptr2 = const_cast< int * const > ( ptr1 ); 29. Co oznacza taki zapis dla klasy Foo: virtual ~Foo() = 0; Co powiemy o własnościach klasy Foo? Co i gdzie trzeba tak naprawdę jeszcze napisad? Klasa Foo ma czysto wirtualny destruktor, zatem jest klasą abstrakcyjną, której obiektu nie da się utworzyd. A jednak, żeby klasa w ogóle miała sens (żeby możliwe było np. dziedziczenie z niej i tworzenie obiektów potomnych), musimy, poza ciałem klasy, zdefiniowad destruktor chodby jako pustą funkcję: Foo::~Foo() { 30. Mamy dwie klasy (fragment kodu): class A { public: int fun(int); /* 1 */ - ta metoda nie jest jeszcze wirtualna! virtual int fun(double); /*2 */ class B : public A { public: virtual fun(int); /* 3 */ int fun(double); /* 4 */ Które z metod będą wywołane w poniższym fragmencie programu: A *a1 = new B; A &a2 = *a1; // przepraszam tu był błąd edycyjny, oczywiście a1 nie ptr A a3 = a2; a2.fun(1); // która metoda /* 1 */ bo w klasie A nie jest wirtualna a3.fun(1.1); // która metoda / * 2 */ bo obiekt a3 jest przez wartośd a więc przycięty (*a1).fun(1.1); // która metoda /* 4 */ zgodnie z polimorficznym wywołaniem 31. Napisz zawartośd klasy Foo tak, żeby jej obiektów nie dało się utworzyd na stercie, a dało się utworzyd tylko na stosie: class Foo { // po prostu trzeba zablokować operacje new i delete, czyli na przykład: private: void* operator new ( size_t ); void operator delete ( void* );
32. Mamy zaalokowany dynamicznie int oraz, przez operator dereferencji (wyłuskania) przypisany do referencji do inta: int &ref = * new int; Po lewej stronie mamy referencję do int. Po prawej działa operator new, ale ten operator zwraca adres do obiektu, więc konieczne jest jeszcze wyłuskanie (operator *). Nie jest konieczne, ale dla większej czytelności można zapisać: * ( new int ); Jak powinna wyglądad instrukcja usuwająca ten zasób: delete &ref; Proszę zwrócid uwagę, że powyższy zapis oznacza wyłuskanie adresu obiektu ref, który przechowywał obiekt wykreowany na stercie. Operator delete wymaga jako argumentu adresu, więc sam ref (który adresem nie był) byłoby błędne. 33. Krótko opisz na czym polega idea niewirtualnego interfejsu (non-virtual interface). Niewirtualny interfejs to taki, który posiada wyłącznie metody niewirtualne za wyjątkiem destruktra (który jest wirtualny, ewentualnie czysto wirtualny). Metody wirtualne są wtedy deklarowane w częściach private lub protected i wywoływane przez metody interfejsu (publiczne, niewirtualne). Taki interfejs jest bardziej stabilny i odzwierciedla obiektową zasadę, że to co się zmienia powinno byd ukrywane (hermetyzacja danych). Metody wirtualne w zasadzie prezentują zmienną częśd kodu poprzez polimorficzne wywołania. 34. Mamy klasę, która deklaruje przyjaźo do pewnej funkcji: class Foo { int a; friend void fun(); Co możesz napisad o funkcji fun? Czy funkcja ta będzie potrafiła np. wypisad wartośd zwykłego prywatnego składnika klasy Foo, takiego jak a? Klasa Foo deklaruje przyjaźo z globalną funkcją fun(). Zatem funkcja fun() teoretycznie ma dostęp do prywatnych części klasy Foo. Czy jednak ma dostęp np. do składnika int a;? Praktycznie niestety nie ma, bo zaprzyjaźnionym funkcjom nie jest przekazywany wskaźnik this, zatem nie mogą one, bez otrzymania jako jawny argument, pracowad na konkretnym obiekcie klasy Foo czyli funkcja fun() w postaci jak wyżej, nie będzie w stanie działad na niestatycznych składowych klasy. 35. Wśród poniższych porównao, działających w oparciu o mechanizm RTTI, wskaż te, które dają wynik logiczny true (prawda). Pomocniczo korzystamy z tablicy: int tab[10]; a. typeid( new int * ) == typeid ( int** ); b. typeid( tab ) == typeid( const int* ); c. typeid( std::string ) == typeid( alfa ); d. typeid( int*& ) == typeid( &tab );