Обновлено 28.09.2020

Практикум «Наследование. Конструирование объектов производных классов»

Практикум включает вопросы и практические задания по материалу главы 3.1 книги «Введение в ООП».

Основная тема: механизм конструирования объектов производных классов и соответствующий синтаксис C#.

Следует рассматривать задания в приведенном порядке, они упорядочены по темам и сложности. Некоторые задания включают написание кода.

0x01C

Проанализируйте следующие два варианта реализации классов Taxi (такси) и CargoTaxi (грузовое такси).

class Taxi1
{
    public int Id;
    public string RegistrationNumber;
    public Taxi1(int id, string registrationNumber)
    {
        Id = id;
        RegistrationNumber = registrationNumber;
        display("Taxi");
    }
}

class CargoTaxi1 : Taxi1
{
    public float Payload;
    public CargoTaxi1(int id, string registrationNumber, float payload)
        : base (id, registrationNumber)
    {
        Payload = payload;
        display("CargoTaxi");
    }
}

class Taxi2
{
    public int Id;
    public string RegistrationNumber;
    public Taxi2(int id, string registrationNumber)
    {
        Id = id;
        RegistrationNumber = registrationNumber;
        display("Taxi");
    }
}

class CargoTaxi2
{
    public Taxi2 Taxi;
    public float Payload;
    public CargoTaxi2(int id, string registrationNumber, float payload)
    {
        Taxi = new Taxi2(id, registrationNumber);
        Payload = payload;
        display("CargoTaxi");
    }
}

Выберите все верные утверждения.

  1. В первом случае реализуется отношение агрегации.
  2. Во втором случае реализуется отношение агрегации.
  3. В первом случае реализуется отношение наследования.
  4. Во втором случае реализуется отношения наследования.
  5. Отношение наследования точнее отражает семантику классов.
  6. Отношение агрегации точнее отражает семантику классов.
0x023

Что будет выведено пользователю при выполнении следующего кода для классов из предыдущего вопроса:

CargoTaxi1 taxi1 = new CargoTaxi1(100, "й001йй", 2000);
CargoTaxi2 taxi2 = new CargoTaxi2(100, "ф001фф", 2000);
0x024

Дополним код классов Taxi1 и Taxi2 из предыдущего вопроса следующим методом:

public string GetDriverTelNum ()
{
    return "+7 123 4567890";
}

// \...

CargoTaxi1 taxi1 = new CargoTaxi1(100, "й001йй", 2000);
CargoTaxi2 taxi2 = new CargoTaxi2(100, "ф001фф", 2000);

Выберите все верные способы вызова этого метода для объектов taxi1 и taxi2:

  1. taxi1.GetDriverTelNum();
  2. taxi1.Taxi.GetDriverTelNum();
  3. taxi1.base.GetDriverTelNum();
  4. taxi2.GetDriverTelNum();
  5. taxi2.Taxi.GetDriverTelNum();
  6. taxi2.base.GetDriverTelNum();
0x025

Возможно ли только по коду вызова метода объекта определить, относится этот метод к базовому классу (то есть объявлен в базовом классе) или к производному?

  1. Да, так как при вызове метода базового класса используется другой синтаксис.
  2. Нет, синтаксически вызов метода базового класса выглядит точно так же, как вызов метода производного.
0x026

Выберите все верные способы обращения из кода классов CargoTaxi1 и CargoTaxi2 (из предыдущих вопросов) к полю RegistrationNumber класса Taxi1 и Taxi2 соответственно.

  1. CargoTaxi1: RegistrationNumber.
  2. CargoTaxi1: this.RegistrationNumber.
  3. CargoTaxi1: base.RegistrationNumber.
  4. CargoTaxi2: RegistrationNumber.
  5. CargoTaxi2: this.RegistrationNumber.
  6. CargoTaxi1: base.RegistrationNumber.
  7. CargoTaxi1: Taxi.RegistrationNumber.
0x027

Выберите все верные утверждения.

