2015 экзамен ответы вариант 2 (Решённые задачи прошлых лет)
Описание файла
Файл "2015 экзамен ответы вариант 2" внутри архива находится в папке "Решённые задачи прошлых лет". PDF-файл из архива "Решённые задачи прошлых лет", который расположен в категории "". Всё это находится в предмете "языки программирования" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст из PDF
Ответы на вопросы экзамена по курсу«Языки программирования» 09.01.2016В ответах курсивом выделены необязательные пояснения, которые можно опустить(особенно на экзамене)Вариант 2Задача 2-1Объясните, что означает понятие «множественное наследование» в языке С++. Объясните,какие проблемы возникают при реализации виртуальных функций при множественномнаследовании (не перечисляйте общих для множественного наследования проблем, аограничьтесь только специфическими проблемами реализации виртуальных методов примножественном наследовании)?Ответ«Множественное наследование» в языке С++ означает одновременное наследование отнескольких базовых классов (среди которых не должно быть одинаковых).Нужно привести хотя бы один пример записи хотя бы двух баз, а затем сразуприступить к объяснению проблем.
Разговоры про конфликты имен, однозначностьдоступа, ромбовидное наследование и т. п. НЕ ИМЕЮТ ОТНОШЕНИЯ к поставленномувопросу — какие проблемы возникают при реализации виртуальных методов примножественном наследовании. На лекциях обсуждалась по-крайней мере, одна проблема:организация таблицы виртуальных методов при множественном наследовании.Проблема состоит в том, что указатель this при множественном наследовании долженкорректироваться в зависимости от того, какие именно функции (точнее, методы какойименно базы) вызываются.Пусть имеется следующее наслевование:class Base1 {… void f();...}; class Base2 {...void g();...};class D: public Base1, public Base2 { … void h(); …};Base1 b1; Base2 b2; D d;Рассмотрим вызов d.f() - указатель this есть &d.
Однако при вызове d.g() указательthis уже не может быть равен &d, потому что g – это метод класса Base2, а &d указывает нена Base2, а на Base1 (именно поэтому в первом случае &d проходит). Поэтому компиляторв случае вызова d.g() корректирует указатель this, передаваемый в метод g() , увеличивая&d на необходимое число байтов — размер Base1 с учетом выравнивания и т.
п.Это можно сделать во время трансляции, так как связывание невиртуальных методов —статическое. Однако допустим, что и f(), и g(), и h() - виртуальные методы, причем и f(), иg() - замещены в классе D.class Base1 {… virtual void f();...};class Base2 {...virtual void g();...};class D: public Base1, public Base2 {… virtual void h();virtual void f();virtual void g();};Base1 * pb1; Base2 * pb2; D * pd;Base1 b1; Base2 b2; D d;Рассмотрим теперь вызов pb1→f(). Какая функция будет вызвана, и какой указатель thisдолжен быть передан?Если бы pb1 был инициализирован как pb1=&b1, то была бы вызвана функция Base1::f().Если же pb1 был инициализирован как pb1=&d, то была бы вызвана функция D::f().Но в любом случае указатель this должен быть передан равным pb1.Ситуация меняется в случае вызова pb2→g() (то есть для второй базы в спискенаследования).Если бы pb2 был инициализирован как pb2=&b2, то была бы вызвана функция Base2::g().Если бы pb2 был инициализирован как pb2=&d, то была бы вызвана функция D::g().И снова указатель this должен быть равен pb2.
Однако представим, что функция g() НЕЗАМЕЩЕНА в классе D (то есть она объявлена в классе Base2, но заместителя в классе Dнет). Тогда в ОБОИХ вариантах инициализации указатель this должен указывать на объекткласса Base2. Поэтому в первом случае передается this=pb2. А во втором случае (pb2=&d),нужно скорректировать &d на величину Base1 с учетом выравнивания и т. п. так, чтобыthis указывал на часть d от Base2.
Как компилятор догадается, в каком случае нужнокорректировать? В этом и состоит одна из проблем.На этом можно и закончить — ведь проблема объяснена. Но можно (хотя инеобязательно) добавить и про возможные решения проблемы. Стандартный подходсостоит в том, что таблица виртуальных методов содержит в каждой строкедополнительное поле — смещение, которое нужно добавить к указателю this при вызовеметода, соответствующего этой строке.
Кроме этого, вместо одной таблицывиртуальных методов для производного класса D приходится делать ДВЕ таблицы:первая содержит ссылки на методы классов Base1 и D (две строки — для f() и h(),дополнительные смещения для всех нулевые), вторая — ссылки на методы Base2 и D (двестроки — для g() и h(), причем дополнительное смещение для h() - нулевое, а для g() нулевое, если метод замещен в D, и ненулевое, если метод НЕ замещен в D).Задача 1-2Объясните, что означают термины «семантика возобновления» и «семантика завершения».Приведите пример языка, в котором реализована семантика возобновления.
В какихслучаях семантика возобновления может оказаться предпочтительнее, чем семантиказавершения?ОтветСемантика возобновления: после обработки исключения управление может вернутьсянепосредственно в точку, где возникло исключение (варианты: на следующий операторили на любой оператор из того же блока). Пример языка: Visual Basic.Семантика завершения: после возникновения исключения блок, в котором оно возникло,обязательно завершается. Обработка исключения происходит в блоках, вызвавших блок сисключением.В средних и крупных проектах семантика завершения показала свое превосходство,однако, в небольших и простых проектах в ряде случаев семантика возобновления можетбыть предпочтительнее, чем завершение. Примеров можно привести много.
Например:выделение ресурса по принципу ленивой инициализации, ввод информации отпользователя с последующей валидацией. В случае ошибки возможно повторить попыткуввода (вернуться в точку, где возникло исключение), задать значение по умолчанию (наследующий оператор), завершить исполнение или испробовать другие варианты ввода (надругой оператор в блоке).Задача 1-3Дайте определение языковой конструкции «интерфейс». Что означает термин «интеграцияинтерфейсов в язык программирования»? Приведите два различных примера интерфейсов,интегрированных с языком С#.ОтветИнтерфейс можно рассматривать как абстрактный класс, доведенный до «абсолюта».Интерфейс состоит только из публичных абстрактных методов.
Поскольку реализацииметодов нет (равно как нет и невиртуальных методов), то интерфейс не имеетнестатических членов (статические члены, например, константы допустимы). Вобъявлении интерфейса присутствуют только сигнатуры методов (а также свойств вязыках, где есть это понятие), и, возможно, статических членов и вложенных интерфейсов.Интерфейс представляет собой «чистый» контракт. Производный класс, наследуяинтерфейс, «подписывается» под контрактом.
Производный класс, наследующийинтерфейс и замещающий все его методы, называют реализацией интерфейса.Интеграция интерфейсов в язык означает, что язык (компилятор) поддерживает рядстандартных интерфейсов, позволяющих интегрировать семантику языковых конструкцийи пользовательские классы, реализующие эти интерфейсы. Например, в языке C#стандартный интерфейс IEnumerable используется в цикле foreach для прохода поколлекциям. Если пользовательский класс реализует этот интерфейс, то он тоже можетиспользоваться в цикле foreach, как это можно делать со стандартными массивами иколлекциями.Другой пример интегрированного интерфейса в языке C# - IDisposable – интерфейсдля работы с явно уничтожаемыми объектами:interface IDisposable{void Dispose();}Явно уничтожаемые объекты должны реализовать этот интерфейс.
ПроцедураDispose() должна освобождать ресурсы, занятые объектом (кроме памяти, посколькуэто - забота сборщика мусора).Задача 1-4Перечислите модули, которые могут быть единицами компиляции (ЕК) в языке Ада. КакиеЕК называются первичными, а какие — вторичными? В чем состоит отличие первичных ивторичных ЕК в этом языке?ОтветЕК в Аде — спецификации пакетов, в том числе и родовых - первичные модули, телапакетов, тела подпрограмм (в том числе и родовых) - вторичные модули.
Отличиепервичных от вторичных состоит в том, что первичные модули связаны толькоодносторонней связью — то есть клиенты первичных модулей их импортируют(предложение WITH список_имен_модулей), а сами первичные модули о клиентах ничегоне знают. А вторичные модули могут (не всегда, но могут!) иметь двусторонние связи — вклиенте (например, объемлющем теле пакета P ) вмеcто модуля-тела вложенного пакета PPвставляется заглушка ( PACKAGE BODY PP IS SEPARATE;), а сам модуль связывается склиентом с помощью спецификации SEPARATE (P) PACKAGE BODY PP IS ….. END PP;Задача 1-5Что будет напечатано в результате работы следующей программы на Javascript? Считаем,что метод console.log печатает значение аргумента на экран.u = (function f() { var k = 0; return function(x) { return x+k++;} }) ()for (i = 0; i < 5; i++) console.log(u(i))for (i--; i >=0; i--) console.log(u(i))ОтветЗдесь при возврате из функции f() происходит замыкание локальной переменной k.Значение, запомненное в u – это функция от одной переменной и замыканием k.
Прикаждом вызове этой функции r увеличивается на 1.При первом цикле (5 итераций) увеличивается и k, и i, поэтому значения будут 0, 2, 4, 6, 8.После последней итерации k=4.При втором цикле (тоже 5 итераций) k увеличивается, зато i уменьшается, поэтомузначение будет все время 9 (при первой итерации второго цикла i = 4, а k++ = 5).99999Задача 1-6Переписать программу из задачи №5 на языке С++ с использованием механизма лямбдафункций так, чтобы она выдавала в стандартный вывод те же самые значения.Ответint k = 0;int main(){auto u = [](){ return [](int i){return i+k++; };}();int i;for (i = 0; i < 5; i++) std::cout << u(i) << " ";for (i--; i >= 0; i--) std::cout << u(i) << " ";return 0;}Задача 1-7Рассмотрим фрагмент программы на языке Java, содержащий определение класса С:package test;abstract class C { public abstract int f(); }class D { public void foo (){ C c = new C { public int f() { return 1; }};}}При трансляции этого файла выдается ошибка.