przykładowe odpowiedzi do pytań z egzaminu zerowego
Transkrypt
przykładowe odpowiedzi do pytań z egzaminu zerowego
IMIĘ i NAZWISKO: Przykładowe Odpowiedzi NR: 0
EGZAMIN „ZEROWY” (12 CZERWCA 2015) – JĘZYK C++
1. Standard C++14 pozwala na deklarację funkcji w postaci: auto fun();
Skąd wiadomo jaki typ zwraca taka funkcja, co jest potrzebne to tego?
Dedukcja typu odbywa się w oparciu o typ zwracany przez instrukcję return, zatem
konieczna jest definicja tej funkcji, bez której nie można tej funkcji użyć (wywołać).
2. Napisz (czytelnie) jak wyglądają komendy preprocesora, które zwykle stosuje się w plikach
nagłówkowych celem zapobieżenia wielokrotnym włączeniom nagłówka:
#ifndef JAKASFLAGA
#define JAKASFLAGA
// tu cała zawartość
#endif
3. Co znajduje się w poniższej klasie (dokładnie co to jest)?
class Klasa {
static int n; // to jest deklaracja (nie definicja) składowej statycznej typu int
};
4. Mamy poniższy kod (fragment), czego się możemy po nim spodziewać:
while ( ( char litera = cin.get() ) != ‘w’ ) { cout << litera << endl; }
Możemy spodziewać się błędu kompilacji, ponieważ w C++ nie można oddzielać inicjalizacji
lokalnie utworzonego obiektu nawiasami od reszty (chodzi o czerwone nawiasy powyżej).
5. A czego się należy spodziewać po poniższym kodzie (fragment)?
int n = 5;
namespace { int n = 7; }
int main() { int n = 9; cout << ::n << endl; }
Tu jest kolizja nazw… ale, rzeczywiście w tym przykładzie jest to nieco ukryte. To znaczy, gdyby
nie było lokalnego n, oraz byłoby: cout << n << endl; kompilator wyrzuciłby błąd
niejednoznacznego odniesienia się do n. W tym jednak przykładzie, gdy odwołuję się do n poprzez
zapis ::n to odwołuję się do n globalnego (wydrukuje się 5). Gdyby zakomentować linię z int n=5;
to wydrukowałoby się 7. Zatem, w tym konkretnym przykładzie skompiluje się i wydrukuje 5.
6. Które z poniższych są tylko deklaracjami (zaznacz obok litery):
a) const double d = 3.14;
⦁ b) extern int n; // deklaracja
⦁ c) int foo();// deklaracja
⦁ d) enum class Wylicz : int; // deklaracja
7. Jakiego typu jest zmienna k: auto k = 8uL;
Przyrostki u (unsigned) oraz L (long) modyfikują typ stałej całkowitej 8 (int), zatem dedukowany
typ zmiennej k to unsigned long
8. Spośród poniższych wskaź wszystkie niepoprawne składnie:
a) long const& // to samo co: const long&
⦁ b) const long& const // nie ma „stałej referencji”
⦁ c) long& const // tak samo jak wyżej
d) long const && // poprawne choć bez sensu, bo prawa-referencja jest w celu przenoszenia
9. Za pomocą typu std::array zdefiniuj obiekt, który będzie dwuwymiarową (czyli array
zagnieżdżony w array) tablicą o wymiarach 4 x 3:
1
array< <array<int, 3>, 4>
10. Podpisz poniższe przypadki, w jaki sposób przekazywany jest argument do funkcji:
a) void fun( int n ); // przez wartość (czyli kopia argumentu do środka funkcji)
b) void fun( const int& n ); // przez referencję do stałego obiektu int
c) void fun( int&& n ); // przez prawą referencję (z możliwością operacji przeniesienia)
d) void fun( const int* n ); // przez wskaźnik do stałego obiektu typu int
11. W miejsce kropek wpisz „cokolwiek” co sprawi, że definicja funkcji będzie poprawna:
auto fun( int n ) {
if ( n < 0 ) return 5 ; // dowolne coś, co określi zwracany typ
else return fun( n +1 );
}
12. Uzupełnij poniższe w taki sposób, żeby po prawej stronie w miejscu kropek był zapisany typ:
wskaźnik na metodę składową klasy T, która przyjmuje jako argument referencję do stałego
obiektu typu T, a zwraca wskaźnik na obiekt T
using ptr = T* ( T::* )( T const& ) ;
13. Wskaż wśród poniższych te funkcje, które nie mogą równolegle istnieć jako wersje przeciążone:
a) void fun( int ) const;
b) int fun( int );
⦁ c) void fun( int* ); // c) i d) nie mogą współistnieć bo ich wywołanie wygląda tak samo
⦁ d) void fun( int[] ); // tzn. np. nazwa tablicy int (jest też wskaźnikiem do int)
14. Zapisz przykład zdefiniowania uogólnionego wskaźnika na funkcję std::function takiego, który
będzie pokazywał na funkcję: bool compare( const char*, const char* );
function<bool(const char*, const char*)> f = compare; // potrzebny nagłówek <functional>
15. Jeśli obiekt m jest typu std::map<int, double> to jakiego typu jest obiekt k w poniższym zapisie:
const auto& k = m; // dokładnie zapisać typ
const map<int, double>&
16. Jakiego typu jest d w poniższym przykładzie:
int k; int const * const ptr = &k;
auto d = ptr; // podaj typ d
d jest typu const int* (ponieważ modyfikator const wskaźnika jest na najwyższym poziomie
ignorowany w przypadku dedukcji typu przez auto)
17. Jeśli mamy int n = 10; to w których z poniższych linii jest błąd:
⦁ a) decltype(( n )) k; // zapis z podwójnym nawiasem to referencja – musi być zainicjalizowana
⦁ b) int&* k = &n; // nie ma czegoś takiego jak wskaźnik do referencji
c) int k = std::move( n );
⦁ d) int tab[ n ]; // wg standardu (nie kompilatora g++) tablica z rozmiarem wyrażonym nie przez
wielkość stałą lub wyrażenie stałe, jest błędem
18. std::tuple jest typem heterogenicznym, zapisz deklarację funkcji, która zwraca tuplę z typami A, B,
C, a następnie zapisz takie użycie std::tie, dzięki któremu odczytasz dwie pierwsze wielkości, a
trzecią pominiesz (możemy przyjąć, że masz np. lokalne obiekty A a; B b; C c;)
tuple< A, B, C > fun( );
tie( a, b, std::ignore ) = fun( );
2
19. Wymień podstawowe dwa typy hierarchii w relacjach pomiędzy obiektami:
dziedziczenie („jest-czymś”) oraz zawieranie/agregacja („ma-coś”)
20. Mamy klasę Foo, jaką wartość będzie mieć składowa klasy int k na koniec programu:
class Foo { int k { 8 };
public:
void fun( int& k ) { k = 4; }
};
int main() {
Na koniec wartość składowej k klasy Foo
int k { 2 };
będzie mieć wartość taką samą, jak po inicjalizacji
Foo obiekt;
czyli 8, funkcja fun zmienia argument (podany przez
obiekt.fun( k );
referencję) czyli zmienną lokalną k z programu.
}
21. Dlaczego destruktor klasy bazowej zazwyczaj musi być publiczny i wirtualny?
Ponieważ również operacja niszczenia obiektów (utworzonych np. do wskaźnika
bazowego, za pomocą operatora new) musi przebiegać polimorficznie, aby obiekty
typów potomnych zostały poprawnie usunięte.
22. Napisz w miejsce kropek jakąkolwiek definicję konstruktora, prezentującą tzw. delegację
konstruktorów (czyli tworzenie ich w oparciu o już istniejące):
class Foo {
Foo( ) : Foo( 7, ”ala ma asa” ) { }
private:
Foo( int n, const char* s );
};
23. Napisz dla poniższej klasy operator konwersji do typu const char*
class Foo {
string s;
public:
operator const char*( ) const { return s.c_str( ); } // dobrze jeśli metoda jest stała, ale nie musi
};
24. Jeśli mamy const char& ref to zakreśl poniżej na co taka referencja może pokazywać (po prawej
stronie = jest opisowa informacja):
⦁ a) const char& ref = modyfikowalna lewa-wartość
Wszystkie przypadki są poprawne,
⦁ b) const char& ref = modyfikowalna prawa-wartość
lewa-referencja do stałego obiektu
⦁ c) const char& ref = stała lewa-wartość
może pokazywać na wszystko.
⦁ d) const char& ref = stała prawa-wartość
25. Dla klasy Foo napisz deklarację konstruktora przenoszącego:
class Foo { public:
Foo& Foo( Foo&& src );
};
26. Co się nie wygeneruje automatycznie, jeśli będzie deklaracja / definicja konstruktora kopiującego:
Nie wygeneruje się konstruktor domyślny, nie wygeneruje się konstruktor przenoszący.
27. Jeśli będzie w klasie definicja destruktora, to co się automatycznie nie wygeneruje?
Jak należy zapisać, żeby jednak taka automatyczna generacja nastąpiła:
class Foo { public:
~Foo( ) { } // nie wygeneruje się automatycznie konstruktor przenoszący, ale można
3
Foo& Foo( Foo&& src ) = default ; // kompilator poinformować, że chcemy, żeby się generował
};
28. Jeśli dwie klasy, Foo i Bar, chcą wzajemnie się zaprzyjaźnić, to jak zapisać takie deklaracje
przyjaźni?
class Foo; // musi być deklaracja
class Bar { friend class Foo; } // teraz mamy już definicję klasy Bar
class Foo { friend class Bar; } // uzupełniamy definicję klasy Foo
29. Proszę w poniższej klasie napisać definicję przyrostkowego operatora inkrementacji:
class Liczba {
size_t k; // oczywiście kod klasy jest trochę skrócony, zakładamy że jest konstruktor
public: // const na początku opcjonalnie
const Liczba operator++( int ) { Liczba tmp( k ); ++k; return tmp; }
};
30. Co się dzieje podczas prywatnego dziedziczenia z klasy Foo przez klasę Bar (czym się stają)
z częścią
a) prywatną w Foo – jest niedostępna w klasie Bar
b) chronioną w Foo – jest prywatna w Bar
c) publiczną w Foo – jest prywatna w Bar
31. Jaką wartość zobaczymy w programie:
int m { 1 };
cout << "wartosc: " << ( ++ m ) ++ << endl; // wartość 2, czyli 1 po preinkrementacji
32. Napisz deklarację dowolnej prywatnej czysto wirtualnej metody składowej w klasie Foo:
class Foo { // domyślne pole w klasie jest private
virtual void fun( ) = 0;
};
33. Zapisz jak zainicjalizować różnymi wartościami 4-elementową tablicę typów int tworzoną
dynamicznie za pomocą operatora new (napisz przykład):
int* ptr = new int[4] { 1, 2, 3, 4 }; // w { } oczywiście mogą być niekoniecznie stałe
34. W miejscu kropek napisz wyrażenie lambda zwracające obiekt typu initializer_list<int>
taki, żeby potem na ekranie wypisało się 12345
auto ff = [ ]( ) -> initializer_list<int> { return { 1,2,3,4,5 }; } ;
for ( auto n : ff() ) { cout << n; }
35. Napisz co oznacza dla wyrażeń lambda taki zapis (przechwycenie), x to dowolny obiekt:
a) [ = ] // przejmuje wszystko (do wnętrza wyrażenia lambda) przez wartość
b) [ &, x ] // przejmuje wszystko przez referencję, za wyj. x – przekazywanego przez wartość
4