Welcome to WordPress.com! This is your very first post. Click the Edit link to modify or delete it, or start a new post. If you like, use this post to tell readers why you started this blog and what you plan to do with it.
Happy blogging!
Welcome to WordPress.com! This is your very first post. Click the Edit link to modify or delete it, or start a new post. If you like, use this post to tell readers why you started this blog and what you plan to do with it.
Happy blogging!
Программирование графики всегда предполагает то, что вы знакомы с геометрией. Чем больше у вас геометрических знаний, тем проще ориентироваться в программировании графики. Рассматривая graphics.h иногда полезно попробовать создать примитив. Так как обучающие материалы предполагают простоту кода, то будет рассмотрен примитивный пример рисования треугольника по заданным координатам.
Требуются знания
С++ для начинающих Классы Первое знакомство
Передача параметров в функции в C++ для начинающих
С++ для начинающих Конструктор класса Передача параметров в базовый конструктор класса
Я покажу два способа создания треугольника по заданным координатам. Это достаточно простой для понимания урок и полезный для получения практических знаний.
Нарисовать треугольник можно чистым контуром, проведя три линии, а можно использовать функцию drawpoly, рисующую многоугольник. В обоих случаях код получается достаточно компактным и каждый волен решать что ему больше подходит
Шаг первый. Анализ и Мозговой штурм.
Детали объекта скрыты от прямого доступа извне, но нам нужно передавать в них данные. Наши данные – это указания о месте положения точек. Чтобы передать эти данные, используется конструктор с параметрами
Если все, что написано понятно, то
Triangle::Triangle(pointtype A,pointtype B,pointtype C) //Я описываю конструктор вне класса
{
a=A; //Копирование данных из структуры A в a и далее по аналогии
b=B;
c=C;
}
================
Так при помощи конструктора, принимаемые в объект параметры будут передаваться в детали объекта. Теперь осталось описать поведение объекта и научить треугольник рисовать себя. Я упоминал, что покажу два способа. Первый способ – рисование линиями:
Triangle::Triangle(pointtype A,pointtype B,pointtype C) //Я описываю конструктор вне класса
{
a=A; //Копирование данных из структуры A в a и далее по аналогии
b=B;
c=C;
}
void Triangle::Show() //Описываю метод Show вне класса
{
moveto(a.x,a.y); //Для начала рисования определяем начальную точку
lineto(b,x,b.y); //Нарисовали сторону ab треугольника
lineto(c.x,c.y); //Нарисовали сторону bc треугольника
lineto(a.x,a.y); //Нарисовали сторону ac треугольника
}
================
Triangle::Triangle(pointtype A,pointtype B,pointtype C) //Я описываю конструктор вне класса
{
a=A; //Копирование данных из структуры A в a и далее по аналогии
b=B;
c=C;
}
void Triangle::Show() //Описываю метод Show вне класса
{
int Poly[8]={a.x,a.y,b.x,b.y,c.x,c.y,a.x,a.y} ; //Инициализация массива параметрами переданными в объект
drawpoly(4,Poly); //Рисование треугольника по данным, записанным в массив
}
================
Функция drawpoly может использоваться для рисования любых многоугольников. Когда мы к ней обращаемся, первым параметром нужно указать количество сторон многоугольника плюс один, вторым параметром массив, хранящий данные координат. Параметр сторон на один больше потому что последним параметром контур многоугольника замыкается. Если посмотреть на первый способ, то видно, что необходимо четыре функции, тут также
Последний шаг – Объявление объекта треугольник, передача ему параметров и Рисование треугольника по указанным координатам
graphics.h Нарисовать треугольник по заданным координатам (Способ2)
================
#include <iostream.h>
#include <graphics.h>
class Triangle
{
pointtype a,b,c; //Детали треугольника = три точки для построения
public:
Triangle(pointtype,pointtype,pointtype); //Прототип конструктора с параметрами
void Show(); //Метод, помогающий понять треугольнику, как себя рисовать
};
void Triangle::Show() //Описываю метод Show вне класса
{
int Poly[8]={a.x,a.y,b.x,b.y,c.x,c.y,a.x,a.y} ; //Инициализация массива параметрами переданными в объект
drawpoly(4,Poly); //Рисование треугольника по данным, записанным в массив
}
void main()
{
int gd=DETECT,gm;
initgraph(&gd,&gm,“”);
pointtype a,b,c; //Три точки треугольника
a.x=20; a.y=20;
b.x=100; b.y=90;
c.x=33; c.y=88;
Triangle Tr1(a,b,c); //Объявили объект, типа треугольник
Tr1.Show(); //Заставили объект нарисовать себя по указанным координатам
class Triangle
{
pointtype a,b,c; //Детали треугольника = три точки для построения
public:
void Show(pointtype,pointtype,pointtype); //Метод, помогающий понять треугольнику, как себя рисовать
};
void main()
{
int gd=DETECT,gm;
initgraph(&gd,&gm,“”);
pointtype a,b,c; //Три точки треугольника
a.x=20; a.y=20;
b.x=100; b.y=90;
c.x=33; c.y=88;
Triangle Tr1; //Объявили объект, типа треугольник
Tr1.Show(a,b,c); //Заставили объект нарисовать себя по указанным координатам
Требуется хорошо понимать
Передача параметров в функции в C++ для начинающих
С++ для начинающих Классы Первое знакомство
С++ для начинающих Конструктор Класса
С++ для начинающих Конструктор класса Передача параметров в базовый конструктор класса
C++ для начинающих private, public, protected
Своё знакомство с графикой я начал с геометрических примеров, в которых используются алгоритмы на базе знаний геометрии. Очень часто требуется нарисовать простой рисунок, что-то типа дом, дерево, солнце. С такого часть программистов начинает понимать графику. Меня кто-то тут в комментариях попросил рассмотреть graphics.h, поэтому я решил выполнить просьбу и начал периодически писать о создании графики в C++ под Dos.
Перед тем, чтобы нарисовать рисунок нужно его хотя бы представлять в общих чертах. Можно нарисовать сразу, можно срисовать с другого места. Честно говоря по жизни я рисовать не умею, но в Paint примитив рисовать могу, поэтому первым делом я нарисовал рисунок в PaintNet
=======
=======
Это то, что у меня получилось. Тут видно дом, солнце, линию горизонта, три дерева и озеро. Дом состоит из таких частей как окна, крыша, двери. Деревья имеют ветви, листву и ствол. Другими словами мы имеем объекты – Дом, дерево, Солнце, озеро. Для того чтобы с объектами было проще работать можно использовать объектно-ориентированный подход к рисованию. При рисовании рисунка я буду использовать классы. Никаких усложнений постараюсь не делать
Тот, кто имеет знания о рисовании в Paint, тот знает, что сначала нужно рисовать фон. Здесь точно также, чтобы фоновая картинка не затирала остальную, нужно создать фон. На моем рисунке фон это небо, солнце, трава. Оно не раскрашено, но обозначена линия горизонта, которая разделяет небо и землю.
Приступаем к написанию программы, создающей фоновый узор. Скорее всего читатель и без моей помощи может это сделать, но я опишу все по порядку.
Код С++ graphics.h Создание фона
======================
#include <stdlib.h>
#include <iostream.h>
#include <graphics.h>
class Fon //СОЗДАЛ КЛАСС ФОН
{
public:
Fon(); //КОНСТРУКТОР
};
Fon::Fon() //ВЫЗОВ КОНСТРУКТОРА ВНЕ КЛАССА
{
setfillstyle(6,GREEN); //Установка стиля заливки
bar(0,0,getmaxx(),getmaxy()); //Рисую залитый прямоугольник. Заливаю все зеленым
setfillstyle(1,1); //Установка стиля заливки
bar(0,0,getmaxx(),150); //Рисую залитый прямоугольник поверх первого. Получил Небо
setfillstyle(1,14); //Установка стиля заливки
fillellipse(600,50,50,50); //Рисую залитый эллипс. Получил Солнце
setfillstyle(1,1); //Установка стиля заливки
fillellipse(500,390,200,50); //Рисую залитый эллипс. Получил Озеро
};
void main()
{
int gm,gd=DETECT; //Переменные для инициализации графики
initgraph(&gd,&gm,“”); //Поиск нужного графического драйвера
Fon Fon1; //Объявлю переменную типа Фон
cin.get();
closegraph();
}
=============
После запуска получил такую картинку.
===========
===========
Кто-то, может, не поймет, почему я, только объявив картинку смог ее нарисовать. В классе описан только один метод и метод – этот конструктор. Конструктор выполняется раньше, чем выполняется функция main, поэтому так выходит.
После нарисованного фона нужно приступать к основным объектам картинки. Я решил начать с рисования дерева. Честно признаюсь – рисование дерева отняло у меня некоторое время. Подобный рисунок я рисовал впервые и поэтому при рисовании дерева я подгонял точку за точкой.
moveto – перемещает перо на указанные координаты. Позиция пера – это начало рисования.
lineto – рисует линию из начала рисования на указанные координаты.
Всмотритесь в дерево, оно у меня нарисовано линиями и овалами с кругами. Вот первым делом я рисовал дерево без листвы. Чтобы мне было немного проще подгонять точку к точке, я сначала перемещал перо в приблизительное место нужной точки и рисовал линию оттуда и на координаты (0,0). Ни разу не попал сразу, поэтому приходилось подгонять вверх, вниз, вправо и влево. Но когда точка попадала в нужное место ствола или ветки, я начинал подгонять те координаты, которые (0,0).
Проще и понятнее: Сначала брался за первую точку отрезка и пытался поставить ее в нужное место, потом за вторую. Таким образом, я смог получить дерево без листвы очень похожее на дерево с моего рисунка.
Вы можете нарисовать свое дерево на бумаге (или в компе), одни ветки без листьев и попытаться срисовать его. Так вы лучше поймете, о чем я толковал. Следующий этап рисование дереву листвы. С листвой тоже немного нужно повозиться, подгоняя её в нужные места. Но в отличии от линии, для листика дерева можно использовать окружность или овал, а их местоположение определяет одна точка., подогнать листья немного проще.
Сам код рисования дерева на моем фоне получился таким
Код С++ graphics.h Дерево на фоне
==============
#include <stdlib.h>
#include <iostream.h>
#include <graphics.h>
class Fon
{
public:
Fon();
};
Fon::Fon()
{
setfillstyle(6,GREEN);
bar(0,0,getmaxx(),getmaxy());
setfillstyle(1,1);
bar(0,0,getmaxx(),150);
setfillstyle(1,14);
fillellipse(600,50,50,50);
setfillstyle(1,1);
fillellipse(500,390,200,50);
};
/*КЛАСС ДЕРЕВО*/
class Tree
{
int x,y; //Координаты начала роста
public:
Tree(int X,int Y) {x=X;y=Y;} //Конструктор с параметрами
void ShowTree(); //Метод, отображение дерева на экране
};
/*ОПИСЫВАЮ МЕТОД РИСОВАНИЯ ДЕРЕВА ВНЕ КЛАССА*/
void Tree::ShowTree()
{
y=y+getmaxy();
//Рисую ствол и ветви
moveto(x,y–150); lineto(x,y–300);
moveto(x,y-(150+75)); lineto(x+70,y-(150+75)-70);
moveto(x+20,y-(150+75)-20); lineto(x+70,y-(150+75)-20);
moveto(x+40,y-(150+75)-40); lineto(x+45,y-(150+75)-70);
moveto(x,y-(150+75)-40); lineto(x+20,y-(150+75)-65);
moveto(x,y-(150+75)-10); lineto(x–30,y-(150+75)-20);
moveto(x,y-(150+75)-30); lineto(x–25,y-(150+75)-40);
//Рисую листву
setfillstyle(1,GREEN);
fillellipse(x,y–300,5,5);
fillellipse(x+20,y–290,7,7);
fillellipse(x+45,y–300,3,7);
fillellipse(x+75,y–300,10,10);
fillellipse(x+20,y–255,5,10);
fillellipse(x+70,y–247,10,10);
fillellipse(x–30,y–247,10,10);
fillellipse(x–30,y–267,7,7);
fillellipse(x–15,y–270,5,9);
}
void main()
{
int gm,gd=DETECT;
initgraph(&gd,&gm,“”);
Fon Fon1; //Рисование фона с помощью конструктора
Tree Derevo1(50,50); //Объявление объекта типа Дерево
Derevo1.ShowTree(); //Рисование Дерева
cin.get();
closegraph();
}
==============
Мне трудно досконально описать метод рисования дерева на экране, как я уже написал, я подгонял точку за точкой, но, надеюсь, несмотря на это, вы понимаете, как это сделано. Мне пришлось запускать программу достаточно много раз, чтобы все точки поставить в нужные места.
После очередного запуска, я получил картинку
===========
===========
Если сравнивать с моим изначально подготовленным рисунком, то заметно небольшое сходство.
Благодаря тому, что существует объектно-ориентированный подход к программированию, мне не нужно мучаться с координатами других деревьев. Вы наверняка увидели, что в моем коде использованы обозначения координат (x,y) вместо обычных цифр. Эти координаты объявлены внутри класса и по идее не доступны из функции main, но конструктор принимает (X,Y), которые потом передаются в (x,y), поэтому не прямой, но доступ к ним есть. При объявлении объекта тип дерево мы указываем два параметра для такой передачи. (У точки на плоскости две координаты). Теперь можно рисовать много деревьев в разных участках экрана. Кодом я показывать пока не буду, кто-то может сам поймет, просто нужно рисовать следующий объект – Дом.
Дом состоит из целого набора деталей (окна, двери, крыша) Каждый нужно нарисовать, причем чтобы получить чуть более чем простой рисунок нам нужно рисовать относительно точек, а не просто по цифрам. Такой подход у меня тоже занял некоторое время, объяснить все у меня, наверное, не получится, но основную идею я изложу
Сначала я пробовал рисовать простые примитивы типа rectangle, контур линиями, но при заливке возникли проблемы, поэтому использованы bar3d, drawpoly. Bar3d рисует прямоугольник, а drawpoly многоугольник. Если всмотреться в дом на картинке, то можно увидеть такие многоугольники как параллелограмм и треугольник. Чтобы мне было немного проще находить координаты чем тогда когда я находил координаты с деревом, я решил выводить на экран сначала textxy(x,y,”TEXT”). Аналогично дереву мне нужно плясать от какой-то точки, поэтому я подгонял эти точки в приблизительные места и только потом рисовал многоугольники. Я это оставил в комментариях кода. Подгонял я разумеется с многократным запуском программы и изменениями координат вывода текста. В этом заключается основная идея моего подхода
Код С++ graphics.h Дом, дерево, солнце
================
#include <stdlib.h>
#include <iostream.h>
#include <graphics.h>
class Fon
{
public:
Fon();
};
Fon::Fon()
{
setfillstyle(6,GREEN);
bar(0,0,getmaxx(),getmaxy());
setfillstyle(1,1);
bar(0,0,getmaxx(),150);
setfillstyle(1,14);
fillellipse(600,50,50,50);
setfillstyle(1,1);
fillellipse(500,390,200,50);
};
class Tree
{
int x,y;
public:
Tree(int X,int Y) {x=X;y=Y;}
void ShowTree();
};
/*ДОБАВИЛИ КЛАСС ДОМ*/
class Dom
{
int x,y; //Координаты начала рисования дома
public:
Dom(int X,int Y) {x=X;y=Y;} //Конструктор класса принимает параметры
void ShowHome(); //Метод класса, рисующий Дом
};
void Tree::ShowTree()
{
y=y+getmaxy();
//Рисую ствол и ветви
moveto(x,y–150); lineto(x,y–300);
moveto(x,y-(150+75)); lineto(x+70,y-(150+75)-70);
moveto(x+20,y-(150+75)-20); lineto(x+70,y-(150+75)-20);
moveto(x+40,y-(150+75)-40); lineto(x+45,y-(150+75)-70);
moveto(x,y-(150+75)-40); lineto(x+20,y-(150+75)-65);
moveto(x,y-(150+75)-10); lineto(x–30,y-(150+75)-20);
moveto(x,y-(150+75)-30); lineto(x–25,y-(150+75)-40);
//Рисую листву
setfillstyle(1,GREEN);
fillellipse(x,y–300,5,5);
fillellipse(x+20,y–290,7,7);
fillellipse(x+45,y–300,3,7);
fillellipse(x+75,y–300,10,10);
fillellipse(x+20,y–255,5,10);
fillellipse(x+70,y–247,10,10);
fillellipse(x–30,y–247,10,10);
fillellipse(x–30,y–267,7,7);
fillellipse(x–15,y–270,5,9);
}
/*МЕТОД РИСОВАНИЯ ДОМА ОПИСАН ВНЕ КЛАССА*/
void Dom::ShowHome()
{
setfillstyle(3,3); //Установка стиля заливки
bar3d(x–150,y,x–50,y–120,2,1); //Рисую фасад дома
//Треугольная часть крыши
int poly[8]; //3 вершины*2 = 6. Нужна точка замыкания 6+2 =8
poly[0]=x–155; poly[1]=y–120; //A(x,y)
poly[2]=x–100; poly[3]=y–200; //B(x,y)
poly[4]=x–45; poly[5]=y–120; //C(x,y)
poly[6]=poly[0];poly[7]=poly[1]; //Замыкание контура
setfillstyle(5,1); //Установка стиля заливки
fillpoly(3,poly); //Рисую залитый треугольник
//Параллелограммная часть крыши
/*Поиск точек для параллелограмма
outtextxy(x-45,y-120,”A”);
outtextxy(x+100,y-120,”B”);
outtextxy(x-100,y-200,”C”);
outtextxy(x+100-45,y-200,”D”); */
int poly1[10]; //4 точки параллелограма*2=8 + точка для замыкания =8+2=10
poly1[0]=x–45; poly1[1]=y–120; //A
poly1[2]=x+100; poly1[3]=y–120; //B
poly1[4]=x+100–45; poly1[5]=y–200; //D
poly1[6]=x–100; poly1[7]=y–200; //C
poly1[8]=poly1[0]; poly1[9]=poly1[1]; //Замыкание контура
setfillstyle(2,1); //Установка стиля заливки
fillpoly(4,poly1); //Рисование залитого четырехугольника
/*БОКОВАЯ СТЕНА ДОМА*/
/*Поиск точек
outtextxy(x-45,y,”A”);
outtextxy(x+100,y,”B”);
outtextxy(x+100,y-120,”C”);
outtextxy(x-45,y-120,”D”); */
setfillstyle(10,2); //Установка стиля заливки
bar3d(x–45,y,x+100,y–120,1,1); //Залитый прямоугольник
//ПОИСК И РИСОВАНИЕ ОКОН
/*outtextxy(x-30,y-50,”A”);
outtextxy(x-30,y-80,”B”);
outtextxy(x,y-80,”C”); */
setfillstyle(1,7); //Установка стиля заливки
bar3d(x–30,y–50,x,y–80,1,1); //Рисование первого окна
/*outtextxy(x+15,y-50,”A”);
outtextxy(x+45,y-80,”B”);*/
bar3d(x+15,y–50,x+45,y–80,1,1); //Рисование второго окна
/*outtextxy(x+60,y-50,”A”);
outtextxy(x+90,y-80,”B”); */
setfillstyle(5,YELLOW); //Установка стиля заливки
bar3d(x+60,y–50,x+90,y–80,1,1); //Рисование третьего окна
/*ПОИСК КООРДИНАТ И РИСОВАНИЕ ДВЕРЕЙ
outtextxy(x-70,y,”A”);
outtextxy(x-100,y-70,”B”);*/
setfillstyle(2,6); //Установка стиля заливки
bar3d(x–70,y,x–100,y–70,1,1); //Рисование двери
}
void main()
{
int gm,gd=DETECT;
initgraph(&gd,&gm,“”);
Fon Fon1; //Рисование фона с помощью конструктора
Tree Derevo1(50,50); //Создание объекта типа Дерево
Derevo1.ShowTree(); //Рисования дерева
Dom Dom1(400,300); //Создание объекта типа Дом
Dom1.ShowHome(); //Рисование дома
cin.get();
closegraph();
}
================
После запуска получилась картинка.
Несмотря на то что этот подход к рисованию труднее понять чем простая подстановка координат, он все-таки лучше. Теперь я могу создавать дом в любом месте экрана как и дерево. Если нужен пример, то пожалуйста. Изменяем функцию main, остальное все остается как написано и получаем что-то типа
Код C++ graphics.h Дом, дерево, Солнце
==============
void main()
{
int gm,gd=DETECT;
initgraph(&gd,&gm,“”);
Fon Fon1; //Рисование фона с помощью конструктора
Tree Derevo1(50,50); //Объяевляем Объект Дерево1
Tree Derevo2(150,150); //Объявляем объект дерево2
Tree Derevo3(300,100); //Объявляем объект Дерево3
Tree Derevo4(400,-100); //Объявляем объект Дерево4
Dom Dom1(400,300); //Объявляем объект Дом один
Dom Dom2(150,380); //Объявляем объект Дом два
Derevo4.ShowTree(); //Рисуем дерево за домами
Dom1.ShowHome(); //Рисуем первый дом
Dom2.ShowHome(); //Рисуем второй дом
Derevo1.ShowTree(); //Рисуем дерево перед домами
Derevo2.ShowTree(); //Рисуем дерево перед домами
Derevo3.ShowTree(); //Рисуем дерево перед домами
cin.get();
closegraph();
}
==============
Получили рисунок
Вот такое вот нелегкое начало рисования приводит к достаточно простому созданию копий. При всем при этом, такие начинания являются первым шагом к пониманию анимации изображений
Я давно написал и думал, что выложил этот исходник рисования правильной звезды с указанным количеством концов, но сегодня заметил, что этой статьи нет. Правильную n-конечную звезду я смог нарисовать своими собственными силами в то время, когда разбирался с правильным n-угольником. За пройденное время у меня появились статьи других разделов и я уже немного подзабыл что тут и как. Но я считаю нужным поделится моим примером с моими читателями.
Программирование Правильная n-конечная звезда
==========
#include <stdlib.h>
#include <iostream.h>
#include <graphics.h> //Для работы с графическими функциями
#include <math.h> //Для работы с тригонометрическими функциями
/*ФУНКЦИЯ ПОСТРОЕНИЯ ЗВЕЗДЫ*/
void star(float R,float r,float n)
{
float a=0;
pointtype *p=new pointtype [n*2+1]; //Массив для хранения координат вершин звезды
float x=getmaxx()/2; //Расчет центра по х
float y=getmaxy()/2; //Расчет центра по y
//Цикл расчета вершин звезды
for (int i=1;i<n*2+2;i++)
{
if (!(i%2)) //При выполнении условия четности следующие формулы
{
p[i].x=x+r/2*cos(a*M_PI/180);
p[i].y=y–r/2*sin(a*M_PI/180);
}
else //При невыполнении условия четности следующие формулы
{
p[i].x=x+R*cos(a*M_PI/180);
p[i].y=y–R*sin(a*M_PI/180);
}
a=a+180/n;
}//Завершаем построение звезды соединяя её окончание с начальной точкой
p[n*2+1].x=p[1].x;
p[n*2+1].y=p[1].y;
moveto(p[1].x,p[1].y);
//Последовательное соединение точек массива, хранящего вершины звезды
for (i=1;i<n*2+2;i++)
{
lineto(p[i].x,p[i].y);
}
delete []p; //Освобождаем память
}
//Функция ввода двух радиусов и числа концов
void input()
{
float R,r,n; //Внешний и внутренний радиусы
cout<<“Внутренний радиус = “;cin>>r;
cout<<“Внешний радиус = “;cin>>R;
cout<<“Число концов (n) = “;cin>>n;
star(R,r,n); //Построение звезды по радиусам
}
void main()
{
system(“CLS”);//ПОДГОТОВКА РАБОТЫ С ГРАФИКОЙ
int gdriver = DETECT, gmode, errorcode;
initgraph(&gdriver, &gmode, “”);
input(); //Ввод параметров звезды с последующим её построением
system(“PAUSE”);
return;
}
=============
Сначала вызывается функция ввода данных, пользователь вводит параметры правильной n конечной звезды и из функции ввода происходит вызов функции рисования этой правильной n конечной звезды. Помню,что у меня проблема была при рисовании правильного n-угольника связанная с типами данных (там об этом упомянуто), поэтому призываю быть внимательными при объявлении переменных и назначений им типов. В компьютерной графике хорошо заметно, что что-то не так. Можете поиграть с типами данных. Я не долго думая всё заменил на float, но это только чтобы самому себе сэкономить время.
M_PI – это число ПИ = 3,14 и определено в math.h
Звезда – выпуклый многоугольник, поэтому неудивительно, что я смог решить тогда когда этим занимался
Надеюсь этот материал поможет вам в ваших начинаниях. Этот код позволяет строить хоть трех конечную, хоть сто конечную правильную звезду.
==========
Давайте вспомним одно свойство массива. Массив – это набор однотипных данных. Может показаться, что использование наследования и виртуальных функций немного нарушает такое определение. Если размышлять, то можно прийти к мыслям: “Если массив содержит данные только одного типа, то значит в один массив объекты разных типов мне поместить не удастся”. Это совершенно верные мысли. Несмотря на это любой самый обычный массив можно построить таким образом, что элементы, обрабатываемые с помощью него будут иметь различные типы. Может показаться, что я противоречу сам себе, но вовсе нет. Для того, чтобы построить такой массив с элементами различных типов используют массив указателей. Каждый элемент такого массива – это указатель.
Перехожу от слов к делу.
Задача: Создать массив объектов класса млекопитающие
Предположим у нас есть 3 вида млекопитающих и остальные неизвестные нам. Нам известны собака, кот и свинья. Каждое млекопитающее издает собственные звуки. Кот мяукает, собака лает, свинья хрюкает.
Создадим базовый класс – млекопитающие, от которого унаследуем виды млекопитающих и определим для них уникальное поведение (как нам уже известно каждое млекопитающее должно издавать свой звук)
Код C++ Виртуальные функции Динамический полиморфизм
================
/*КЛАСС-РОДИТЕЛЬ*/
class Mammal
{
public:
virtual void Speak() {cout<<“Mammal Speak\n”;} //Виртуальный метод. Звук неизвестного млекопитающего
};
/*СОЗДАЕМ ПОДКЛАСС МЛЕКОПИТАЮЩЕГО*/
class Dog:public Mammal
{
public:
void Speak() {cout<<“GavGav\n”;} //Виртуальный метод. Собака лает
};
/*СОЗДАЕМ ПОДКЛАСС МЛЕКОПИТАЮЩЕГО*/
class Cat:public Mammal
{
public:
void Speak() {cout<<“Meow\n”;} //Виртуальный метод. Кот мяукает
};
/*СОЗДАЕМ ПОДКЛАСС МЛЕКОПИТАЮЩЕГО*/
class Pig:public Mammal
{
public:
void Speak() {cout<<“HruHru\n”;} //Виртуальный метод. Свинья хрюкает
};
================
Применены приемы наследования. Создано три потомка из основного класса. Каждый потомок – это известное нам млекопитающее
После того как описали нужные действия приступаем к написанию основного кода. Дадим пользователю возможность заносить млекопитающих в массив в том порядке и тех млекопитающих, которых он сам хочет.
Код С++ Виртульные функции Динамический полиморфизм
================
#include <iostream.h>
#include <stdlib.h>
/*КЛАСС-РОДИТЕЛЬ*/
class Mammal
{
public:
virtual void Speak() {cout<<“Mammal Speak\n”;} //Виртуальный метод. Звук неизвестного млекопитающего
};
/*СОЗДАЕМ ПОДКЛАСС МЛЕКОПИТАЮЩЕГО*/
class Dog:public Mammal
{
public:
void Speak() {cout<<“GavGav\n”;} //Виртуальный метод. Собака лает
};
/*СОЗДАЕМ ПОДКЛАСС МЛЕКОПИТАЮЩЕГО*/
class Cat:public Mammal
{
public:
void Speak() {cout<<“Meow\n”;} //Виртуальный метод. Кот мяукает
};
/*СОЗДАЕМ ПОДКЛАСС МЛЕКОПИТАЮЩЕГО*/
class Pig:public Mammal
{
public:
void Speak() {cout<<“HruHru\n”;} //Виртуальный метод. Свинья хрюкает
};
/*==================*/
void main()
{
system(“CLS”);
Mammal *Array[5]; //Объявляем массив указателей на класс Млекопитающие
Mammal *ptr; //Объявили указатель на класс Млекопитающие
int MyWibor; //Переменная для выбора пользователем
//Небольшое украшательство
cout<<“Выбери млекопитающее: \n”;
cout<<“1 – СОБАКА\n”;
cout<<“2 – КОШКА\n”;
cout<<“3 – СВИНЬЯ\n\n”;
//С помощью цикла заполняем массив указателями на объекты
for (int i=0;i<5;i++) //Пусть млекопитающих пять
{
cout<<i+1<<“. “; //Номер текущего млекопитающего
cin>>MyWibor; //Пользователь вводит число
switch (MyWibor) //Основываясь на введенном числе выделяем память для нужного млекопитающего
{
case 1:
ptr=new Dog; //Если один – выделяем память для создания класса Собака
break;
case 2:
ptr=new Cat; //Если два – выделяем память для создания класса Кошка
break;
case 3:
ptr=new Pig; //Если три – выделяем память для создания класса Свинья
break;
default:
ptr=new Mammal; //Если что-то другое – выделяем память для создания класса Млекопитающее
break;
}//switch Выбор сделан
Array[i]=ptr; //Выбор сделан, память выделена, заносим указатель на созданный объект в массив
} //Цикл for завершен
for (i=0;i<5;i++) Array[i]->Speak(); //Проходим по всему циклу и вызываем метод Speak для каждого элемента
for (i=0;i<5;i++) delete Array[i]; //Мы выделяли память, значит нужно её освободить.
cin.get();
cin.get();
}
================
При запуске этой программы будет предложено ввести число. В зависимости от введенного числа программа определит объект какого типа пользователь стремиться создать. Для объекта выбранного типа выделится необходимая память и указатель, который указывает на адрес созданного объекта будет записан в массив как элемент массива. Благодаря использованию виртуальных функций нам нет нужды задумываться о том, как правильно вызвать метод, который будет описывать звук издаваемый млекопитающим. Другими словами для каждого элемента массива будет вызван именно тот метод, который описан или переопределен в объекте, на который указатель, являющийся элементом массива указывает
Посмотрев и поняв работу примера вы лучше поймете описание полиморфизма: Один класс, множество методов
Основная идея использования полиморфизма: “Независимо от возрастания объемов кода и сложности программы, ко всем объектам, выведенным из базового класса, можно использовать один единственный общий способ доступа для каждого объекта, независимо от того что поведения этих объектов различны”
С переопределением функции вы скорее всего уже знакомы
==============
/*Родитель*/
class A
{
public:
void func() {cout<<“РОДИТЕЛЬ”;} //Функция базового класса
};
/*Потомок класса А*/
class B:public A
{
void func() {cout<<“ПОТОМОК”;} //Переопределили функцию func класса A для класса B
};
=============
Переопределение очень схоже с перегрузкой, но это принципиально разные понятия
С помощью указателя на базовый класс можно обращаться к любому объекту-наследнику этого класса.
Когда объектов-наследников много, то одна и та же функция может быть переопределена для каждого по своему. Давайте попробуем написать простую программу в привычном нам виде, но с использованием указателя
Код С++ Указатели Классы и переопределение функции
==============
#include <iostream.h>
#include <stdlib.h>
/*РОДИТЕЛЬ*/
class A
{
public:
void Show() {cout<<“A”<<endl;} //Метод класса, выводящий на экран A
};
/*ПОТОМОК КЛАССА А*/
class B:public A
{
public:
void Show() {cout<<“B”<<endl;} //Метод класса, выводящий на экран B
};
void main()
{
system(“CLS”);
A obj_A; //Объявили объект типа класса А
A *p; //Объявили указатель на класс А
B obj_B; //Объявили объект типа класса B
p=&obj_A; //Указали на адрес объекта – родителя
p->Show(); //Вызвали метод с помощью указателя
p=&obj_B; //Изменили адрес, на который указывает указатель на адрес потомка
p->Show(); //Попытка вызвать метод Show для объекта B
cin.get();
}
==============
Несмотря на то, что мы проинструктировали компилятор, поменяв указатель на адрес объекта-потомка, произошел вызов метода из базового класса. И в первом и во втором случае на экран вывелось A. В общем, при использовании указателя не произошло переопределения. Иногда программисту требуется написание программы подобного вида, но, как оказывается, возникает вопрос по обеспечению уникального поведения для каждого класса-потомка. Возникает вопрос о том, как обеспечить переопределение функций и почему так срабатывает.
Так срабатывает потому что мы не указали, что класс наследник должен переопределять виртуальную функцию, а указали переопределение обычной. В такой ситуации компилятор считает, что нам нужна та функция, которая встречается первая сверху (имеется ввиду иерархия классов). Про вопрос по переопределению функций. Для начала вспомним, что потомков у класса может быть гораздо больше чем один и у каждого могут быть функции, которые необходимо переопределить. Если использовать вариант, который описан выше, то сколько бы у нас объектов-потомков класса А не было, для всех них будет происходить вызов метода из базового класса, что нас явно не устраивает. На помощь приходит использование виртуальных функций.
Код С++ Указатели Классы и переопределение функции
==============
#include <iostream.h>
#include <stdlib.h>
/*РОДИТЕЛЬ*/
class A
{
public:
virtual void Show() {cout<<“A”<<endl;} //Виртуальный Метод класса, выводящий на экран A
};
/*ПОТОМОК КЛАССА А*/
class B:public A
{
public:
void Show() {cout<<“B”<<endl;} //Виртуальный Метод класса, выводящий на экран B
};
void main()
{
system(“CLS”);
A obj_A; //Объявили объект типа класса А
A *p; //Объявили указатель на класс А
B obj_B; //Объявили объект типа класса B
p=&obj_A; //Указали на адрес объекта – родителя
p->Show(); //Вызвали метод с помощью указателя
p=&obj_B; //Изменили адрес, на который указывает указатель
p->Show(); //Попытка вызвать метод Show для объекта B
cin.get();
}
==============
Теперь при изменении указателя, компилятор выбирает метод именно того потомка, на который указатель указывает. Сколько бы потомков у класса А не было, для каждого из них будет происходить именно тот вызов метода, который нужен. При каждом новом вызове метода Show() С++ каждый раз определяет тип вызываемого объекта, адресуемого указателем и исходя из этого определяет какую версию Show() использовать
Немного нужных сведений:
Код С++ Зачем нужен конструктор копирования
=========
#include <iostream.h>
#include <stdlib.h>
/*СТРУКТУРА МАССИВ*/
struct Array
{
int *element; //Указатель на int
Array(int N); //Конструктор структуры, принимающий параметр типа int
~Array(); //Деструктор структуры для освобождения памяти
};
Array::Array(int N) //Описываю конструктор вне структуры
{
element=new int[N]; //При каждом новом вызове будет выделяться новый участок памяти размером согласно принимаемому параметру
}
Array::~Array() //Описываю деструктор вне структуры
{
delete []element; //Освобождаю память, выделенную внутри конструктора
}
int main()
{
system(“CLS”);
Array v(3); //Объявляю переменную v, тип которой наша структура и выделяю память для 3 элементов int
v.element[0]=20; //Записываю в переменную v значения
v.element[1]=30;
v.element[2]=40;
Array x=v; //Объявляю переменную x, тип которой наша структура и копирую туда данные из v
cout<<x.element[0]<<” “<<x.element[1]<<” “<<x.element[2]<<endl; //Вывожу на экран элементы x
v.element[0]=100; //Например, появилась необходимость изменить объект v
cout<<x.element[0]<<” “<<x.element[1]<<” “<<x.element[2]<<endl; //Вывожу на экран элементы x
cin.get();
}
=========
Если вы плохо знакомы с указателями, то выполнение этого кода может произойти слегка не так, как вы ожидали. Несмотря на то, что и в первом и во втором случае, я вывожу на экран только данные второго созданного мною объекта, не изменяя его напрямую, данные в нем изменились после изменения данных в первом объекте. Такое поведение работы программы объясняется тем, что срабатывает конструктор копирования по умолчанию. Этот конструктор копирования копирует указатель как указатель и получается, что в обоих объектах оба указателя указывают на один и тот же адрес. То есть если изменить данные, расположенные на том адресе, то и первый и второй объект будут получать те измененные данные. Думаю, суть вы должны уловить. Вот в таких случаях и возникает вопрос безопасного копирования данных из одного объекта в другой. Безопасное копирование в этом случае обозначает, что изменения одного из объектов не должны влиять на другой объект. Для решения этого вопроса как раз и используют конструктор копирования.
Код С++ Зачем нужен конструктор копирования
============
#include <iostream.h>
#include <stdlib.h>
/*СТРУКТУРА МАССИВ*/
struct Array
{
int *element; //Указатель на int
int size; //Размер массива
Array(int N); //Конструктор принимает параметр, по которому определяет количество элементов для массива
Array(Array &obj); //Конструктор копирования
~Array(); //Деструктор для освобождения памяти
};
Array::Array(int N) //Описываю конструктор с параметром типа int вне класса
{
element=new int[N]; //При каждом новом вызове выделяю память для N элементов типа int
size=N; //Запоминаю размер согласно указанному параметру
}
Array::Array(Array &obj) //Описываю конструктор копирования вне класса
{
element=new int[obj.size]; //Выделяю память для элемента объекта
for (int i=0;i<obj.size;i++) element[i]=obj.element[i]; //Поэлементно копирую каждый элемент из принимаемого объекта в текущий
}
Array::~Array() //Описываю деструктор вне класса
{
delete []element; //Освобождаю память
}
int main()
{
system(“CLS”);
Array v(3); //Объявил объект v и указал, что он содержит три элемента
v.element[0]=20; //Записал данные в объект
v.element[1]=30;
v.element[2]=40;
Array x=v; //Объявляю второй объект и копирую в него данные из первого
cout<<x.element[0]<<” “<<x.element[1]<<” “<<x.element[2]<<endl; //Вывожу данные второго объекта на экран
v.element[0]=100; //Меняю одно поле первого объекта
cout<<x.element[0]<<” “<<x.element[1]<<” “<<x.element[2]<<endl; //Вывожу данные второго объекта на экран
cin.get();
}
============
В самый конец вы можете дописать вывод данных первого объекта на экран. В этом коде был использован прием, благодаря которому каждый объект будет обладать своим уникальным указателем. В первом случае была проблема в том, что оба указателя указывают на одно и то же место, значит, чтобы решить проблему, нужно сделать два указателя, которые не будут зависимы друг от друга и будут указывать на разные адреса. Кроме этого нужно учитывать, что при копировании размеры объектов должны совпадать. Чтобы размеры объектов совпадали, я при выделении памяти взял размер из принимаемого объекта и выделил столько памяти, сколько выделено для принимаемого объекта. Не нужно забывать, что при явном использовании конструктора копирования, выполнение копирования наша забота, поэтому копируем необходимые данные своими силами. Я выполнил поэлементное копирование одного массива в другой.
После выполнения таких не хитрых операций легко можно работать с обоими объектами, не беспокоясь за то, что изменяя один объект мы навредим внутри другого. Это и есть ответ на вопрос: “Зачем нужен конструктор копирования”
Я натыкался на примеры с деструкторами, но мне такие примеры давали больше воды, чем пользы. Такой пример немного нагляднее, понятнее и самое главное хорошо дает понять основные причины использования конструктора копирования. Но примеры ориентированные на деструкторы тоже полезны
Вообще, если программист не прописывает конструктор копирования своими руками, то компилятор сам вызывает такой конструктор каждый раз при создании класса, но иногда возникает необходимость прописывать такой конструктор копирования явно.
Немного о конструкторе копирования написано в википедии. Там попытка объяснить, зачем его нужно использовать. У меня не возникало необходимости, но по мере написания ваших программ вам может потребоваться использование конструктора копирования
Я предпочитаю более простые варианты для обучения и поэтому приведу максимально простой пример, который смог достать в просторах интернета. Это не объяснение, зачем нужен конструктор копирования, а пример его использования
Код С++ Конструктор копирования
==========
#include <stdlib.h>
#include <iostream.h>
/*НАШ КЛАСС*/
class A
{
int b; //некоторая переменная b, доступна только классу
public:
A (int a); //Конструктор, принимающий параметр а типа int
A(A &a); //Конструктор копирования, заданный явно
void Show(); //Метод для отображения переменной b на экране
};
A::A(int a) //Описываем конструктор, принимающий параметр явно
{
b=a; //Присваиваем в переменную b класса A значение, равное принятому параметру а
}
A::A(A &a) //Описываем конструктор копирования явно
{
//Пустой блок
}
void A::Show() //Описываем метод вне класса
{
cout<<b<<endl; //Отображаем b класса А на экране
}
int main()
{
system(“CLS”);
A a1(10); //Объявляем переменную а1 типа класса А и передаем туда 10, оно записывается в b вовнутрь класса
A a2=a1; //Объявляем переменную а2 типа класса A и пытаемся создать копию на основе объекта a1
a1.Show(); //Отображаем b объекта a1 На экране
a2.Show(); //Отображаем b объекта a2 На экране
cin.get();
return 0;
}
==========
Если вы немного понимаете классы и попробуете прочитать код не глядя на его выполнение, можно предположить, что произойдет присвоение в a2 данных из объекта a1, но при выполнении кода окажется, что это не так. Весь фокус в том, что когда вы прописываете конструктор копирования явно, то компилятор не создает неявных конструкторов копирования. Несмотря на то, что у нас в коде пустой блок, созданием своего такого конструктора мы проинструктировали компилятор, что мы отбираем у него полномочия на копирование данных из объекта в объект и будем крутить и копировать то, что нам надо своими руками. Так как блок пустой, то ничего и не происходит.
Что нужно делать, чтобы конструктор копирования, созданный нами работал и копировал данные одного объекта в другой? Нужно прописать код для такого копирования на месте пустого блока. Попробуйте немного подумать сами, что и чему нужно присвоить. Если придумать не можете, то только тогда смотрите
A::A(A &a) //Описываем конструктор копирования явно
{
b=a.b; //Копируем в b значение, взятое из принимаемого объекта
}
Напишите, выполните код. Вы увидите, что теперь присвоение одного объекта в другой срабатывает как нужно. Какие выводы можно сделать? Это говорит о том, что для того, чтобы копировать данные одного объекта в другой объект с помощью явно заданного конструктора копирования, нужно выполнить копирование своими руками. Если бы это был массив, нужно бы было выполнить поэлементное копирование каждого элемента массива. Аналогично для структур, аналогично для других случаев.
b=a.b; – В метод класса принимается ссылка на объект тип которого совпадает с типом класса текущего объекта, следовательно к элементам принимаемого объекта можно обратиться через точку. Нам нужно скопировать b из принимаемого объекта в b текущего.
Этот материал рассмотрен очень поверхностно, но может быть кому-то это поможет
Читать продолжение: Конструктор копирования. Причины использования
Для того чтобы человек мог понять, что такое шаблон класса и как шаблон класса использовать в своем коде я решил использовать один из самых простых вариантов на мой взгляд. Выбор пал на создание класса массив. Думаю, вы знакомы с понятием массива и поэтому знаете, как добавить и отобразить массив на экране. У массива должен быть тип, его тип соответствует типу данных, которые он хранит в себе. Внутри массива определенное число данных, поэтому должен быть счетчик элементов. Ничто не мешает создать простой класс Массив, который будет выполнять простые действия: Добавление и Отображение элементов
Код С++ Шаблоны классов Создание класса без шаблонов
============
#include <stdlib.h>
#include <iostream.h>
/*НАШ КЛАСС*/
class Massiv
{
int Array[10]; //Массив целочисленных значений из 10 элементов
int count; //Счетчик элементов массива
public:
void Add(int &); //Метод для добавления элементов в массив
void Show(); //Метод для отображения массива на экране
};
/*ДОБАВЛЯЕМ ЭЛЕМЕНТ В МАССИВ*/
void Massiv::Add(int &x)
{
static int pos=0; //Статическая переменная действует как глобальная, но доступна только там, где живет
Array[pos]=x; //Присваиваем принимаемый методом параметр в массив
pos++; //Меняем позицию для следующего присвоения
count=pos; //Счетчик равен последней позиции
}
/*ОТОБРАЖАЕМ МАССИВ НА ЭКРАНЕ*/
void Massiv::Show()
{
for (int i=0;i<count;i++) cout<<Array[i]<<“\t”; //С помощью цикла проходим по всему массиву
cout<<endl;
}
void main()
{
system(“CLS”); //Чистим экран
Massiv Arr; //Объявляем Объект Arr нашего класса Massiv
Arr.Add(100.555); //Добавляем в Arr значение (если добавить так, то произойдет приведение к типу int)
Arr.Add(200); //Добавляем в Arr значение
Arr.Add(300); //Добавляем в Arr значение
Arr.Show(); //Показываем Массив Arr на экране
cin.get();
}
============
Это пример создания обычного класса. Но у программистов иногда возникает необходимость создания такого же класса, в котором отличается только тип данных. Например, может потребоваться создание класса, в котором требуется создание массива, который будет хранить в себе и обрабатывать не целочисленные переменные, а строковые. Как вариант, можно дописать кучу классов для каждого из типов переменных, но это не дело. Уйдет некоторое время и код получится большим, громоздким. Чем больше кода, тем проще в нем ошибаться и тем сложнее искать ошибки (это должно быть понятно тем, у кого голова дружит с логикой). Вот тут и приходят на помощь шаблоны классов.
Код С++ Шаблоны классов Использование Шаблонов классов
============
#include <stdlib.h>
#include <iostream.h>
int pos=0; //Позиция внутри массива, я решил объявить глобально
template <class T> //Шаблон с класса с параметром T
class Massiv //Наш класс
{
T Array[10]; //Пусть тип объявленного массива будет соответствовать типу параметра из Шаблона
int count; //Счетчик элементов
public:
void Add(T &); //Метод с параметром-ссылкой на переменную типа параметра из Шаблона
void Show(); //Метод для отображения массива на экране
};
template<class T> void Massiv<T>::Add(T &x) //Прописываем метод вне класса с использованием шаблона класса
{
Array[pos]=x;//Присваиваем в текущий элемент параметр принимаемый методом. Тип этого параметра соответствует типу, принимаемому шаблоном
pos++; //Переходим к позиции следующего элемента
count=pos; //Счетчик равен текущей позиции элемента
}
template <class T> void Massiv<T>::Show() //Прописываем метод вне класса с использованием шаблона класса
{
for (int i=0;i<count;i++) cout<<Array[i]<<“\t”; //С помощью цикла выводим все данные массива на экран
cout<<endl;
pos=0; //После вывода данных обозначаем, что текущий элемент нулевой
}
void main()
{
system(“CLS”); //Чистим экран
Massiv<int> Arr; //Объявляем переменную типа нашего класса и указываем необходимый для обработки тип в угловых скобках
Arr.Add(100); //Добавляем элементы
Arr.Add(200);
Arr.Add(300);
Arr.Show(); //Отображаем массив на экране
Massiv<char *> Arr2; //Объявляем другую переменную типа нашего класса и указываем необходимый для обработки тип в угловых скобках
Arr2.Add(“Строка”); //Добавляем данные
Arr2.Add(“Начинаю понимать”);
Arr2.Add(“УРА”);
Arr2.Show(); //Отображаем массив на экране
cin.get();
}
============
По коду могу добавить, что в моем случае после отображения данных происходит сброс текущей позиции в ноль. Для этого использована глобальная переменная. В остальном вы можете наблюдать очень большое сходство с первым кодом. Когда объявляете метод вне класса, то сначала прописывается шаблон с необходимыми параметрами (их может быть и не один), потом класс с параметрами из шаблона. (Параметры в угловых скобках). Сам метод описывается обычным образом с использованием оператора глобального разрешения (::), но при необходимости, внутри принимаемых методом параметров тип принимаемой переменной подменяется на тип, принимаемый шаблоном класса.
Кроме уже сказанного, само объявление переменной, типа класса с использованием шаблона слегка отличается от обычного объявления переменной типа простого собственного класса. В угловых скобках указываются параметры, которые должен принимать шаблон. Вот и все премудрости. Возможно, я мог кого-то запутать этими словами, но все это читается по коду.
Шаблоны классов – Обобщенное определение некоторого семейства классов, имеющих схожую структуру, но различных в смысле используемых типов или констант.
=======
Перегрузка операторов – это возможность назначать новый смысл операторам при использовании их с определенным классом.
Во всей литературе для обучения перегрузке операторов скорее всего используется один и тот же пример. Самое стандартное – это перегрузка операторов для работы со строкой. Легко понять, что знак плюс или минус для чисел и знак плюс или минус для строки несут различные смыслы. В первом случае математические вычисления, во втором добавление или удаление символа (символов) или части строки.
Если попытаться просто сложить две строки при помощи знака плюс, то скорее всего вы увидите ошибку. Но ничто не мешает перегрузить операторы.
Код С++ Перегрузка операторов
=============
#include <stdlib.h>
#include <iostream.h>
#include <string.h> //для работы со строковыми функциями
/*СОЗДАЕМ СОБСТВЕННЫЙ КЛАСС*/
class string
{
public:
string (char*); //конструктор класса принимающий один параметр
void operator +(char*); //определение оператора +
void operator -(char); //определение оператора –
void show_string(void); //метод класса для отображения строки
private:
char data[256]; //символьный массив, доступный только классу
};
string::string(char *str) //Транзитом через конструктор
{
strcpy(data,str); //копируем в символьный массив класса данные из принимаемой извне строки
}
void string::operator +(char *str) //Определяем оператор +
{
strcat(data,str); //как функцию сложения двух строк
}
void string::operator -(char letter) //Определяем оператор –
{
char temp[256]; // будем создавать новую строку
int i,j; //счетчики циклов
//Проходим по всей строке класса с помощью цикла и если символ строки не равен принятому символу (параметру), то копируем его в новую строку
for (i=0,j=0;data[i];i++) if (data[i]!=letter) temp[j++]=data[i];
temp[j]=NULL;
strcpy(data,temp); //Копируем новую строку в символьный массив класса
}
void string::show_string(void)
{
cout<<data<<endl; //Показываем символьный массив класса
}
void main()
{
system(“cls”); //Очистка экрана
char *stroka,*stroka2; //Объявление двух указателей для строк
cin.getline(stroka,256); //Считывание первой строки с клавиатуры
cin.getline(stroka2,256); //Считывание второй строки с клавиатуры
string title(stroka); //Объявление переменной типа нашего класса и передача в конструктор первой строки
title+” “; //С помощью перегрузки операторов добавили к строке пробел
title+stroka2; //C помощью перегрузки операторов добавили к строке вторую строку
title.show_string(); //Отобразили результирующую строку на экране
title–‘в’; //При помощи перегрузки операторов пытаемся удалить символ в
title.show_string(); //Отобразили результирующую строку
cin.get();
}
===============
Особых сложностей в коде нет. Хотя помню, что достаточно долго не мог понять что к чему. В общем случае перегружать можно почти все операторы.
Нельзя перегружать:
. (точка)
* (звездочка)
:: (два двоеточия подряд)
?:(вопрос с двоеточием)
sizeof (тоже нельзя)
Нужно знать:
Кроме уже описанных ограничений на перегрузку операторов имеют место другие ограничения.
Нужно помнить, что перегрузка операторов нужна для облегчения читаемости кода и злоупотребление этим вместо пользы может приносить вред.