1) Расставьте std::cout таким образом, чтобы установить, в каком порядке и какие функции будут вызваны из программы
2) Скопмилируйте проект, набрав в консоли mingw32-make
3) Запустите test3.exe и ознакомьтесь с последовательностью выводимых сообщений
4) Объясните порядок вызова сообщений и последовательность действий
**************************************************************************************/
#include <iostream>
#include <string>
#include <vector>
class figure
{
protected:
int x, y;
public:
virtual ~figure()
{
}
void set_coords(int ax, int ay)
{
x = ax;
y = ay;
};
// Функция объявлена, как чисто виртуальная
// Ее невозможно вызвать, и объект с такой функцией не может существовать
// А значит не сможет существовать наследник, который ее не переопределит.
virtual void draw()=0;
protected:
// Конструктор объявлен в этой области для того, чтобы
// невозможно было создать переменную типа figure, так
// как единственный доступный конструктор находится
// в области, доступной только наследникам
figure()
{
}
// Добавим конструктор с параметрами
// Конструктор с параметрами позволяет
// передавать значения на этапе создания
// см. последний созданный объект
figure(int ax, int ay)
{
set_coords(ax, ay);
}
};
class point: public figure
{
protected:
// Это такой тип строковых переменных
std::string color;
public:
void set_color(const char *a_color)
{
color.assign(a_color);
}
void draw()
{
std::cout << "Точка с координатами (" << x << "; " << y << ") и цветом " << color.c_str() << std::endl;
}
point()
{
}
// после двоеточия мы указываем, что
// нужно вызвать конструктор figure, и передать в него параметры ax, ay
// Без этого указания вызвался бы конструктор figure() - см. задание 1
// Мы же указываем, что хотим принудительно вызвать именно figure(int ax, int ay)
point(int ax, int ay, const char* a_color):
figure(ax, ay)
{
set_color(a_color);
}
};
class rectangle: public point
{
private:
// Это такой тип строковых переменных
int side_a, side_b;
public:
void set_side(int a_side_a, int a_side_b)
{
side_a = a_side_a;
side_b = a_side_b;
}
void draw()
{
std::cout << "Прямоугольник с центром в (" << x << "; " << y << "), сторонами "
<< side_a << " и " << side_b << " и цветом " << color.c_str() << std::endl;
}
rectangle()
{
}
rectangle(int ax, int ay, const char *acolor, int asidea, int asideb):
point(ax, ay, acolor)
{
set_side(asidea, asideb);
}
protected:
};
class circle: public point
{
private:
// Это такой тип строковых переменных
int radius;
public:
void set_radius(int a_radius)
{
radius = a_radius;
}
// Обратите внимание, что все функции draw, кроме функции базового класса
// не объявляются, как виртуальные, потому что это и так подразумевается
// после первого объявления
void draw()
{
std::cout << "Окружность с центром в (" << x << "; " << y << "), радиусом " << radius << " и цветом " << color.c_str() << std::endl;
}
circle()
{
}
circle(int ax, int ay, const char * acolor, int aradius):
point(ax, ay, acolor)
{
set_radius(aradius);
}
};
void draw_figures(std::vector<figure *> &fig)
{
// Хочу обратить внимание на то, что эта функция ничего
// не знает о том, что за элементы ей переданы
// У нее есть просто обобщенный список фигур, о которых
// функция знает, что у них есть функция отрисовки, которая
// замещается в каждом наследнике.
// И еще есть функция смены координат (которую мы сейчас
// не используем, потому что наша задача только отрисовать)
for(unsigned int i = 0; i < fig.size(); i++)
fig[i]->draw();
// Таким образом можно работать с абстрактными объектами,
// у которых выделена некоторая асбтрактная характеристика
// Например, можно сделать цепочку принципиально разных фильтров
// о которых известно, что есть функция "фильтровать"
// А все остальные настройки выполняются в том месте программы,
// в котором известно о детальном алгоритме, по которому фильтр
// работает, и о том, какие настройки можно для него сделать
// По сути мы заменили серию вызовов
// p1.draw()
// c1.draw()
// r1.draw()
// p2.draw()
// r2.draw()
// ... (сколько еще фигур мы бы захотели добавить)
// на две строчки - цикл и вызов функции по указателю на базовый класс
// То есть мы смогли объединить разные объекты (которые мы бы не смогли
// объединить иным образом) в единый список объектов.
}
int main()
{
// Мы не сможем создать фигуру (можете проверить)
// Но мы можем создать массив указателей на фигуры
// Вектор - это такой тип данных, который позволяет
// создавать массив переменной длины
// То есть вместо
// figure *figure_array[100] = {0};
// мы можем просто сказать, что у нас будет массив указателей
// на класс фигура, и размер определить в процессе выполнения программы
std::cout << "создаем переменную figures типа вектор, элементы которого типа figure" << std::endl;
std::vector<figure *> figures;
std::cout << "создаем переменную типа point" << std::endl;
point p1;
std::cout << "задаем для ней координаты - вызываем соответствующую функцию класса point" << std::endl;
p1.set_coords(20, 40);
std::cout << "задаем цвет - вызываем соответствующую функцию класса point" << std::endl;
p1.set_color("красный");
std::cout << "записываем в массив данный элемент" << std::endl;
// Добавляем указатель на точку в массив
figures.push_back(&p1);
std::cout << "тоже самое для circle" << std::endl;
circle c1;
c1.set_coords(100, 20);
c1.set_radius(100);
c1.set_color("синий");
figures.push_back(&c1);
std::cout << "тоже самое для rectangle" << std::endl;
rectangle r1;
r1.set_coords(20, 80);
r1.set_side(40, 50);
r1.set_color("желтый");
figures.push_back(&r1);
std::cout << "тоже самое для point" << std::endl;
point p2;
p2.set_coords(10, 80);
p2.set_color("черный");
figures.push_back(&p2);
// Можно упростить процесс создания, если использовать конструкторы с аргументами
rectangle r2(1, 2, "фиолетовый", 3, 4);
figures.push_back(&r2);
std::cout << "рисуем фигуры" << std::endl;
draw_figures(figures);
// Так же хочу обратить внимание на то, каким образом механизм
// наследования позволяет убрать дублирование кода.
//
return 0;
}