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

Podobne dokumenty