przykładowe odpowiedzi do pytań z egzaminu pierwszego
Transkrypt
przykładowe odpowiedzi do pytań z egzaminu pierwszego
IMIĘ i NAZWISKO: Przykładowe Odpowiedzi NR: 0 EGZAMIN 1 (22 CZERWCA 2015) – JĘZYK C++ 1. Do czego służy nienazwana przestrzeń nazw? Do łączenia wewnętrznego zawartej w niej elementów, czyli zabezpiecza przed kolizja nazw pomiędzy różnymi plikami (nazwy z tej przestrzeni widziane tylko w ramach danego pliku), jest to metoda polecana zamiast stosowania przydomka static dla obiektów globalnych. 2. Napisz rzutowanie (w stylu C++) wskaźnika int const *ptr; tak, żeby był po tym rzutowaniu stałym wskaźnikiem na int: const_cast< int * const > ( ptr ); 3. Napisz poprawne usuwanie obiektów alokowanych na stercie: int *ptr1 = new int(4); auto *ptr2 = new double[4]; delete ptr1; delete [ ] ptr2; 4. Napisz trzy przykłady pętli po całym wektorze vector<int> v; // przykładowo: for ( auto n : v ) … // taka sama forma pętli tylko np. z referencją to nie jest odrębny przykład for ( int i=0; i<v.size( ); ++i ) … for ( auto it=v.begin( ); it!=v.end( ); ++it ) … 5. Jeśli mamy int k; to jakiego typu jest m takie, że: decltype( auto ) m = k; // m jest typu int 6. Jeśli klasa Foo posiada następującą metodę składową: int fun( ) const; to jakiego dokładnie typu jest wskaźnik this przekazywany tej metodzie? this jest stałym wskaźnikiem, dodatkowo jeśli jest przekazywany do stałej metody, to staje się stałym wskaźnikiem na stałą, czyli: const Foo * const (to samo: Foo const * const). 7. Na czym polega idiom programowania „copy on write” ? Na współdzieleniu zasobów i odwlekaniu ich powielania (kopiowania) aż do momentu gdy któryś z obiektów chce dany (współdzielony) zasób zmodyfikować. Idiom wymaga prowadzenia „licznikia” obiektów obserwujących dany zasób. 8. W jaki sposób działa mechanizm kompilatora „copy elision” ? Optymalizacja polegająca na ominięciu niektórych (formalnie nadal wymaganych) etapów przekazywania obiektu, np. pominięcie konstruktora kopiującego i utworzenie obiektu za pomoca konstruktora bezpośrednio w docelowym miejscu (przy zwracaniu z funkcji obiektu przez wartość). 1 9. Napisz wskaźnik na składową klasy Foo typu int, zainicjalizuj go ustawiając na którąś składową: class Foo { public: int a, b, c; }; int Foo::*ptr = &Foo::c; 10. Mamy tablicę double tab[10]; Jak w kodzie obliczysz za pomocą operatora sizeof jej wielkość: Ponieważ pytanie można było zrozumieć na dwa sposoby, uznaję zarówno prostą odpowiedź sizeof( tab ) jak i tą, którą miałem na myśli, czyli sizeof( tab ) / sizeof ( double ) 11. Napisz przeciążoną definicję operatora dodawania + jako metody składowej klasy Foo: class Foo { unsigned k; public: Foo( unsigned n ) : k(n) { } Foo operator+ ( const Foo& p ) { return Foo( k + p.k ); } }; 12. Ile razy w poniższym przykładzie kodu zostanie wywołany (dla klasy Foo) a) konstruktor 3 razy (obiekt statyczny k, obiekt lokalny m, obiekt na stercie do wskaźnika p) b) destruktor 2 razy (obiekt na stercie nie jest usuwany) class Foo { static Foo k; }; Foo Foo::k; int main( ) { Foo m; auto p = new Foo; } 13. Proszę napisać przykładowe użycia operatora new w wersji „placement new”: new (0x400) Foo; // chodzi o składnię z nawiasem ( ) w którym podaje się adres docelowy, // w którym zostanie wywołany konstuktor danego typu 14. Wskaż te linie, które dadzą błąd kompilacji. class Foo { public: explicit Foo( initializer_list<int> a ) { } }; ⦁ Foo f1(5); // brak konstruktora z int Foo f2{5}; ⦁ Foo f3 = 5; // brak konstruktora z int ⦁ Foo f4 = {5}; // nie można wywołać niejawnie konstruktora, bo explicit 15. Który konstruktor zostanie wywołany do budowy obiektu f ? class Foo { public: Foo( unsigned ); Foo( short ); ⦁ Foo( initializer_list<int> ); }; Foo f { 5 }; // zapis { } preferuje konstruktor z argumentem initializer_list 2 16. Co zobaczymy na ekranie dla poniższego obiektu tab: auto tab { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // powstaje obiekt tab typu initializer_list<int> cout << tab.begin( ) << " " << tab.end( ) << endl; Zobaczymy jakiś adres początku tab i adres za ostatnim elementem tab. 17. Jakiego typu będzie k : auto k = new auto ( 3.14 ); double* (3.14 jest typu double, zatem new double zwraca wskaźnik do double) 18. Napisz „ekwiwalent” poniższego obiektu funkcyjnego klasy Foo, w postaci wyrażenia lambda. class Foo { int k; public: Foo(int n): k(n) { } void operator()(int m) { } }; [ k ] ( int m ) { } 19. Napisz definicję wskaźnika na metodę składową klasy T, która przyjmuje jako argument referencję do wskaźnika na typ char, a zwraca modyfikowalną prawą referencję do typu string: string&& (T::*ptr)( char*& ) 20. Mamy taką definicję funkcji, co w tej funkcji jest złego? int& fun( int k ) { return k; } Funkcja zwraca referencję do lokalnego obiektu, który zaraz przestanie istnieć (co doprowadza to tzw. „wiszącej referencji”). Podobnie zresztą byłoby ze wskaźnikiem – nie zwracamy lokalnych obiektów przez referencję ani wskaźnik. 21. W jakiej kolejności w kodzie będą wywołane destruktory, podczas likwidacji obiektu C c; class A { }; class B { A* a; }; class C : virtual A, virtual B { }; W kolejności odwrotnej do konstruktorów, czyli: C B A 22. Napisz w klasie Bar, w jaki sposób „odziedziczyć” z klasy Foo konstruktor: class Foo { public: Foo( int n ); }; class Bar : public Foo { public: using Foo::Foo; }; 23. Napisz dla poniższej klasy operator konwersji do typu std::string class Foo { size_t num; public: operator string( ) const { to_string( num ); } // const opcjonalnie }; 3 24. Jeśli mamy char&& ref to zakreśl poniżej na co taka referencja może pokazywać (po prawej stronie przypisania jest opisowa informacja): a) char&& ref = modyfikowalna lewa-wartość ⦁ b) char&& ref = modyfikowalna prawa-wartość c) char&& ref = stała lewa-wartość d) char&& ref = stała prawa-wartość 25. W jakiej kolejności wywołane będą konstruktory, jeśli stworzymy obekt C c; w poniższym kodzie: class A { }; class B : virtual public A { }; class C : public A, virtual public B { }; Najpierw konstruowane są klasy dziedziczone wirtualnie, czyli A, B, potem w ramach budowania obiektu końcowego znowu obiekt A (dziedziczony niewirtualnie, co oczywiście doprowadza do niejednoznaczności w dostępie do A), na koniec C. Zatem kolejność: A B A C 26. Co to jest i do czego służy back_inserter Jest to formalnie szablon funkcji, zwracający iterator „back_inserter_iterator” służący do wstawiania do danego kontenera elementu na jego końcu. p.s. punktowałem również odpowiedzi dość niekompletne, zwłaszcza brakowało wyjaśnień „co to jest”. 27. Napisz jak posortować con, będący obiektem listy, list<string> con; według kryterium od największego do najmniejszego: con.sort( greater< string> ( ) ); 28. Co dokładnie oznacza zaznaczony w poniższej klasie fragment kodu? class Klasa { // … friend void fun( ) { } }; Deklaracja przyjaźni względem funkcji globalnej, zdefiniowanej w miejscu tejże deklaracji. Pisanie, że fun nie ma argumentów i nic nie zwraca, nie było istotne (reszta – i owszem). 29. Proszę w poniższej klasie napisać definicję kopiującego operatora przypisania: class Liczba { size_t k; public: Liczba& operator=( const Liczba& s ) { if ( this != &s ) k = s.k; return *this; } }; 30. Klasa Bar publicznie dziedziczy z klasy Foo, co się dzieje (w klasie Bar) z częściami: a) prywatną w Foo: niedostępna w Bar (ale jak najbardziej dziedziczona!) b) chronioną w Foo: chroniona w Bar c) publiczną w Foo: publiczna w Bar 4 31. Jakiego typu iteratory obsługują następujące kontenery: a) vector, deque: iterator swobodnego dostępu (random access iterator) b) list: iterator dwukierunkowy (bidirectional iterator) c) stack: ten kontener nie posiada iteratorów 32. Mamy obiekt std::map< std::string, size_t > m; Proszę napisać przynajmniej dwa sposoby, na jakie można dodać kolejne elementy do tego obiektu. // przykładowo: m[ "ala" ] = 5; m.insert( pair<string, size_t>( "ola", 6 ) ); 33. Napisz w jakim nagłówku w biblitece standardowej znajdują się funktory, oraz napisz po jednym przykładzie dla obiektu funkcyjnego reprezentującego działania: arytmetyczne, relacyjne, logiczne: #include <functional> oraz do wyboru, arytmetyczne – plus<T>, minus<T>, multiplies<T>, divides<T>, modulus<T>, negate<T> relacyjne – equal_to<T>, not_equal_to<T>, greater<T>, greater_equal<T>, less<T>, less_equal<T>, logiczne – ligical_and<T>, logical_or<T>, logical_not<T> 34. Jakie formalne wymagania mają negatory (std::not1 oraz std::not2) względem funktorów, na których mają działać? Negatory wymagają zachowania konwencji nazewniczej w obiektach funkcyjnych, na których działają, czyli typ zwracany result_type, argument argument_type lub first_argument_type i second_argument_type. Można to osiągnąć np. dziedzicząc z szablonów unary_function lub binary_function 35. Mamy klasę Klasa, z publiczną metodą składową call( ) oraz obiekt tab, jak poniżej w kodzie. Napisz przykład użycia algorytmu for_each takiego, że przechodzi po tab oraz, za pomocą std::bind, wywołuje metodę call (jako obiekt funkcyjny): class Klasa { int k; public: Klasa(int n) : k(n) { } void call( ) { cout << k ; } }; // w programie: array< Klasa, 10 > tab { 1,2,3,4,5,6,7,8,9,10 }; for_each( tab.begin( ), tab.end( ), bind( &Klasa::call, _1 ) ); 5