- •Оглавление
- •Всё остальное – стр. № 33.
- •Цель работы
- •Основные сведения
- •Варианты заданий
- •В следующих вариантах рекомендуется создавать 2 класса и объекты или указатели на один из них включать в другой (агрегация классов)
- •Контрольные вопросы
- •Цель работы
- •Основные сведения
- •Варианты заданий
- •Контрольные вопросы
- •Цель работы.
- •Основные сведения
- •Варианты заданий
- •Контрольные вопросы
- •Цель работы.
- •Основные сведения
- •Варианты заданий
- •Контрольные вопросы
- •Лабораторная работа 5. Классы и объекты в с# Цель работы
- •Основные сведения
- •Контрольные вопросы
- •Требования к отчетам
- •Литература
- •Приложения
- •1.Создание консольного приложения
Контрольные вопросы
В чем заключается наследование одного класса другому? В чем разница в организации
наследования полей и методов?
Удачной ли является иерархия классов, при которой некоторый класс Х является производным от большого количества классов с большим числом полей в каждом ( AB C … X)? Какая существует альтернатива наследованию?
Каким образом производится управление доступом к унаследованным компонентам производных классов? Есть ли в представленном фрагменте программы ошибки?
class Base {
int y;
public:
void setx(int n){x=n;}
void showx() const{ cout <<x<<endl;}
};
class Derived: public Base {
int x;
public:
void sety(int n){y=n;}
void show_sum() const{ cout<<x+y<<endl; }
void show() const {cout<<y<<endl; }
};
Есть ли ошибки в нижеследующих объявлениях:
Shape sh; // класс Shape абстрактный
Shape *psh;
Shape *psh1=new Shape();
Лабораторная работа 3. Виртуальные функции в С++
Цель работы.
Ознакомление с механизмом позднего связывания виртуальных функций.
Основные сведения
Виртуальные функции
Продолжим обсуждение виртуальных функций, начатый в предыдущей работе. Когда говорят о вызове виртуальных функций, обычно имеют в виду т.н. позднее связывание объекта производного класса с этой функцией. При обычном связывании через заранее созданный объект класса адрес функции известен на стадии компиляции. При позднем связывании адреса виртуальных функций заносятся в создаваемую для каждого класса при выполнении программы таблицу vftable, поэтому адрес функции в одной и той же точке программы может быть различным в зависимости от значения указателя, обращающегося к таблице. Т.е. конкретная реализация виртуальной функции определяется динамически во время выполнения программы.
Указателем, обращающимся к таблице, является указатель на базовый класс. Его значением может быть адрес объекта любого класса иерархии.
Очень часто класс, содержащей виртуальный метод, называют полиморфным классом. Главная особенность заключается в том, что полиморфные классы допускают обработку объектов, тип которых неизвестен во время компиляции. Если в производном классе виртуальный метод не будет переопределен, то при вызове будет найден метод с таким именем вверх по иерархии классов (т.е. в базовом классе). Схема использования виртуальных функций приведена ниже.
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 не определена
};
void main()
{
Derived1 d1;
cout<< d1.fb(); //Позднее связывание не работает
//Правильный вызов
Based *pb=0;
pb=new Derived1;
cout<<pb->fb(); // возвращает 5
delete pb;
pb=new Derived2;
cout<<pb->fb(); // возвращает 7
delete pb;
pb=new Derived3;
cout<<pb->fb(); // возвращает 0
if(pb) delete pb;
}
В лабораторной работе для изучения механизма работы виртуальных функций используется идея паттерна проектирования программ под названием «Фабричный метод» (Faсtory Method) [3]. Паттерн используется в тех случаях, когда выбор класса, объект которого надо создавать, делается в процессе выполнения программы.
Здесь он применяется в программе, работающей с объектами двух разных классов T1 и T2, которые являются производными классами одного базового класса Shape.
Производные классы должны создавать плоские объекты следующих типов: квадрат, треугольник, прямоугольник, параллелограмм, трапеция, правильный шестиугольник, правильный восьмиугольник (square, triangle, rectangle, parallelogram, trapeze, hexagon, octagon). Для каждого типа фигуры вычисляются площадь, центр тяжести, радиусы вписанных или описанных окружностей и другие атрибуты [5], а также должны быть предусмотрены виртуальные методы Вращения (Rotate) и Перемещения (Move).
Операции над объектами этих классов могут быть такими (функции с 2-мя аргументами Shape* obj1, Shape* obj2):
сравнить два объекта по площади - Compare;
определить факт пересечения объектов – IsIntersect;
определить факт включения одного объекта в другой – IsInclude.
Алгоритмы для реализации этих функций есть в любом учебнике по компьютерной графике. Если вас затрудняет реализация этих алгоритмов, то я разрешаю вместо них сделать следующее. Введем область вращения фигуры, представляющую из себя окружность, описанную вокруг нее, там, где это возможно, или проведенную из центра тяжести с радиусом, равным расстоянию от него до наиболее удаленной точки фигуры. Теперь перечисленные операции можно реализовывать не с объектами, а с их областями вращения.
Ниже приводится рекомендуемый вариант программы (упрощенный). В ней из трех фигур выбираются и обрабатываются две – объекты Triangle и Rectangle.
#include "stdafx.h"
#include <iostream>
using namespace std;
class Point{
public:int x,y;
};
class Shape { // Абстрактный базовый класс
public:
Point *arc;
char ID;
/* Поля и методы */
virtual int GetArea()=0;//Вычисление площади фигуры
virtual void CreateShape()=0;
virtual ~Shape(){}
};
class Rectangle : public Shape {
public:
void CreateShape(){cout<<"Input Rectangle coordinate points "<<endl;
cin>>arc[0].x; /*вводим все координаты*/}
/* методы*/
int GetArea(){ /*вычисление конкретной площади*/return 4;}
Rectangle (){arc=new Point[4]; ID='R';}
~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;
else 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;
}
Замечание. Можно для идентификации класса текущего объекта воспользоваться библиотекой typeinfo (#include <typeinfo>) и его компонентой typeid. Название класса выводится
typeid(*p).name(), где p=указатель на базовый класс.
Итак, конкретное задание подразумевает создание фабрики объектов для всех 7 фигур, выбором 2-х из них согласно заданию и написание для них функций класса Operation.
В этой работе я не требую вводить какие-либо классы для упрощения main, так как главное в ней – разобраться с механизмом вызова виртуальных функций.