Метод (или поле) класса A, объявленный открытым и не имеющий конфликтов имен, …

  1. …может быть вызван из производного по отношению к A класса без использования специального синтаксиса.
  2. …может быть вызван из производного по отношению к A класса с использованием ключевого слово base и только так.
  3. …может быть вызван из базового по отношению к A класса без использования специального синтаксиса.
  4. …может быть вызван из базового по отношению к A класса с использованием ключевого слово base и только так.
  5. …может быть вызван из внешнего класса через экземпляр объекта, производного по отношению к A.
  6. …может быть вызван из внешнего класса через экземпляр объекта, базового по отношению к A.
0x01D

Сопоставьте термины друг другу.

  1. Агрегация.
  2. Наследование.
  3. Отношение «часть - целое».
  4. Отношение «частное - общее».
  5. Отношение has-a.
  6. Отношение is-a.
0x020

Изучите следующий код.

class Bus
{
    private int id;
    private string dataSourceConnectionString;
    public Bus (string dataSourceConnectionString, int busId)
    {
        this.dataSourceConnectionString = dataSourceConnectionString;
    }

    public Point GetPos ()
    {
        display ("Подключение с использованием строки...");
        const string sql = "select X, Y from VEHICLE_POS where ID = ":id";
        display ("Получение координат...");
        return new Point (10, 10);
    }
}

class Taxi
{
    private int id;
    private string dataSourceConnectionString;
    public Taxi (string dataSourceConnectionString, int taxiId)
    {
        this.dataSourceConnectionString = dataSourceConnectionString;
    }

    public Point GetPos ()
    {
        display ("Подключение с использованием строки...");
        const string sql = "select X, Y from VEHICLE\_POS where ID = ":id";
        display ("Получение координат...");
        return new Point (10, 10);
    }
}

Измените приведенный код таким образом, чтобы ваше решение соответствовало следующей диаграмме (без наследования):

0x021

Измените код из предыдущего вопроса таким образом, чтобы ваше решение соответствовало следующей диаграмме (с наследованием):

0x028

Классы Bus и Taxi на диаграмме классов из предыдущего вопроса - производные по отношению к VehicleCoordinatesProvider?

  1. Конечно же да.
  2. Конечно же нет.
0x004

Какая из UML-диаграмм верно отражает следующий код:

class A
{

}

class B
{
    public B (A a)
    {

    }
}

class C : B
{
    public C () : base (null)
    {

    }
}

0x001

В зависимости от языка программирования для обозначения «наследующего» класса B и «наследуемого» класса A используются различные пары терминов.

class A
{

}

class B : A
{

}

Сопоставьте термины:

  1. Базовый класс.
  2. Base class.
  3. Производный класс.
  4. Derived class.
  5. Суперкласс.
  6. Superclass.
  7. Подкласс.
  8. Subclass.
  9. Вложенный класс.
  10. Nested class.
0x00C

Эквивалентны ли следующие объявления класса B?

class B : A
{
    public B() : base()
    {

    }
}

class B : A
{
    public B()
    {

    }
}
  1.  Да.
  2.  Нет.
0x00D

В следующем коде конструктор B() вызывает конструктор того же класса B(int), которому, в свою очередь, сопоставлен конструктор базового класса A(int). Допустима ли такая цепочка?

class A
{
    public A (int size)
    {

    }
}

class B : A
{
    public B (int size) : base(size)
    {

    }
    public B () : this (0)
    {

    }
}
  1. Да.
  2. Нет.
0x012

Проанализируйте следующий код.

class A
{
    protected int id;
    public A(int id = 1)
    {
        this.id = id;
    }
    public A(string id = "1")
    {
        this.id = int.Parse(id);
    }
}

class B1 : A
{

}

class B2 : A
{
    public B2()
    {

    }
}

class B3 : A
{
    public B3() : base ()
    {

    }
}

class B4 : A
{
    public B4() : base(1)
    {

    }
}

Объявления каких классов B синтаксически корректны?

0x02A

В следующем коде укажите, что необходимо использовать в пропусках (для некоторых пропусков возможно более одного корректного варианта): 1) ключевое слово this; или 2) base; или 3) не использовать ничего.

class Robot
{
    protected int uid;
    protected RobotState state;

