Imię i nazwisko:                                                                                                              13 września 2003

 

Uwaga: W poniższym teście w każdym punkcie ilość poprawnych odpowiedzi wynosi od 0 do 4. Każdą z nich należy oznaczyć symbolem „+”, a odpowiedź błędną – symbolem „­–”.

 

1.      Asercje zaleca się stosować m.in. do

a)      sprawdzania, czy operator new faktycznie przydzielił potrzebną nam pamięć.

b)      sprawdzania, czy użytkownik podał dane liczbowe w wymaganym przez nas zakresie.

c)      sprawdzania, czy indeks tablicy nie wykracz poza dopuszczalny zakres.

d)      sprawdzania, czy po wywołaniu metody klasy wciąż zachowany jest jej niezmiennik.

 

2.      Załóżmy, że początek definicji klasy X wygląda  następująco:
class X : public Y
{

int _z;

std::vector<int> _wektor;

   public:

      X(int z) : _wektor(_z), _z(z) {_wektor.push_back(z);}

...

};

wynika stąd, że:

a)      konstruktor obiektu _wektor zostanie wywołany przed domyślnym konstruktorem klasy bazowej Y;

b)      wartość z zostanie nadana składowej _z przed wywołaniem konstruktora składowej _wektor;

c)      instrukcja _wektor.push_back(z) zostanie wywołana po wywołaniu konstruktora składowej _wektor;

d)      w destruktorze obiektów klasy X nie można odwoływać się do składowej _wektor, gdyż składowe ulegają automatycznemu zniszczeniu tuż przed wywołaniem ciała destruktora

 

3.      Załóżmy, że początek definicji klasy X wygląda  następująco:
class X
{

std::vector<int> _wektor;

   public:

static int z;

static int f() {...}
...

};

wynika stąd, że:

a)      jeżeli utworzę dwa obiekty klasy X i w jednym z nich nadam wartość 10 składowej z, to drugi obiekt automatycznie uzyska informację, że jego składowa z też ma wartość 10.

b)      jeżeli utworzę dwa obiekty klasy X i w jednym z nich nadam wartość 10 zmiennej _wektor[0], to drugi obiekt automatycznie uzyska informację, że jego zmienna _wektor[0] też  ma wartość 10.

c)      w (wykropkowanej) definicji funkcji składowej f nie można używać wskaźnika this.

d)      funkcja składowa f nie ma dostępu do składowej _wektor.

 

4.      Wartość wyrażenia (6&3) + (6|3) + (6^3) + (6%3) + 0x36

a)      wynosi 60

b)      wynosi 62

c)      wynosi 64

d)      wynosi 66

 

5.      rozpatrzmy instrukcję if (x == y == 0) x = y = 1;

a)      jeśli początkowo x i y są równe 0, to po jej wykonaniu będą równe 1;

b)      jeśli początkowo x i y są równe 2, to po jej wykonaniu będą równe 1;

c)      jeśli początkowo x jest równe 1, a y równe jest 2, to po jej wykonaniu x i y będą równe 1;

d)      instrukcja x = y = 1; wykona się wtedy i tylko wtedy, gdy początkowo x i y są jednocześnie równe 0.

 


6.      Rozpatrzmy następującą deklarację:
       double f(double a, double *b, double & c);
w ramach deklaracji
       double x, y, z, tab[10];
poprawne jest następujące wywołanie funkcji f:

a)      f(x, *y, &z);

b)      f(x, &y, *z);

c)      f(x, &x,  x);

d)      f(x, tab, tab[0]);

7.      Rozpatrzmy następującą makrodefinicję:
        #define max(x,y) ( ((x) > (y)) ? (x) : (y) )
Po wykonaniu ciągu instrukcji
       int x = 0, y =1;
   int z = max(x++, y++);

a)      z ma wartość 2;

b)      z ma wartość 3;

c)      y ma wartość 2;

d)      y ma wartość 3;

Uwaga: w pierwszej fazie działania operatora ?: wyznaczana jest wartość testu wraz z wszelkimi efektami ubocznymi.

 

8.      Rozpatrzmy następującą funkcję:
       inline int max(int x, int y) {return (x > y) ? x : y;}
Po wykonaniu ciągu instrukcji
       int x = 0, y =1;
   int z = max(x++, y++);

a)      z ma wartość 2;

b)      z ma wartość 3;

c)      y ma wartość 2;

d)      y ma wartość 3;

9.      Rozpatrzmy następujący szablon funkcji:
template <typename T>      
inline T const& max(T const& x, T const& y) {return (x > y) ? x : y;}
Po wykonaniu ciągu instrukcji
       int x = 0, y =1;
   int z = max(x++, y++);

a)      z ma wartość 2;

b)      z ma wartość 3;

c)      y ma wartość 2;

d)      jeżeli w definicji szablonu pominęlibyśmy modyfikatory const, to instrukcja max(x++, y++); byłaby błędem składniowym, bo implikowałby użycie referencji do obiektów tymczasowych.

 

10.   Rozpatrzmy następującą deklarację:
int X::size(X& x) const;
Użyte w niej słówko const

a)      modyfikuje typ wartości zwracanej przez funkcję

b)      modyfikuje typ wskaźnika this (używanego w ciele funkcji X::size(X& x) const)

c)      modyfikuje typ argumentu funkcji, tj. x

d)      jest błędem składniowym.

 

11.   Linker to osobny program służący głównie do

a)      optymalizacji hierarchii dziedziczenia (zależności między klasami itp.)

b)      optymalizacji czasu działania programu (i być może wielkości kodu wykonywalnego)

c)      wyszukiwania w programie błędów składniowych

d)      łączenia skompilowanych plików źródłowych z bibliotekami w celu utworzenia pliku wykonywalnego

 


