в

Kazan Dev Alliance

Казанское Сообщество Разработчиков Программного Обеспечения

Персональный блог Александра Демченко

Questions from C++ examine

В зимнем семестре мне довелось в КГУ читать спец. курс по С++. Предполагалось, что студенты ранее могли не изучать C или С++.

В зимнем семестре удалось рассмотреть базовые конструкции языка и классы. Шаблоны были перенесены на весенний семестр. Также одна лекция была посвящена макросам.

Программу зимнего семестра можно скачать тут.

 Экзамен делилился на 2 части. Первая и главная часть - практическая. Нужно было реализовать класс и протестировать его. Это задание было в двух вариантах. Первым вариантом было реализовать класс DynamicArray - массив, при необходимости расширяющий свои размеры (похожий на std::vector). Вторым вариантом - реализовать класс String (строка). Практическое задание было достаточно подробно описано. Было небольшое preview, в котором описывались проблемы, которые разрабатываемый класс должен решить, был приведен интерфейс класса и, наконец, были приведены некоторые инварианты, которым класс должен удовлетворять. Последнее, в частности, должно было снизить вероятность возникновения спорных вопросов, когда, к примеру, я считаю, что некоторая функциональность работает неверно, а студент считает, что верно.

Практическое задание (C++) - вариант 1 (PDF скачать)

Практическое задание (C++) - вариант 2 (PDF скачать)

 

Второй маленькой частью экзамена были простенькие (где-то даже забавные) вопросы по С++, которые не требуют большого исследования. Размещаю эти вопросы и жду ответов в комментах ;-)

  1. Пусть задан некоторый встроенный тип Т. Всегда ли результат sizeof(T) зависит от реализации?

  2. Не используя инструкций выбора напишите функцию Round, которая принимает аргумент типа float и возвращает целое, являющееся результатом округления аргумента. Гарантируется, что аргумент
    неотрицателен.

    Правила округления обычные: если дробная часть меньше 0.5, то выбирается наибольшее целое, не превышающее аргумента. В противном случае -  наименьшее целое, которое не меньше аргумента.

  3. Приведите примеры (>1), когда отсутствие инициализатора в объявлении вызовет ошибку компиляции.

  4. Имеется следующий код:
       
        char* pString = "C++";
        char val = *(pString + 3);
        char* p = pString + 4;
        pString[0] = 'D';

    Определено ли поведение во второй, третьей и четвертой строках листинга. Если да, то каков результат их выполнения, а именно, что будет содержаться в val после выполнения второй строки, в p после
    выполнения третьей и в pString[0] после выполнения четвертой строки.

  5. Здесь требуется некоторый комментарий. В течение семестра мы не рассматривали многомерные массивы и в этом задании студентам предлагается догадаться до того, как это можно сделать в C++ самостоятельно.

    Для данного типа T тип T* является "указателем на T". То есть переменная типа T* может хранить адрес объекта типа T. Из этого определения следует, что если в качестве типа T взять T*, то переменная типа T** является указателем на T* и может хранить адрес переменной типа T*.
    Рассмотрим это на примере:
       int main()
       {
           int x = 1;
           int* pX = &x;
           int** ppX = &pX;
           return 0;
       }

    Переменная ppX имеет тип "указатель на указатель на целое" и, следовательно, может хранить адрес объекта, являющегося указателем на целое.

    Выделение памяти под массив указателей на T (по аналогии) происходит с помощью операции new T*[], тип возвращаемого значения такой операции --- T**.
    Двумерный массив можно считать одномерным массивом, каждый элемент которого сам является массивом.

    Задача. Используя рассмотренные сведения, написать код выделения памяти под двумерный целочисленный массив из m строк и n столбцов (m и n неизвестны на момент компиляции).

    Написать код освобождения памяти для полученного массива.

  6. Дан следующий код:

       #include<iostream>
       using namespace std;
       enum Number
       {
          One = 1,
          Two,
          Three
       };

       int main()
       {
          Number a = ...;
          switch(a)
          {
             case One:
               cout<<"One";
               break;
             case Two:
               cout<<"Two";
             case Three:
               cout<<"Three";
               break;
             default:
               cout<<"Don't know";
               break;
        }
        return 0;
      }
    Строка Numbers a = ...; - это псевдокод. а инициализируется некоторым значением. Что выведется на экран если a равно One, Two, Three? Переписать приведенный код с использованием инструкции выбора if вместо switch.

  7. Напишите функции PrefixIncrement и PostfixIncrement которые увеличивают на единицу значение целого числа, переданного в качестве параметра. Обеспечьте, чтобы первая функция имела поведение аналогичное префиксному инкременту, а вторая - постфиксному.

  8. Почему следующий код вызовет ошибку компиляции:
       
        int x = 1;
        int* p = &x++;

    Скомпилируется ли такой код:
       
        int x = 1;
        int* p = &(++x);

  9. Почему следующий код вызовет неопределенное поведение:

        int* pInt = new int(12);
        cout<<*pInt;
        delete[] pInt;

  10. Сколько раз выполнится следующий цикл

        for (int i = 10; i--; i > 10)
        {
            //Do something
        }
     
    Свой ответ объяснить.
    Подсказка. Не торопитесь с ответом.

  11. Имеется следующий код:

       class A
       {
           A(const A& copy)
           {
               //Do something
           }
         public:
           A() {}
       };

      
    class B
       {
           A a1;
           A a2;
       };

    Все ли конструкторы, которые при необходимости генерируются компилятором неявно, могут быть им корректно созданы в случае с классом B.
    В частности, будет ли код компилироваться, если к указанному коду добавить main:


       int main()
       {
           B b;
           B c = b;
           return 0;
       }

  12. Пусть имеется некоторый пользовательский бинарный оператор, определенный в виде  глобальной функции. Как гарантировать, чтобы первый его операнд был модифицируемым(неконстантным) lvalue? (Гарантировать в том смысле, что если это не так, то возникнет ошибка компиляции). Как гарантировать, что его первый операнд
    может быть константным объектом? Как это гарантировать, если оператор определен в виде метода класса?

  13. Имеется следующий код:
       class A{ };

       class B : protected A { };

       class C : public B
       {
       public:
          void SomeMethod()
          {
             A* pA = this;
          }
       };

       int main()
       {
           B b;
           A* pA = &b;
           return 0;
       }

    Какие строки этого кода вызовут ошибку компиляции и почему?

    Изменилась ли бы ситуация и, если да, то как, если при наследовании B от A использовалось бы закрытое, открытое наследование?

  14. Имеется следующий код:
       class A
       {
       protected:
           int val;
       public:
           A(int val)
           {
               this->val = val;
           }
       };

      
    class B : A
       {
       public:
           B()
           {
               val = 3;
           }
       };

    Почему код не компилируется при таким образом определенном конструкторе класса B. Измените конструктор B так, чтобы код успешно компилировался.

    Будет ли код компилироваться и почему, если конструктор B определить следующим образом:

       class B : A
       {
       public:
           B() : val(3) { }
       };

 

Скачать вопросы в PDF

Комментарии

 

Хосе сказал:

Александр, вы все еще в Аримии? Помню экзамен, и еще помню, что первым его самый лучший студент написал))

January 3, 2009 2:36 PM

Оставить комментарий

(required)  
(optional)
(required)  

© 2007 Kazan Developers Community and Post`s Authors