    public Robot(int uid)
    {
        ____.uid = uid;
        ____.state = RobotState.Off;
    }

    public void GoToTheBaseStation ()
    {
        ____.state = RobotState.AtTheBaseStation;
    }
}

class VacuumCleaner : Robot
{
    private VacuumState state;

    public VacuumCleaner(int uid) : base (____.uid)
    {
        ____.state = VacuumState.Off;
        display ("{____.uid}...");
    }

    private void StopVacuum()
    {
        display ("stopping...");
        ____.State = VacuumState.Off;
    }

    public void StopAndReturn ()
    {
        if (____.State == VacuumState.On)
        {
            ____.StopVacuum();
        }
        if (____.State != RobotState.AtTheBaseStation)
        {
            ____.GoToTheBaseStation();
        }
    }
}
0x00A

Класс B - производный по отношению к классу A. Класс C - производный по отношению к классу B. Выберите все верные утверждения.

  1. Класс C может обратиться к открытому (public) методу или полю класса B.
  2. Класс C может обратиться к открытому (public) методу или полю класса A.
  3. Класс C может обратиться к защищенному (protected) методу или полю класса B.
  4. Класс C может обратиться к защищенному (protected) методу или полю класса A.
  5. Класс C может обратиться к закрытому (private) методу или полю класса B.
  6. Класс C может обратиться к закрытому (private) методу или полю класса A.
0x018

Если класс содержит только закрытые конструкторы, может ли он быть базовым классом?

class A
{
    private A()
    {

    }
}

// ?
class B : A
{

}
  1. Да.
  2. Нет.
0x029

В следующем коде в пропусках укажите модификаторы видимости (private, protected, public) минимально необходимые, чтобы код работал.

class Point
{
    ____ float x;
    ____ float y;
    ____ float GetDistanceTo(Point p)
    {

    }
}

class Position : Point
{
    ____ DateTime time;

    public GetPositionSpanTo(Position p)
    {
        float distance = GetDistanceTo (p);
        TimeSpan timeSpan = time - p.time;
        return new PositionSpan (distance, timeSpan);
    }

    public void Set (float posX, float poxY, DateTime posTime)
    {
        x = posX;
        y = posY;
        time = posTime;
    }

    public void Set (Position p)
    {
        Set (p.x, p.y, p.time);
    }
}

Также укажите строки кода, в которых:

  1. Выполняется обращение к закрытому или защищенному полю другого объекта, объявленном непосредственно в классе объекта (не в базовом).
  2. Выполняется обращение к закрытому или защищенному полю другого объекта, объявленному в базовом классе этого объекта.
0x028

В следующем коде выполняется обращение к защищенному полю извне, корректен ли этот код?

class A
{
    protected int id;
}

class B : A
{
    public B (int id)
    {
        this.id = id;
    }
}

class C : A
{
    public C (B b)
    {
        id = b.id;
    }
}

B b = new B(100);
C c = new C(b);
  1. Да, так как это экземпляра того же класса.
  2. Нет, так как это другой экземпляр.
0x02C

Заполните таблицу модификаторов видимости.

Из кода каких методов, указанных в строках таблицы, возможно обратиться к методам с модификаторами видимости, указанными в столбцах таблицы? Для каждой ячейки необходимо указать «да» (есть доступ) или «нет» (нет доступа).

private (закрытый)protected (защищенный)public (открытый)
Из методов того же класса???
Из методов производных классов???
Из методов других (не производных) классов???
Из методов того же объекта того же класса???
Из методов того же объекта производного класса???
Из методов базового класса???
0x02D

Классы B1 и B2 - производные по отношению к классу A. Класс B1 включает как поле экземпляр класса B2:

class A
{

}

class B1 : A
{
    protected B2 b2;
}

class B2 : A
{

}

Выберите все верные утверждения.

  1. Код методов класса B1 имеет доступ к защищенным и закрытым поля базового класса A объекта b2.
  2. Код методов класса B1 имеет доступ к защищенным и закрытым полям класса B2 объекта b2.
0x007