12.   rozpatrzmy instrukcje
       char* a[200];
   char* (*b)[200];
  char* (*c)();
   char* (*d[200])();

a)      a jest tablicą 200 napisów w stylu języka C

b)      poprawna jest instrukcja b = &a;

c)      c jest wskaźnikiem na funkcję

d)      poprawna jest instrukcja d[0] = c;

 

13.   Zamierzamy wykorzystać tablicę obiektów wyprowadzonych przez dziedziczenie z abstrakcyjnej klasy X zawierającej deklaracje interesujących nas metod wirtualnych. Możemy posłużyć się następującą definicją:

a)      std::vector<X> tablica;

b)      std::vector<X*> tablica;

c)      X* tablica = new X [n];

d)      X tablica[100];

 

14.   Następująca instrukcja sortuje 100 pierwszych elementów tablicy int tab[1000]:

a)      std::sort( tab[0], tab[100] );

b)      std::sort( &tab[0], &tab[100] );

c)      std::sort( tab.begin(), tab.begin() + 100 );

d)      std::sort( tab, tab + 100 );

 

15.   Chcemy otworzyć plik, którego nazwa zawierać będzie wartości zmiennych całkowitych n i k; np. dla n == 10 i k == 7 plik ma mieć nazwę "n10_k7.dat". W tym celu możemy posłużyć się następującymi instrukcjami:
      
#include <sstream>
    #include <fstream>
   ...
    std::ostringstream ostr;
   ostr << "n" << n << "_k" << k << ".dat";

a sam plik otwieramy instrukcją

a)      std::ofstream F(ostr);

b)     std::ofstream F(ostr.str());

c)      std::ofstream F(ostr.c_str());

d)     std::ofstream F(ostr.str().c_str());

 

16.   Rozpatrzmy następujący kod:
template <typename In, typename Out>
Out copy (In first, In last, Out result)
{
   while (first != last)
      *result++ = *first++;
   return result;
}

a)      Słówko In jest zdefiniowane w innej części programu (np. przez typedef lub #define).

b)     Słówko typename można by tu zastąpić słowem class

c)      Pierwszy argument funkcji copy jest iteratorem przekazywanym przez wartość.

d)      W ramach deklaracji
            
char tab[] = "ciekawostka programistyczna";
poprawna jest instrukcja
            
cout << copy (tab, tab+10, tab+10);


17.   Jacek zauważył, że po zadeklarowaniu w funkcji main() tablicy 1 000 000 liczb typu int: 
     
int tab[1000000];
jego program przestał działać. Problem ten można obejść:

a)      przesuwając definicję tablicy do zakresu globalnego;

b)      alokując tę tablicę przy pomocy operatora new;

c)      posługując się standardowym wektorem (std::vector<int>);

d)      zamieniając tablicę jednowymiarową na tablicę dwuwymiarową (int tab[1000][1000]);

18.    

a)      Jeżeli wewnątrz pliku *.cpp umieszczę definicję funkcji typu inline, to będę mógł z niej korzystać tylko w tym pliku.

b)      Jeżeli definicję funkcji typu inline umieszczę wewnątrz pliku nagłówkowego, to będę mógł z niej korzystać w każdym pliku włączającym dany plik nagłówkowy.

c)      Jeżeli definicję zwykłej funkcji (nie inline) umieszczę wewnątrz pliku nagłówkowego, to prawdopodobnie program się nie skompiluje.

d)      Jeżeli w kilku różnych plikach źródłowych zdefiniuje się obiekty globalne, to nie wiadomo, w jakiej kolejności będą one konstruowane.

19.   Można się spodziewać, że implementacja następującej kolekcji może być oparta jest na drzewie czerwono-czarnym:

a)      std::vector

b)      std::list

c)      std::map

d)      std::stack

20.   Rozpatrzmy następujący kod, w którym typ wartości stałej metody This zastąpiono słowem RESULT:
class Foo
{
public:
   RESULT This() const { return this;}
};

...
Foo foo;
std::cout << (&foo – foo.This()) << ’\n’;
Prawdziwe są następujące zdania:

a)      jeżeli RESULT zastąpimy słowem Foo, to kompilator zgłosi błąd składniowy;

b)     jeżeli RESULT zastąpimy wyrażeniem Foo*,  to kompilator zgłosi błąd składniowy

c)      jeżeli RESULT zastąpimy wyrażeniem Foo&,  to kompilator zgłosi błąd składniowy

d)     jeżeli RESULT zastąpimy wyrażeniem const Foo*,  to std::cout wyświetli pewną ściśle określoną wartość

 


Zadania

Rozwiązania proszę najpierw napisać na brudno i po sprawdzeniu przenieść na podpisaną kartkę z wersją „finalną”. Proszę zwrócić uwagę na sposób definiowania argumentów i wartości funkcji.

 

  1. Zdefiniuj szablon funkcji abs(x), która zwraca wartość bezwzględną liczby x. Zakładamy, że argument przekazywany jest przez stałą referencję.
  2. Zaimplementuj klasę Macierz, która reprezentuje macierz kwadratową liczb typu int. Rozmiar tablicy powinien być podawany w konstruktorze. Obmyśl poręczny sposób posługiwania się elementami tej macierzy. Nie zapomnij o inicjalizacji!
  3. Oto błędna definicja algorytmu copy_backward przepisana z polskiego wydania podręcznika B. Stroustrupa (Język C++, WNT, Warszawa 2000):

    template<class Bi, class Bi2> Bi2 copy_backward (Bi first, Bi last, Bi2 res)
    {
          while (first != last) *--res = *--first;
          return res;
    }

    Proszę poprawić powyższy kod tak, aby faktycznie odpowiadał algorytmowi
    copy_backward