
- •Оглавление
- •Цель работы
- •Основные сведения Основные сведения
- •Варианты задания
- •Контрольные вопросы
- •Цель работы
- •Основные сведения
- •Варианты задания
- •Контрольные вопросы
- •Лабораторная работа3. Виртуальные функции. Выбор типа объектов Цель работы.
- •Основные сведения
- •Варианты задания
- •Контрольные вопросы
- •Лабораторная работа 4. КонтейнерыStl. Обработка исключений Цель работы.
- •Основные сведения
- •Варианты задания
- •Контрольные вопросы
- •Лабораторная работа 5. Классы, объекты, наследование в с# Цель работы
- •Основные сведения
- •Контрольные вопросы
- •Требования к отчету
- •Литература
- •Приложения
- •1.Создание консольного приложения в VisialStudio.Net
Контрольные вопросы
В чем заключается наследование одного класса другому? В чем разница в организации наследования полей и методов?
Определены 2 класса:
сlass Based{public: int x;};
сlass Derived :public Based{};
Какое значение выводится на консоль?
Based b1;
b1.x=3;
Derived d1;
d1.x=4;
cout<<b1.x;
Удачной ли является иерархия классов, при которой некоторый класс Х является производным от большого количества классов с большим числом полей в каждом ( AB C … X)? Какая существует альтернатива наследованию?
Каким образом производится управление доступом к унаследованным компонентам производных классов? Есть ли в представленном фрагменте программы ошибки и какое сообщение выдаст компилятор? Как исправить ошибку?
class Base {
int x;
public:
int y;
void setX(int n){x=n;}
void showX() const{ cout<<x<<endl;}
};
class Derived: public Base {
public:
void setY(int n){y=n;}
void show_sum() const{ cout<<x+y<<endl; }
};
int _tmain(int argc, _TCHAR* argv[])
{
Derived d1;
d1.show_sum();
return 0;
}
Есть ли ошибки в нижеследующих объявлениях
// класс Shape абстрактный
Shape sh;
Shape *psh;
Shape *psh1=new Shape();
6. Что из себя представляет виртуальная функция и как она должна вызываться?
7. Как создать общий интерфейс иерархии классов?
Лабораторная работа3. Виртуальные функции. Выбор типа объектов Цель работы.
Ознакомление с механизмом создания объектов во время выполнения программы с возможностью выбора классов.
Основные сведения
Продолжим обсуждение использования виртуальных функций, начатое в предыдущей работе. Там инструкции создания объектов производных классов записаны явно, в тексте программы (см. конструктор Garage в листинге 2.2); виртуальные функции определяют поведение объектов, подстраиваясь под их тип.
В этой работе механизм виртуальных функций используется для создания программ, в которых выбор типа объекта производится во время выполнения программы по запросу пользователя. Это идея паттерна проектирования программ под названием «Фабричный метод» (Faсtory Method) [2].
Еще немного о виртуальных функциях. Когда говорят об их вызове, обычно имеют в виду т.н. позднее связывание объекта класса и функции. Дело в том, что наличие виртуальной функции в иерархии классов приводит к созданию для каждого класса таблицы, в которую заносятся адреса реализаций виртуальных функций этого класса. Таким образом, адреса разных реализаций одной виртуальной функции иерархии различны. Объект класса при создании получает адрес таблицы своего класса. Указателем, обращающимся ко всем таблицам иерархии, является указатель на базовый класс. Если при выполнении программы в какой-либо точке этот указатель получает адрес объекта одного их производных классов, то он через соответствующее поле объекта обращается к таблице этого класса и вызывает соответствующую реализацию виртуальной функции. Поэтому конкретная реализация виртуальной функции определяется динамически во время выполнения программы. Поэтому же вызов виртуальных функций обходится дороже, чем вызов обычной - выполняется дополнительная операция адресации.
Если в производном классе виртуальный метод не переопределен, то вызов будет передаваться вверх по иерархии классов вплоть до базового ( последний в этом случае не может быть абстрактным).
Упрощенная схема программы с выбором типа объектов во время выполнения программы приведена ниже.
Листинг3.1
class Based {
public:
virtual int fb(){return 0;}
//При наличии в классе виртуальных функций необходим виртуальный деструктор
virtual ~Based(){}
};
class Derived1:public Based {
public:
int fb() {return 5;}
~Derived1(){}
};
class Derived2:public Based {
int fb() {return 7;}
~Derived2(){}
};
class Derived3:public Based {
// fbне определена
};
voidmain()
{
Derived1 d1;в
Based *pb=0;
int id=1;
cout<<"Id?"<<endl;
cin>>id;
switch(id)
{case 1: pb=new Derived1; break;
case 2: pb=new Derived2; break;
case 3: pb=new Derived3; break;
default: pb=new Derived3;
}
cout<<pb->fb();
deletepb;
}
В лабораторной работе должна быть создана программа, случайным образом выбирающая два класса (T1, T2) из семи производных классов и выполняющей некоторые действия над объектами этих двух классов. Базовым является класс Shape.
Производные классы должны создавать плоские объекты следующих типов: квадрат, треугольник, прямоугольник, параллелограмм, трапеция, правильный шестиугольник, правильный восьмиугольник (square, triangle, rectangle, parallelogram, trapeze, hexagon, octagon). Для каждого типа фигуры вычисляются площадь, центр тяжести и другие атрибуты [4], а также должны быть предусмотрены виртуальные методы Вращения (Rotate) и Перемещения (Move).
Операции над объектами этих классов могут быть такими (функции с 2-мя аргументами Shape* obj1, Shape* obj2):
сравнить два объекта по площади - Compare;
определить факт пересечения объектов – IsIntersect;
определить факт включения одного объекта в другой – IsInclude.
Алгоритмы для реализации этих функций есть в любом учебнике по компьютерной графике. Если вас затрудняет реализация этих алгоритмов, то я разрешаю вместо них использовать области вращения фигур, которые представляют из себя окружность, описанную вокруг нее там, где это возможно, или проведенную из центра тяжести с радиусом, равным расстоянию от него до наиболее удаленной точки фигуры. Теперь перечисленные операции можно реализовывать не с объектами, а с их областями вращения.
Ниже приводится рекомендуемый вариант программы (упрощенный). В ней из трех фигур выбираются и обрабатываются две – объекты классов Triangle и Rectangle.
//Листинг3.2
#include "stdafx.h"
#include <iostream>
using namespace std;
struct Point{
int x,y;
};
class Shape { // Абстрактный базовый класс
public:
Point *arc;
char ID;
/* Поля и методы */
virtual int GetArea(){return 0;}//Вычисление площади фигуры
virtual void CreateShape()=0;
virtual ~Shape(){}
};
class Rectangle : public Shape {
public:
Rectangle (){arc=new Point[4]; ID='R';}
void CreateShape(){cout<<"Input Rectangle coordinate points "<<endl;
cin>>arc[0].x; /*вводим все координаты*/}
/* методы*/
int GetArea(){ /*вычисление конкретной площади*/return 4;}
~Rectangle(){delete[] arc;}
};
class Triangle : public Shape {
public:
Triangle (){arc=new Point[3]; ID='T';}
void CreateShape(){cout<<"Input Triangle coordinate points"<<endl;
cin>>arc[0].x; /*вводим все координаты*/}
/*.....*/
int GetArea(){/*вычисление конкретной площади*/return 3;}
~Triangle(){delete[] arc;}
};
class Octagon : public Shape {
public:
Octagon(){arc=new Point[8]; ID='O';}
void CreateShape(){/*...*/}
/* .....*/
int GetArea(){/*вычисление конкретной площади*/return 8;};
~Octagon(){delete[] arc;}
};
class Operation
{ //Класс, инкапсулирующий методы обработки
public:
void Compare(Shape* s1, Shape* s2)
{// Проверка правильности подбора типов фигур
if ((s1->ID=='T' && s2->ID=='R')||(s2->ID=='T' && s1->ID=='R'))
cout<<"Correct choice"<<endl;
else {cout<<"Not such operation"<<endl; return;}
//Вычисления
if (s1->GetArea()> s2->GetArea()) cout<<"Area "<<s1->ID<<" > "<<s2->ID<<endl;
if (s1->GetArea()==s2->GetArea()) cout<<"Area "<<s1->ID<<" = "<<s2->ID<<endl;
if (s1->GetArea()< s2->GetArea()) cout<<"Area "<<s1->ID<<" < "<<s2->ID<<endl;
}
void IsInclude(Shape* s1, Shape* s2)
{/* */}
void IsIntersect(Shape* s1, Shape* s2)
{/* */}
};
class FactoryShape{// Класс–фабрика объектов, производных от Shape
public:
Shape* generator()
{ switch(rand() % 3) {
case 0:
return new Triangle;
case 1:
return new Rectangle;
case 2:
return new Octagon;
/* */
}
}
};
int _tmain(int argc, _TCHAR* argv[])
{
//Создаем 2 указателя на базовый класс
Shape *p1=0,*p2=0;
//Создаем заданные объекты
FactoryShape f;
cout<<"Input 1-t shape"<<endl;
char cond='y';
while (cond=='y')
{
if (p1) delete p1;
p1=f.generator();
cout<<p1->ID<<endl;
cout<<"Continue choice?(y/…)"<<endl;
cin>>cond;
}
p1->CreateShape();
cout<<"Input 2-d shape"<<endl;
cond='y';
while (cond=='y')
{if (p2) delete p2;
p2=f.generator();
cout<<p2->ID<<endl;
cout<<"Continue choice? (y/…)"<<endl;
cin>>cond;
}
p2->CreateShape();
Operation op;
op.Compare(p1,p2); //Сравниваем площади
if(p1) delete p1;
if(p2) delete p2;
return 0;
}
Замечания.
1. Можно для идентификации класса текущего объекта воспользоваться библиотекой typeinfo (#include <typeinfo>) и его компонентом typeid. Название класса выводится на консоль функцией typeid(*p).name(), где p-указатель на базовый класс.
2. Приведенный код является лишь схемой реализации задания; его можно и нужно изменять; в частности, можно для фабрики объектов использовать меню. Особо любознательные могут сделать т.н. обобщенный конструктор – запихнуть фабрику как статическую функцию в базовый класс.
Итак, конкретное задание подразумевает создание фабрики объектов для всех 7 фигур, случайное генерирование объектов, выбор 2-х из них согласно заданию и применение написанных только для этих двух классов функций класса Operation. Для остальных классов создаются только конструктор и заглушки методов.
Ответы на вопросы, связанные со свойствами геометрических фигур, можно получить в [3,4].