Наследуются ли конструкторы классов? Корректен ли следующий код?

class A
{
    public A()
    {

    }
}

class B : A
{
    public B (int size) : base ()
    {

    }
}

// Обращение к конструктору без параметров,
// объявленному в базовом классе?
var b = new B();
  1. Да, мы можем использовать конструктор базового класса при создании экземпляра производного класса при условии, что в производном классе не объявлен конструктор с такой же сигнатурой и что конструктор базового класса имеет модификатор видимости public.
  2. Нет, конструкторы не наследуются, и мы не можем прямо вызывать конструктор базового класса для создания экземпляра производного класса подобно тому, как мы вызываем метод базового класса для экземпляра производного.
0x02E

Изучите следующий код.

class Driver
{
    private int id;
    public string License;
    public Dirver (int id, string license)
    {
        this.id = id;
        License = license;
    }
}

class Vehicle
{
    private VehicleGLONASSProvider vehicleGLONASSProvider;
    protected int id;
    protected Driver driver;
    protected string registrationNumber;

    public Vehicle(
        int id, 
        string registrationNumber, 
        Driver driver,
        VehicleGLONASSProvider vehicleGLONASSProvider)
    {
        this.id = id;
        this.registrationNumber = registrationNumber;
        this.driver = driver;
        this.vehicleGLONASSProvider = vehicleGLONASSProvider;
    }

    public Point GetPos()
    {
        return vehicleGLONASSProvider.GetPos (id);
    }
}

class Bus : Vehicle
{
    private string route;
    public Bus (
        int id, 
        string registrationNumber, 
        string route, 
        Driver driver,
        VehicleGLONASSProvider vehicleGLONASSProvider)
    : base (id, registrationNumber, driver, vehicleGLONASSProvider)
    {
        this.route = route;
    }
}

Реализуйте конструктор копирования, при необходимости добавьте конструкторы или методы в приведенные классы, но не меняйте уровни видимости полей.

public Bus (Bus bus)
{
// ...
}

Конструктор копирования должен в максимально возможно мере полагаться на значения полей, а не методы, через который мы можем эти значения получить. При копировании должны быть созданы полные копии всех зависимых объектов, а не ссылки.

0x002

Назовите все способы запрещения наследования класса.

  1. Пометить класс ключевым словом sealed.
  2. Объявить все конструкторы класса закрытыми private.
  3. По умолчанию наследования запрещено, наоборот, для разрешения наследования класса нужно помечать его специальным ключевым словом.
  4. Пометить все методы класса ключевым словом private.
0x017

Рассмотрите следующий пример.

class A
{
    public static int StaticField;
    public static void StaticMethod()
    {

    }
}

class B : A
{

}

class C : B
{

}

A.StaticField = 1;
B.StaticField = 1;
C.StaticField = 1;
display(C.StaticField);

Наследуются ли статические поля и методы? Выберите все верные утверждения:

  1. В методах производного класса можно обращаться к открытым и защищенным статическим полям и методам базового класса.
  2. К статическим открытым (public) полям и методам базового класса A можно обращаться с использованием имени базового класса: A.StaticField, A.StaticMethod().
  3. К статическим открытым (public) полям и методам базового класса A можно обращаться с использованием имени производного класса: B.StaticField, B.StaticMethod().
0x02F

Сопоставьте термины и определения:

  1. Множественное наследование (multiple)…
  2. …наследование от нескольких базовых классов.
  3. Многоуровневое наследование (multilevel)…
  4. …базовые классы могут быть, в свою очередь, производными по отношению к другим базовым классам.
0x030

Выберите все верные утверждения:

  1. Множественное наследование существенно усложняет логику программы, при этом те же цели могут быть достигнуты другими подходами, поэтому механизм множественного наследования реализован только в некоторых языках.
  2. Механизм множественного наследования реализован только в некоторых языках, так как это сложная задача для разработчиков языка и возможна только для зрелых и давно развивающихся языков.
  3. Механизм множественного наследования классов реализован в C#.
0x031

Изучите следующий код.

class A
{
    public void M()
    {

    }
}

class B : A
{

}

