В зимнем семестре мне довелось в КГУ читать спец. курс по С++. Предполагалось, что студенты ранее могли не изучать C или С++.
В зимнем семестре удалось рассмотреть базовые конструкции языка и классы. Шаблоны были перенесены на весенний семестр. Также одна лекция была посвящена макросам.
Программу зимнего семестра можно скачать тут.
Экзамен делилился на 2 части. Первая и главная часть - практическая. Нужно было реализовать класс и протестировать его. Это задание было в двух вариантах. Первым вариантом было реализовать класс DynamicArray - массив, при необходимости расширяющий свои размеры (похожий на std::vector). Вторым вариантом - реализовать класс String (строка). Практическое задание было достаточно подробно описано. Было небольшое preview, в котором описывались проблемы, которые разрабатываемый класс должен решить, был приведен интерфейс класса и, наконец, были приведены некоторые инварианты, которым класс должен удовлетворять. Последнее, в частности, должно было снизить вероятность возникновения спорных вопросов, когда, к примеру, я считаю, что некоторая функциональность работает неверно, а студент считает, что верно.
Практическое задание (C++) - вариант 1 (PDF скачать)
Практическое задание (C++) - вариант 2 (PDF скачать)
Второй маленькой частью экзамена были простенькие (где-то даже забавные) вопросы по С++, которые не требуют большого исследования. Размещаю эти вопросы и жду ответов в комментах ;-)
-
Пусть задан некоторый встроенный тип Т. Всегда ли результат sizeof(T) зависит от реализации?
-
Не используя инструкций выбора напишите функцию Round, которая принимает аргумент типа float и возвращает целое, являющееся результатом округления аргумента. Гарантируется, что аргумент
неотрицателен.
Правила округления обычные: если дробная часть меньше 0.5, то выбирается наибольшее целое, не превышающее аргумента. В противном случае - наименьшее целое, которое не меньше аргумента.
-
Приведите примеры (>1), когда отсутствие инициализатора в объявлении вызовет ошибку компиляции.
-
Имеется следующий код:
char* pString = "C++";
char val = *(pString + 3);
char* p = pString + 4;
pString[0] = 'D';
Определено ли поведение во второй, третьей и четвертой строках листинга. Если да, то каков результат их выполнения, а именно, что будет содержаться в val после выполнения второй строки, в p после
выполнения третьей и в pString[0] после выполнения четвертой строки.
-
Здесь требуется некоторый комментарий. В течение семестра мы не рассматривали многомерные массивы и в этом задании студентам предлагается догадаться до того, как это можно сделать в 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 неизвестны на момент компиляции).
Написать код освобождения памяти для полученного массива.
-
Дан следующий код:
#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.
-
Напишите функции PrefixIncrement и PostfixIncrement которые увеличивают на единицу значение целого числа, переданного в качестве параметра. Обеспечьте, чтобы первая функция имела поведение аналогичное префиксному инкременту, а вторая - постфиксному.
-
Почему следующий код вызовет ошибку компиляции:
int x = 1;
int* p = &x++;
Скомпилируется ли такой код:
int x = 1;
int* p = &(++x);
-
Почему следующий код вызовет неопределенное поведение:
int* pInt = new int(12);
cout<<*pInt;
delete[] pInt;
-
Сколько раз выполнится следующий цикл
for (int i = 10; i--; i > 10)
{
//Do something
}
Свой ответ объяснить.
Подсказка. Не торопитесь с ответом.
-
Имеется следующий код:
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;
}
-
Пусть имеется некоторый пользовательский бинарный оператор, определенный в виде глобальной функции. Как гарантировать, чтобы первый его операнд был модифицируемым(неконстантным) lvalue? (Гарантировать в том смысле, что если это не так, то возникнет ошибка компиляции). Как гарантировать, что его первый операнд
может быть константным объектом? Как это гарантировать, если оператор определен в виде метода класса?
-
Имеется следующий код:
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 использовалось бы закрытое, открытое наследование?
-
Имеется следующий код:
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