Методические указания по выполнению лабораторных и практических работ по мдк
Скачать 3.25 Mb.
|
Практическая работа № 1.14. Создание наследованных классов { public: Base(int, float); }; class Derived: Base 64 { public: Derived(char* lst, float amt); }; Derived:: Derived(char* lst, float amt) : Base(strlen(lst),amt) { } В деструкторе производного класса компилятор автоматически генерирует вызовы базовых деструкторов, поэтому для удаления объекта производного класса следует сделать деструктор в базовых классах виртуальным. Для вызова используется delete this либо operator delete. Виртуальные функции Функция-элемент может быть объявлена как virtual. Ключевое слово virtual предписывает компилятору генерировать некоторую дополнительную информацию о функции. Если функция переопределяется в производном классе и вызывается с указателем (или ссылкой) базового класса, ссылающимся на представитель производного класса, эта информация позволяет определить, какой из вариантов функции должен быть выбран: такой вызов будет адресован функции производного класса. Для виртуальных функций существуют следующие правила: виртуальную функцию нельзя объявлять как static. спецификатор virtual необязателен при переопределении функции в производном классе. виртуальная функция должна быть определена в базовом классе и может быть переопределена в производном. Ход работы Задание. Написать программу с наследованием класса стек от класса массив. #include #include { int *num; int kol; public: massiv(int n); void print(); virtual int kolich(){return kol;} void put(int k,int n){num[k]=n;} massiv(){delete num;} }; massiv::massiv(int n) { num=new int[n]; kol=n; for(int i=0;i { for(int i=0;i { int top; public: stec(int); virtual int kolich() {return top;} void pop(int k); }; stec::stec(int n):massiv(n) { top=0; } void stec::pop(int k) { put(top++,k); } void main() 65 { randomize(); massiv a(10); a.print(); stec b(10); b.pop(random(100)-50); b.pop(random(100)-50); b.pop(random(100)-50); b.print(); } Задание 2. Разработать программу с использованием наследования классов, реализующую классы: графический объект; круг; квадрат. Используя виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран его размер и координаты. Задание 3. Разработать программу с использованием наследования классов, реализующую классы: железнодорожный вагон; вагон для перевозки автомобилей; цистерна. Используя виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран его вес и количество единиц товара в вагоне. Практическая работа № 1.15. Работа с объектами через интерфейсы Цель работы: изучить способ создания и реализации интерфейса Ход работы Задание 1. Создание и реализация интерфейса В этом упражнении Вы создадите интерфейс, определяющий поведение классов, которые будут его реализовывать. Предполагается, что в библиотечной системе, разработанной в прошлой работе есть необходимость реализовать возможность оформления подписки на периодические издания. Включение этой функциональности в базовый класс Item не является правильным решением, так как к изданиям, оформляющим подписку не относятся, например книги. Поэтому необходимо создать интерфейс, объявляющий возможность оформления подписки и тогда классы, для которых предполагается данная функциональность должны будут реализовывать этот интерфейс. IPubs_с_требуемой_функциональностью'>Выполните подготовительные операции Создайте папку Lab07 и скопируйте в нее решение MyClass, созданное в прошлом упражнении. Создайте интерфейс IPubs с требуемой функциональностью Откройте проект MyClass.sln в папке install folder\Labs\Lab07\. Добавьте в проект новый интерфейс с именем IPubs: Projects (Проект) Add class (Добавить класс). В окне Добавление нового элемента выберите Интерфейс и укажите его имя IPubs. В интерфейсе IPubs объявите его функциональные члены – метод для проверки оформлена ли подписка на издание Subs и свойство IfSubs для оформления подписки: interface IPubs { void Subs(); bool IfSubs { get; set;} } 36 Реализуйте интерфейс в классе Magazine Откройте класс Magazine и добавьте интерфейс в список наследования: class Magazine : Item, IPubs { Реализуйте свойство и метод, объявленные в интерфейсе: public bool IfSubs { get; set; } public void Subs() { Console.WriteLine("Подписка на журнал \"{0}\": {1}." , title, IfSubs); } 66 Протестируйте новую функциональность В методе Main класса Program добавьте для уже имеющегося журнала mag1 установку свойству IfSubs значения, устанавливающую подписку и вызовите метод Subs для отображения информации о подписке: Magazine mag1 = new Magazine("О природе", 5,"Земля и мы", 2014, 1235, true); mag1.TakeItem(); mag1.Show(); mag1.IfSubs = true; mag1.Subs(); Постройте проект и исправьте ошибки, если это необходимо. Запустите и протестируйте программу. Задание 2. Использование стандартных интерфейсов В библиотеке классов .Net определено множество стандартных интерфейсов, задающих желаемую функциональность объектов. В этом упражнении Вы примените интерфейс IComparable, который задает метод сравнения объектов по принципу больше и меньше, что позволяет переопределить соответствующие операции в рамках класса, наследующего интерфейс IComparable. Сравнение и дальнейшая сортировка будет реализована по полю invNumber – Инвентарный номер. Реализуйте наследование интерфейса IComparable Добавьте в объявление абстрактного класса Item наследование интерфейса IComparable: abstract class Item : IComparable { Интерфейс IComparable определен в пространстве имен System и содержит единственный метод CompareTo, возвращающий результат сравнения двух объектов – текущего и переданного ему в качестве параметра. Реализация данного метода должна возвращать: 0 – если текущий объект и параметр равны, отрицательное число, если текущий объект меньше параметра и положительное число, если текущий объект больше параметра. Добавьте в класс Item реализацию этого метода, причем сравнение реализуйте по полю invNumber: int IComparable.CompareTo(object obj) { Item it = (Item)obj; if (this.invNumber == it.invNumber) return 0; else if (this.invNumber > it.invNumber) return 1; else return -1; } Протестируйте использование новой функциональности В методе Main класса Program создайте массив ссылок на абстрактный базовый класс Item: Item[] itmas = new Item[4]; Заполните массив созданными ранее книгами и журналом: itmas[0] = b1; itmas[1] = b2; itmas[2] = b3; itmas[3] = mag1; Отсортируйте массив с помощью статического метода Sort класса Array: Array.Sort(itmas); Отобразите весь список книг и журналов, используя полиморфный вызов метода Show: Console.WriteLine("\nСортировка по инвентарному номеру"); foreach (Item x in itmas) { x.Show(); } Постройте проект и исправьте ошибки, если это необходимо. Запустите и протестируйте программу. Информация о каждом элементе хранения должна выводиться согласно возрастанию инвентарных номеров. Задание 3. Создание иерархии классов «Фигуры» 67 В этом упражнении требуется создать иерархию классов – геометрических фигур: треугольник, окружность и квадрат, которые являются производными классами общего класса Shape. Класс треугольник реализован в упражнении 3 лабораторной работы 5. Классы окружность и квадрат определяются соответственно радиусом и стороной. Реализуйте конструкторы, создающие объекты с заданным радиусом и стороной и методы, позволяющие: вывести длины радиуса окружности и стороны квадрата на экран; расчитать периметр и площадь фигур. Общую функциональность фигур реализуйте в базовом классе Shape. Реализуйте дополнительную функциональность для треугольника и квадрата –вращение фигуры вокруг своего центра. Метод вращения представьте в интерфейсе. Классы треугольник и квадрат должны реализовывать этот интерфейс. Практическая работа № 1.16. Использование стандартных интерфейсов Цель работы: изучить способы использования стандартных интерфейсоф Ход работы Интерфейс представляет некое описание типа, набор компонентов, который должен иметь тип данных. И, собственно, мы не можем создавать объекты интерфейса напрямую с помощью конструктора, как например, в классах: 1 IMovable m = new IMovable(); // ! Ошибка, так сделать нельзя В конечном счете интерфейс предназначен для реализации в классах и структурах. Например, возьмем следующий интерфейс IMovable: 1 2 3 4 interface IMovable { void Move(); } Затем какой-нибудь класс или структура могут применить данный интерфейс: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // применение интерфейса в классе class Person : IMovable { public void Move() { Console.WriteLine("Человек идет"); } } // применение интерфейса в структуре struct Car : IMovable { public void Move() { Console.WriteLine("Машина едет"); } } При применении интерфейса, как и при наследовании после имени класса или структуры указывается двоеточие и затем идут названия применяемых интерфейсов. При этом класс должен реализовать все методы и свойства применяемых интерфейсов, если эти методы и свойства не имеют реализации по умолчанию. Если методы и свойства интерфейса не имеют модификатора доступа, то по умолчанию они являются публичными, при реализации этих методов и свойств в классе и структуре к ним можно применять только модификатор public. Применение интерфейса в программе: 1 using System; 68 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 namespace HelloApp { interface IMovable { void Move(); } class Person : IMovable { public void Move() { Console.WriteLine("Человек идет"); } } struct Car : IMovable { public void Move() { Console.WriteLine("Машина едет"); } } class Program { static void Action(IMovable movable) { movable.Move(); } static void Main(string[] args) { Person person = new Person(); Car car = new Car(); Action(person); Action(car); Console.Read(); } } } В данной программе определен метод Action(), который в качестве параметра принимает объект интерфейса IMovable. На момент написания кода мы можем не знать, что это будет за объект - какой-то класс или структура. Единственное, в чем мы можем быть уверены, что этот объект обязательно реализует метод Move и мы можем вызвать этот метод. Иными словами, интерфейс - это контракт, что какой-то определенный тип обязательно реализует некоторый функционал. Реализация интерфейсов по умолчанию Начиная с версии C# 8.0 интерфейсы поддерживают реализацию методов и свойств по умолчанию. Зачем это нужно? Допустим, у нас есть куча классов, которые реализуют некоторый интерфейс. Если мы добавим в этот интерфейс новый метод, то мы будем обязаны реализовать этот метод во всех классах, применяющих данный интерфейс. Иначе подобные классы просто не будут компилироваться. Теперь вместо реализации метода во всех классах нам достаточно определить его реализацию по умолчанию в интерфейсе. Если класс не реализует метод, будет применяться реализация по умолчанию. 1 2 3 4 5 class Program { static void Main(string[] args) { IMovable tom = new Person(); 69 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Car tesla = new Car(); tom.Move(); // Walking tesla.Move(); // Driving } } interface IMovable { void Move() { Console.WriteLine("Walking"); } } class Person : IMovable { } class Car : IMovable { public void Move() { Console.WriteLine("Driving"); } } В данном случае интерфейс IMovable определяет реализацию по умолчанию для метода Move. Класс Person не реализует этот метод, поэтому он применяет реализацию по умолчанию в отличие от класса Car, который определяет свою реализацию для метода Move. Стоит отметить, что хотя для объекта класса Person мы можем вызвать метод Move - ведь класс Person применяет интерфейс IMovable, тем не менее мы не можем написать так: 1 2 Person tom = new Person(); tom.Move(); // Ошибка - метод Move не определен в классе Person Множественная реализация интерфейсов Интерфейсы имеют еще одну важную функцию: в C# не поддерживается множественное наследование, то есть мы можем унаследовать класс только от одного класса, в отличие, скажем, от языка С++, где множественное наследование можно использовать. Интерфейсы позволяют частично обойти это ограничение, поскольку в C# класс может реализовать сразу несколько интерфейсов. Все реализуемые интерфейсы указываются через запятую: 1 2 3 4 myClass: myInterface1, myInterface2, myInterface3, ... { } Рассмотрим на примере: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using System; namespace HelloApp { interface IAccount { int CurrentSum { get; } // Текущая сумма на счету void Put(int sum); // Положить деньги на счет void Withdraw(int sum); // Взять со счета } interface IClient { string Name { get; set; } } class Client : IAccount, IClient { int _sum; // Переменная для хранения суммы 70 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public string Name { get; set; } public Client(string name, int sum) { Name = name; _sum = sum; } public int CurrentSum { get { return _sum; } } public void Put(int sum) { _sum += sum; } public void Withdraw(int sum) { if (_sum >= sum) { _sum -= sum; } } } class Program { static void Main(string[] args) { Client client = new Client("Tom", 200); client.Put(30); Console.WriteLine(client.CurrentSum); //230 client.Withdraw(100); Console.WriteLine(client.CurrentSum); //130 Console.Read(); } } } В данном случае определены два интерфейса. Интерфейс IAccount определяет свойство CurrentSum для текущей суммы денег на счете и два метода Put и Withdraw для добавления денег на счет и изъятия денег. Интерфейс IClient определяет свойство для хранения имени клиента. Обатите внимание, что свойства CurrentSum и Name в интерфейсах похожи на автосвойства, но это не автосвойства. При реализации мы можем развернуть их в полноценные свойства, либо же сделать автосвойствами. Класс Client реализует оба интерфейса и затем применяется в программе. Интерфейсы в преобразованиях типов Все сказанное в отношении преобразования типов характерно и для интерфейсов. Поскольку класс Client реализует интерфейс IAccount, то переменная типа IAccount может хранить ссылку на объект типа Client: 1 2 3 4 5 6 7 8 // Все объекты Client являются объектами IAccount IAccount account = new Client("Том", 200); account.Put(200); Console.WriteLine(account.CurrentSum); // 400 // Не все объекты IAccount являются объектами Client, необходимо явное приведение Client client = (Client)account; // Интерфейс IAccount не имеет свойства Name, необходимо явное приведение string clientName = ((Client)account).Name; Преобразование от класса к его интерфейсу, как и преобразование от производного типа к базовому, выполняется автоматически. Так как любой объект Client реализует интерфейс IAccount. Обратное преобразование - от интерфейса к реализующему его классу будет аналогично преобразованию от базового класса к производному. Так как не каждый объект IAccount является 71 объектом Client (ведь интерфейс IAccount могут реализовать и другие классы), то для подобного преобразования необходима операция приведения типов. И если мы хотим обратиться к методам класса Client, которые не определены в интерфейсе IAccount, но являются частью класса Client, то нам надо явным образом выполнить преобразование типов: string clientName = ((Client)account).Name; |