class C : B
{
    public C()
    {
        base.M();
    }
}

Выберите все верные утверждения.

  1. В коде ошибка, так как ключевое слово base обозначает «ближайший» базовый класс в иерархии наследования, а метод M() объявлен в «самом» базовом.
  2. В коде ошибка, так как ключевое слово base может использоваться только для указания соответствующего конструктора: public C() : base ().
  3. В коде все верно, так как ключевое слово base может использоваться для вызова методов и полей базовых классов, как если бы мы их вызывали из «ближайшего» базового. При этом использование base здесь обязательно.
  4. В коде все верно, так как ключевое слово base может использоваться для вызова методов и полей базовых классов, как если бы мы их вызывали из «ближайшего» базового. При этом использование base здесь необязательно, код без него будет работать так же.
  5. Вместо ключевого слова base здесь можно использовать this.
0x009

Допустимо ли обращение к базовому классу базового класса через выражение base.base?

  1. Да.
  2. Нет.
0x008

Какой модификатор видимости имеет ключевое слово base?

  1. public
  2. private
  3. protected
  4. Поведение ключевого слово base не подходит ни под один из модификаторов видимости.
0x032

В следующем примере при попытке создания экземпляра класса A произойдет переполнение стека.

class A
{
    public static int InstanceNum = 0;
    public A Field = new A();

    public A()
    {
        InstanceNum++;
    }
}​

try
{
    var a = new A();
}
finally
{
    display(A.InstanceNum);
}

При прерывании программы из-за переполнения стека, чему равно значение поля A.InstanceNum?

  1. Ноль.
  2. Больше нуля, число зависит от того, сколько экземпляров класса A было рекурсивно создано до достижения размера стека.

Дополнительный вопрос: попадем ли мы в блок finally?

0x014

Допускается ли обращаться к другим полям того же класса в инициализаторе поля класса? Например, как в следующем коде, где инициализатор поля c использует поле b:

class A
{
    public B b = new B();
    public C c = new C(b);
}
  1. Да, допускается. Если соответствующее поле еще не инициализировано, то будет использовано значение по умолчанию для типа этого поля.
  2. Да, допускается. Но используемое поле обязательно должно быть уже инициализировано, как в приведенном примере.
  3. Да, допускается. При этом компилятор сам выявит порядок инициализации, обеспечивающий инициализацию всех зависимых друг от друга полей.
  4. Нет, в инициализаторе поля нет доступа к переменной this (this.b).
0x033

Допускается ли обращение к методам и полям базового класса из инициализаторов полей производного?

class A
{
    public int Id;
}

class B : A
{
    public C c = new C(Id);
}
  1. Да, допускается.
  2. Нет, не допускается, в инициализаторе поля нет доступа к переменной base (base.Id).
0x034

Изучите следующий код.

class Vehicle
{
    private int id = -1;
    private string registrationNumber;

    public Vehicle(int id, string registrationNumber)
    {
        display("Vehicle");
        this.id = id;
        this.registrationNumber = registrationNumber;
    }
}

class Taxi : Vehicle
{
    private string company;
    public Taxi1(
        int id, 
        string registrationNumber, 
        string company)
        : base (id, registrationNumber)
    {
        display("Taxi");
        this.company = company;
    }
}

class CargoTaxi : Taxi
{
    public float Payload = -1;
    public CargoTaxi1(
        int id, 
        string registrationNumber,
        string company,
         float payload)
    : base (id, registrationNumber, company)
    {
        display ("CargoTaxi");
        Payload = payload;
    }
}

var cargoTaxi = new CargoTaxi(100, "яя001я01", "Cars", 5000);

В каком порядке будут выведены пользователю сообщения при создании объекта cargoTaxi (в каком порядке будут выполняться конструкторы в иерархии наследования)?

  1. CargoTaxi, Taxi, Vehicle.
  2. Vehicle, Taxi, CargoTaxi.
0x035

Поле класса может быть инициализировано как инициализатором при объявлении, так и в конструкторе. Могут использоваться одновременно оба варианта:

