EGZAMIN „Język C++” (termin zerowy) 16 czerwca 2011
Transkrypt
EGZAMIN „Język C++” (termin zerowy) 16 czerwca 2011
Imię i nazwisko:
PYTANIA I ODPOWIEDZI
Nr
0
EGZAMIN „Język C++” (termin zerowy) 16 czerwca 2011
1. Napisz, czym się różni manipulator std::endl w odróżnieniu od stałej znakowej \n
std::endl jest funkcją z parametem - obiekt strumienia. Dodaje do buforu strumienia znak \n
a potem wywołuje metodę flush() – opróżnienie buforu, co gwarantuje zobaczenie jego
zawartości np. na ekranie \n jest tylko stałą znakową, oznaczającą przejście do nowej linii
2. Opisz słownie dwa podane poniżej typy:
int *[3];
tablica trzech wskaźników do int (innymi słowy:
trójelementowa tablica wskaźników do int)
int (*)[3];
wskaźnik do trójelementowej tablicy int-ów
3. Zdefiniuj silny typ wyliczeniowy (C++0x) o nazwie Kolor, zawierający składowe na przykład
o nazwach red, green, blue a następnie utwórz zmienną k typu Kolor, zainicjalizowaną
którąś z wartości: (na przykład)
enum class Kolor { red, green, blue };
Kolor k = Kolor::green;
4. Które z poniższych jest nieprawidłowe (C++0x):
a. const T &&
b. ♦ T & const
c. T const &
d. T &&
Odp. b – nie istnieje „stała referencja” (kwalifikatory cv są dla referencji zabronione).
5. Mamy klasę T. Zdefiniuj obiekt auto_ptr, w którym tworzysz dynamicznie obiekt typu T
(w C++0x auto_ptr jest zastąpione przez unique_ptr, ale składniowo wygląda to tak samo):
#include <memory> // tego nie wymagałem w odpowiedzi, tu tylko dla kompletu
std::auto_ptr< T > obiekt( new T );
6. Mamy obiekt mapy: std::map< int, std::string > m;
Jakiego typ jest zwracany przez metodę: m.begin(); (należy napisad typ w całości):
std::map< int, std::string >::iterator
7. Mamy klasę T. Jakiego typu jest wskaźnik this przekazywany do niestatycznej stałej metody
składowej tej klasy?
Wskaźnik this jest stałym wskaźnikiem, dodatkowo jeśli przekazywany jest do stałej metody,
to jest stałym wskaźnikiem do stałej, formalnie więc: const T * const (albo T const * const).
8. Napisz obok, w jakiej kolejności uruchamiane są konstruktory, jeżeli tworzymy obiekt X:
class Foo {
class Bar { } bar1;
};
class Bar : virtual Foo { };
class X : Foo, virtual Bar { // albo zmienić nazwę na Foo2
::Foo foo1;
};
W powyższy kod wkradł się niezamierzony błąd (książkowy przykład kolizji w dziedziczeniu
wielokrotnym), klasa X nie powinna dziedziczyd w zwykły sposób z Foo. Pominąwszy ten
błąd, kolejnośd konstruktorów jest taka: najpierw Bar (ten zagnieżdżony w Foo), potem Foo,
następnie Bar (ten zewnętrzny) - w ten sposób zbudowane są klasy wirtualnie dziedziczone.
Na koniec konstruktor Foo (obiektu foo1 wewnątrz klasy X) i ostatni - konstruktor X.
„Gdyby” zapisane na Foo na liście dziedziczenia klasy X miało nazwę Foo2, to byłby
konstruktor uruchamiany przed ostatnimi Foo i X. Z powodu błędu pytanie możemy uznad
za niebyłe, chyba że ktoś opisał je blisko poprawności, to dostanie punkt.
9. Napisz dla klasy Foo deklaracje konstruktorów (kopiującego i przenoszącego) i operatorów=
(kopiującego i przenoszącego) tak, żeby zablokowana była możliwośd kopiowania
(i przypisania kopiującego), a działająca była „semantyka przenoszenia” (C++0x):
class Foo {
public:
Foo(); // przydałby się jakiś konstruktor tak w ogóle
Foo( Foo&& );
Foo& operator=( Foo&& );
private:
Foo(const Foo&); // tak naprawdę nie trzeba tego pisad bo w C++0x i tak jest już
Foo& operator( const Foo& ); // niedostępne przez fakt napisania tych w wersji move
};
10. Czy możliwe jest utworzenie tablicy referencji do jakiegoś typu T ? Podając odpowiedź,
wyjaśnij, dlaczego?
Nie – bo referencje same z siebie nie są obiektami. Standard wprost zakazuje tablic
referencji § 8.3.2/5 „There shall be no references to references, no arrays of references,
and no pointers to references.” Tablica musi znad rozmiar typu, z którego jest zrobiona.
11. Jakiego formalnie typu będzie zmienna x, zainicjalizowana jak poniżej (C++0x):
auto x = { 1, 2, 3, 4 };
x jest typu std::initializer_list<int>
12. Opisz słownie dwa podane poniżej typy związane z funkcjami:
int *();
funkcja bezargumentowa zwracająca wskaźnik do int
int (*)(double);
wskaźnik do funkcji przyjmującej parametr double, a zwracającej int
13. Proszę napisad dynamiczną alokację 10-elementowej tablicy wskaźników do funkcji
przyjmującej jako argument int*, a zwracającej double:
Najpierw robimy typedef zwykłego wskaźnika do funkcji, a potem alokujemy tablicę:
typedef double (*PTR)(int*);
PTR *p = new PTR[10]; // pomysłowa próba: new ( double(*)(int*) )*10+ jest błędem
14. W poniższym fragmencie kodu są trzy błędy. Zaznacz (kółeczkiem) wszystkie.
#include ”myheader.h”; // średnik na końcu dyrektywy preprocesora jest błędem
int& MyClass::fun( int a = 7 ) { // zwracanie lokalnego obiektu przez referencję
int b = a*a;
// podawanie domyślnej wartości w definicji funkcji
// (deklaracja była wcześniej, w ciele klasy)
return b;
} // mała uwaga dla kilku osób - średnik na końcu definicji funkcji jest bez sensu
15. Co to jest statyczna asercja (C++0x), jaka jest jej składnia?
std::static_assert( stałe wyrażenie – musi byd sprawdzanle podczas kompilacji, literał
napisowy wyświetlany jeśli wartośd logiczna stałego wyrażenia jest false);
16. Wskaż wśród poniższych fałszywą odpowiedź:
a. namespace można tworzyd w przestrzeni globalnej lub wewnątrz innej namespace
b. definicja namespace może byd rozbita na wiele plików
c. ♦ zmienne zamknięte w nienazwanej przestrzeni nazw są łączone zewnętrznie
d. namespace nie można zdefiniowad wewnątrz funkcji ani klasy
17. Mamy prostą klasę class A { int i; }; Zdefiniuj dla niej, w postaci metody składowej:
operator postinkrementacji (inaczej operator przyrostkowy inkrementacji)
const A A::operator++(int) { A temp(i); ++i; return temp; }
operator preinkrementacji (inaczej operator przedrostkowy inkrementacji)
const A& A::operator() { ++i; return *this; }
18. Krótko napisz jak rozpoznad tak zwaną lewą-wartośd oraz czym jest prawa-wartośd. Jaką
referencją pokazuje się na prawe-wartości w celu realizacji „operacji przenoszenia” (C++0x)?
lewa-wartośd ma nazwę, ma adres, który można użyd. Prawe-wartości to najczęściej obiekty
tymczasowe, np. zwracane przez wartośd z funkcji. Referencja używana w semantyce
przenoszenia to T&& (bez przydomka const, żeby się dało przenieśd).
19. W poniższej definicji klasy są błędy cztery, zaznacz wszystkie kółeczkiem:
class Foo {
friend /* jakiś typ */ fun(); // brak typu zwracanego przez funkcję
static int a; // niektórzy mieli pomysł skreślić static – też ok
double b;
public:: // dwa dwukropki zamiast jednego
Foo( int m, double d ) : a(m), d(b) {} // próba inicjalizacji zmiennej statycznej
// oraz pomylona kolejność: b(d)
}; // brak średnika na końcu definicji klasy
Błędów jest 5 (znowu przez zamianę literek) ale jak ktoś wskaże dowolne 4 to będzie ok
20. Które z poniższych nie można użyd w charakterze tablicy 10-wymiarowej:
a. ♦ new int(10);
b. vector<int> v(10);
c. enum { size = 10 }; int tab[size];
d. int tab[] = { 0,1,2,3,4,5,6,7,8,9 };
Po prostu, new int(10) tworzy jeden egzemplarz obiektu typu int zainicjalizowany
wartością 10, więc nie jest to tablica.
21. Dla takiej linii kodu const int i = 5, *p1 = &i, *const p2 = ptr1; jaki jest typ p1 oraz p2
p1 – wskaźnik na stały int
p2 – stały wskaźnik na stały int
// spekulacje, że kod się nie skompiluje niepotrzebne, bo przecież to jest fragment kodu
22. Krótko opisz, co to jest i do czego służy shared_ptr (C++0x)
Szablon klasy, którego obiekty są tworzone na stosie, ale zawierają zasoby kreowane
dynamicznie. Zasoby te mogą byd współdzielone przez wiele obiektów, a shared_ptr zlicza
te obiekty. Zasób zostaje skasowany dopiero gdy ostatni obiekt posiadający go, przestaje
istnied. shared_ptr jest funkcjonalnie „inteligentnym wskaźnikiem”.
23. Mamy kawałek kodu, strukturę zawierającą obiekt std::string. Napisz definicję operatora
konwersji do typu wskaźnik na stałego obiektu char:
struct Foo {
std::string s; // poniżej napisz operator konwersji
operator const char*() { return s.c_str(); } // w C++0x można poprzedzić explicit
};
24. Wśród poniższych wybierz ten zestaw operatorów, w którym znajduje się chod jeden taki,
którego nie wolno przeciążad:
a. * -> ~ >>=
b. ♦ && .* () =
c. ->* [] , <<
d. new ^ | ==
Wśród wszystkich wyżej wypisanych, nie wolno przeciążać tylko operatora .*
25. Wśród poniższych operatorów wskaż wszystkie, które mają (pisząc ich przeciążone wersje)
tylko jeden argument:
a. ♦ []
b. ()
c. ♦ ->
d. >>
Operator indeksowy [] jest jednoargumentowy. Również operator odniesienia się za
pomocą wskaźnika na składową – wersja przeciążona! – jest jednoargumentowy.
26. Mamy klasę bazową
class Foo {
public: int a;
protected: int b;
private: int c;
};
oraz klasę potomną
class Bar : Foo {};
Proszę napisad, w jakim zakresie ważności w klasie Bar są zmienne a, b, c z klasy Foo.
Ponadto, co należy napisad w klasie Bar, żeby a było w części publicznej?
Przy dziedziczeniu prywatnym (a takim jest – domyślnie – powyżej zapisane) składowe
chronione i publiczne przechodzą do części prywatnej. Czyli a i b są w klasie Bar prywatne.
Częśd prywatna z klasy bazowej Foo (zmienna c) jest w klasie pochodnej niedostępna. Jeśli
chcemy zachowad składową a w części publicznej, trzeba użyd dyrektywy using:
// w części publicznej klasy Bar:
public:
using Foo::a;
27. Proszę napisad dla struct Foo { Bar* fun( Bar const& ); }; wskaźnik na metodę składową
fun oraz zainicjalizowad go:
Bar* (Foo::*ptr)( Bar const& ) = &Foo::fun;
28. Oto kawałek kodu:
void fun( int );
struct Foo {
void fun( double );
void fun( vector<int> );
};
// gdzieś w programie
Foo().fun( 5 );
Co się stanie w wyniku tego wywołania?
Foo() tworzy lokalny obiekt, na rzecz którego wywołana jest metoda składowa fun,
formalnie z argumentem typu int. Następuje poszukiwanie w najbliższym zakresie (struktury
Foo) pasującej nazwy funkcji. Żadna z dwóch nie ma dokładnie pasującego argumentu, ale
konwersja „z promocją” jest możliwa z int na double, więc wywołana będzie metoda
składowa void fun(double);
29. Spośród poniższych wskaż to, które nie jest błędne (w świetle C++0x):
a. int a = { 3.14 };
b. struct Foo { int a; int b; }; Foo f = { 1.2, 1 };
c. struct Foo { explicit Foo(int); }; Foo k = { 1 };
d. ♦ struct Foo { explicit Foo(int); }; Foo k { 1 };
Nie jest błędne tylko ostatnie. a. jest błędne, bo nawiasy { } zapobiegają obcinaniu
typu, liczba 3.14 to double a próbuje się inicjalizować int. b. jest błędem z tego
samego powodu, pierwsza z liczb jest double. c. jest błędne ponieważ konstruktor
jednoargumentowy jest z przydomkiem explicit, więc zapis w postaci „konstruktora
kopiującego” (a po prawej – niejawnego utworzenia obiektu Foo) się – właśnie
niejawnie – nie powiedzie.
30. Jeżeli k to obiekt reprezentujący lewą-wartośd, to co należy (można) zrobid, żeby tej
lewej-wartości użyd w operacji przenoszenia? Napisz kawałek kodu:
std::move( k ); // szablon move „ukrywa” lewą-wartośd i przekazuje ją jako p-wartośd
31. Mamy klasę
class Foo { double d;
public:
Foo( double n ); /* 1 */
Foo( const Foo& s ); /* 2 */
};
Napisz najpierw implementację /* 1 */, a następnie dowolną implementację konstruktora
/* 2 */ z użyciem delegacji innych konstruktorów do jego utworzenia (C++0x):
Na przykład:
Foo::Foo( double n ) : d(n) { }
Foo::Foo( const Foo& s ) : Foo ( s.d ) {}
32. Co oznacza poniższa deklaracja dla funkcji fun() ?
extern ”C” void fun();
Funkcja fun() została napisana/skompilowana w innym języku. Powyższa deklaracja oznacza,
że podczas konsolidacji (linkowania) nie należy szukad wersji „dekorowanej” (uzupełnionej
o nazwy zmiennych itp.) tak jak się dzieje z funkcjami w c++, tylko dokładnie takiej nazwy jak
w deklaracji.
33. Który z poniższych typów jest błędny (nie występuje w C++0x):
a. long long
b. ♦ unsigned float
c. char16_t
d. long double
Typy zmiennoprzecinkowe nie mają w ogóle wersji „bezznakowych”. unsigned
dotyczy wyłącznie typów całkowitych.
34. Mamy klasy:
class Foo {};
class Bar {};
Jaki będzie wynik, (co będzie zawierał obiekt x) poniższego rzutowania?
Bar *b = new Bar;
Foo *x = dynamic_cast< Bar* >( b );
Obiekt x będzie zerowym wskaźnikiem, bo rzutowanie jest na „wskaźnik do”, a klasa
Bar nie pochodzi (nie dziedziczy) z klasy Foo – są to zupełnie niezależne typy.
35. Krótko opisz, na czym polega technika “copy on write”.
Technika polegająca na wykonywaniu płytkiego kopiowania i współdzielenia zasobów aż do
chwili, gdy któryś obiekt zechce (współdzielony) zasób zmodyfikowad. Wtedy dopiero
następuje głębokie kopiowanie – rozdzielenie zasobów. Innymi słowy, głębokie kopiowanie
jest odwleczone w czasie aż do chwili, gdy staje się konieczne.