class Circle
{
    public double R = 0;
    public Circle(double radius)
    {
        R = radius;
    }
}

Выберите все верные утверждения:

  1. Если поле не было инициализировано явно при объявлении, то в конструкторе его начальное значение равно значению по умолчанию для соответствующего типа данных.
  2. Если поле не было инициализировано явно при объявлении, то в конструкторе при обращении к нему возникнет ошибка компиляции.
  3. Если поле получает значение и в конструкторе, и при объявлении, то сначала применяется в конструкторе, потом перезаписывается при объявлении.
  4. Наоборот.
0x003

Выберите действия из списка и укажите порядок их выполнения при конструировании конструирования объекта производного класса:

  1. Выделяется память для хранения полей всех классов в иерархии наследования.
  2. Для всех классов в иерархии наследования в порядке от «самого производного» к «самому базовому» …
  3. Для всех классов в иерархии наследования в порядке от «самого базового» к «самому производному» …
  4. …выделяется память для очередного класса в иерархии наследования…
  5. …все поля очередного класса инициализируются или указанными явно значениями, или значениями по умолчанию для соответствующего типа данных.
  6. …все поля класса очередного класса инициализируются указанными явно значениями или остаются неинициализированными…
  7. Определяется цепочка конструкторов в иерархии наследования для вызванного конструктора производного класса.
  8. Конструкторы цепочки выполняются последовательно в порядке от «самого базового» к «самому производному».
  9. Конструкторы цепочки выполняются последовательно в порядке от «самого производного» к «самому базовому».
  10. Последний выполнившийся конструктор возвращает адрес созданного объекта.
  11. После инициализации полей последнего в порядке инициализации класса возвращается адрес созданного объекта.
0x011

Изучите следующий код.

class A
{
    public int FieldA = 1;
    public A()
    {
        FieldA = 2;
    }
}​

class B : A
{
    public int FieldB = 3;
    public B()
    {
    FieldB = 4;
    FieldA = 5;
    }
}

B b = new B();
display(b.FieldA);
display(b.FieldB);

Что будет выведено?

0x016

Изучите следующий код.

class A
{
    public A(string msg)
    {
        display(msg);
    }
}

class B : A
{
    public A fa1 = new A("B.fa1");
    public B()
    {
        display("B.B()");
    }
}

class C : B
{
    public A fa2 = new A("C.fa2");
    public C()
    {
        display("C.C()");
    }
}

var c = new C();

В каком порядке будут выведены сообщения при создании экземпляра класса C?

0x036

Предположим, у нас есть способ в любой момент времени выполнения программы точно определять объем занятой памяти и предположим, что кроме нашего кода никто память не занимает и не освобождает (нет срабатываний сборщика мусора). Что бы вывел при таких допущениях следующий код:

class A
{
    public int Id;
    public A()
    {
        display(memory());
    }
}   

class B : A
{
    public int Id2;
    public B()
    {
        display(memory());
    }
}

B b = new B();
display(memory());
0x037

Выберите все верные утверждения:

  1. Многоуровневое наследование усложняет логику программы, поэтому следует минимизировать число уровней.
  2. Наследование - всегда лучший способ вынести общий код (в базовый класс).
  3. Наследование усложняет логику программы в больше мере, чем агрегация.
0x038

Изучите следующий код.

class ReportA
{
    private void WriteHead()
    {
        display("writeHead");
    }
    private void WriteBottom()
    {
        display("writeBottom");
    }
}
class ReportB
{
    private void WriteHead()
    {
        display("writeHead");
    }
    private void WriteBottom()
    {
        display("writeBottom");
    }
}

Чтобы избежать повторения методов WriteHead и WriteBottom, используемых в разных отчетах и имеющих одинаковую реализацию, выделите эти методы в общий базовый класс, чтобы ваше решение соответствовало следующей модели:

Объясните, почему здесь создание иерархии - неудачное решение. Перепишите приведенный код, чтобы он соответствовал следующей модели:

Объясните, почему это решение лучше.

О сайте, об авторе, контакты, оставить отзыв.

Телеграм-канал: newobjx.

© Тимофей Усов, 2019—2020.