graphics.h Рисование треугольника по заданным координатам

Graphics.h Создание треугольника по заданным координатам

Программирование графики всегда предполагает то, что вы знакомы с геометрией. Чем больше у вас геометрических знаний, тем проще ориентироваться в программировании графики. Рассматривая graphics.h иногда полезно попробовать создать примитив. Так как обучающие материалы предполагают простоту кода, то будет рассмотрен примитивный пример рисования треугольника по заданным координатам.

Требуются знания
С++ для начинающих Классы Первое знакомство
Передача параметров в функции в C++ для начинающих
С++ для начинающих Конструктор класса Передача параметров в базовый конструктор класса

Я покажу два способа создания треугольника по заданным координатам. Это достаточно простой для понимания урок и полезный для получения практических знаний.

Нарисовать треугольник можно чистым контуром, проведя три линии, а можно использовать функцию drawpoly, рисующую многоугольник. В обоих случаях код получается достаточно компактным и каждый волен решать что ему больше подходит

Шаг первый. Анализ и Мозговой штурм.

  • Для начала нужно создать класс и определить в нем поведение объекта. Наш объект – это треугольник.
  • Под поведением объекта понимается то, что этот объект должен уметь делать. В нашем случае Объект треугольник должен уметь рисовать себя, значит нужно описать метод, который научит объект Треугольник себя рисовать.
  • Что такое треугольник? Треугольник является подмножеством многоугольников и у него три угла. Какие данные нужны, чтобы треугольник нарисовать по заданным координатам? Нужно три точки
Итог: Нужны три детали (3 точки) и одно описание поведения (научить треугольник рисовать себя)
После описанного анализа, можно описать класс треугольник следующим образом:
graphics.h Рисование треугольника по заданным координатам линиями
================
 class Triangle
{
 pointtype a,b,c;
//Детали треугольника = три точки для построения
 public:
  Triangle(pointtype,pointtype,pointtype);
//Прототип конструктора с параметрами
  void Show();
//Метод, помогающий понять треугольнику, как себя рисовать
};

================
pointtype – это тип данных точка. Чтобы получить доступ к элементам точки, нужно эти элементы прописать через точку. (пример a.x=100;b.y=111). Если непонятно, смотри структура

Детали объекта скрыты от прямого доступа извне, но нам нужно передавать в них данные. Наши данные – это указания о месте положения точек. Чтобы передать эти данные, используется конструктор с параметрами

Если все, что написано понятно, то

 Шаг второй, описание поведения объекта (описание методов класса)
В классе объявлено два метода (конструктор и метод Show).  Конструктор я использую для сообщения координат объекту 
graphics.h Рисование треугольника по заданным координатам
================
 class Triangle
{
 pointtype a,b,c;
//Детали треугольника = три точки для построения
 public:
  Triangle(pointtype,pointtype,pointtype);
//Прототип конструктора с параметрами
  void Show();
//Метод, помогающий понять треугольнику, как себя рисовать
};

Triangle::Triangle(pointtype A,pointtype B,pointtype C) //Я описываю конструктор вне класса
{
 a=A;
//Копирование данных из структуры A в a и далее по аналогии
 b=B;
 c=C;
}

================
Так при помощи конструктора, принимаемые в объект параметры будут передаваться в детали объекта. Теперь осталось описать поведение объекта и научить треугольник рисовать себя. Я упоминал, что покажу два способа. Первый способ – рисование линиями:

graphics.h Нарисовать треугольник по заданным координатам (Способ1)
================
 class Triangle
{
 pointtype a,b,c;
//Детали треугольника = три точки для построения
 public:
  Triangle(pointtype,pointtype,pointtype);
//Прототип конструктора с параметрами
  void Show();
//Метод, помогающий понять треугольнику, как себя рисовать
};

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 треугольника
}

================

Это другой способ описания метода Show() вне класса
graphics.h Нарисовать треугольник по заданным координатам (Способ2)
================
 class Triangle
{
 pointtype a,b,c;
//Детали треугольника = три точки для построения
 public:
  Triangle(pointtype,pointtype,pointtype);
//Прототип конструктора с параметрами
  void Show();
//Метод, помогающий понять треугольнику, как себя рисовать
};

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();
//Метод, помогающий понять треугольнику, как себя рисовать
};

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);
//Рисование треугольника по данным, записанным в массив
}

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();
//Заставили объект нарисовать себя по указанным координатам
    

cin.get();
closegraph();
}

================
Пример, который я привел не очень полноценный, но помогает понять работу конструктора объектов. Не полноценность его в том, что во время выполнения программы не получится задать координаты и объект построится по тем, которые объявлены при объявлении конструктора. Такая неполноценность не очень приятна, но этого легко избежать если передавать параметры в наш метод Show(). Что и куда передавать зависит больше от ситуации, например если создавать фоновую картинку, то достаточно передать параметры в конструктор, если же объект на фоне, параметры которого могут изменяться в ходе выполнения программы, то имеет смысл передавать параметры в метод объекта, который не конструктор. Так приблизительно выглядит общая картина. 
 Кроме функции drawpoly, есть функция fillpoly, которая тоже рисует многоугольник также как и drawpoly, но при этом можно установить стиль и цвет заливки, которым многоугольник этот закрасится. В коде ниже я использую fillpoly и буду передавать параметры в метод Show
graphics.h Нарисовать треугольник по заданным координатам (Способ2)
================
#include <iostream.h>
#include <graphics.h>

 class Triangle
{
 pointtype a,b,c;
//Детали треугольника = три точки для построения
 public:

  void Show(pointtype,pointtype,pointtype);
//Метод, помогающий понять треугольнику, как себя рисовать
};


void Triangle::Show(pointtype a,pointtype b,pointtype c) //Описываю метод Show вне класса
{
 int Poly[8]={a.x,a.y,b.x,b.y,c.x,c.y,a.x,a.y} ;
//Инициализация массива параметрами переданными в объект
 fillpoly(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; //Объявили объект, типа треугольник
Tr1.Show(a,b,c);
//Заставили объект нарисовать себя по указанным координатам
     

      a.x=500;   a.y=70;
      b.x=600; b.y=100;
      c.x=10;   c.y=70;
     setfillstyle(1,1); //Установка цвета и стиля заливки
     Tr1.Show(a,b,c); //Рисование закрашенного треугольника по координатам точек а,b,c
cin.get();
closegraph();
}

================
 Такие вот варианты рисования треугольников по заданным координатам можно использовать. Я знаю, что расписал не полностью и не всё, но возможно это поможет вам двигаться дальше в вашем обучении и получении знаний.
В последнем варианте легко задавать параметры с помощью cin . Например можно спросить пользователя сколько треугольников он хочет создать и с помощью цикла записать все координаты, после чего вывести треугольники на экран. Можно выводить треугольники прямо внутри цикла после ввода координат для каждых трех точек. В общем можно делать так как вам интереснее и удобнее

graphics.h Рисование Дома, Дерева, Озера Солнца

Программирование графики является одним из вопросов начинающих программистов. Моя речь будет о программировании графики в DOS средствами языка С++ Компилятор, который я использую – это динозавр эпохи компьютеров, но он не мешает мне изучать С++. Я знаю, что раньше учили, сейчас учат и еще сто лет будут учить программировать начиная с Borland c++3.1 или Turbo 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,y150);                        lineto(x,y300);
  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(x30,y-(150+75)-20);
  moveto(x,y-(150+75)-30);       lineto(x25,y-(150+75)-40);
  //Рисую листву
 setfillstyle(1,GREEN);
 fillellipse(x,y300,5,5);
 fillellipse(x+20,y290,7,7);
 fillellipse(x+45,y300,3,7);
 fillellipse(x+75,y300,10,10);
 fillellipse(x+20,y255,5,10);
 fillellipse(x+70,y247,10,10);
 fillellipse(x30,y247,10,10);
 fillellipse(x30,y267,7,7);
 fillellipse(x15,y270,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,y150);                        lineto(x,y300);
  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(x30,y-(150+75)-20);
  moveto(x,y-(150+75)-30);       lineto(x25,y-(150+75)-40);
  //Рисую листву
 setfillstyle(1,GREEN);
 fillellipse(x,y300,5,5);
 fillellipse(x+20,y290,7,7);
 fillellipse(x+45,y300,3,7);
 fillellipse(x+75,y300,10,10);
 fillellipse(x+20,y255,5,10);
 fillellipse(x+70,y247,10,10);
 fillellipse(x30,y247,10,10);
 fillellipse(x30,y267,7,7);
 fillellipse(x15,y270,5,9);
}

/*МЕТОД РИСОВАНИЯ ДОМА ОПИСАН ВНЕ КЛАССА*/
void Dom::ShowHome()
{

 setfillstyle(3,3); //Установка стиля заливки
 bar3d(x150,y,x50,y120,2,1); //Рисую фасад дома

 //Треугольная часть крыши
 int poly[8]; //3 вершины*2 = 6. Нужна точка замыкания 6+2 =8
 poly[0]=x155;   poly[1]=y120;   //A(x,y)
 poly[2]=x100;   poly[3]=y200;   //B(x,y)
 poly[4]=x45;    poly[5]=y120;   //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]=x45;          poly1[1]=y120; //A
 poly1[2]=x+100;       poly1[3]=y120; //B
 poly1[4]=x+10045poly1[5]=y200; //D
 poly1[6]=x100;        poly1[7]=y200; //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(x45,y,x+100,y120,1,1); //Залитый прямоугольник

 //ПОИСК И РИСОВАНИЕ ОКОН
 /*outtextxy(x-30,y-50,”A”);
 outtextxy(x-30,y-80,”B”);
 outtextxy(x,y-80,”C”); */

 setfillstyle(1,7); //Установка стиля заливки
 bar3d(x30,y50,x,y80,1,1); //Рисование первого окна

 /*outtextxy(x+15,y-50,”A”);
 outtextxy(x+45,y-80,”B”);*/
 bar3d(x+15,y50,x+45,y80,1,1); //Рисование второго окна

 /*outtextxy(x+60,y-50,”A”);
 outtextxy(x+90,y-80,”B”); */
 setfillstyle(5,YELLOW); //Установка стиля заливки
 bar3d(x+60,y50,x+90,y80,1,1); //Рисование третьего окна

 /*ПОИСК КООРДИНАТ И РИСОВАНИЕ ДВЕРЕЙ
 outtextxy(x-70,y,”A”);
 outtextxy(x-100,y-70,”B”);*/
setfillstyle(2,6);
//Установка стиля заливки
bar3d(x70,y,x100,y70,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-угольником. За пройденное время у меня появились статьи других разделов и я уже немного подзабыл что тут и как. Но я считаю нужным поделится моим примером с моими читателями.

Программирование Правильная 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=yr/2*sin(a*M_PI/180);
   }
   else
//При невыполнении условия четности следующие формулы
    {
     p[i].x=x+R*cos(a*M_PI/180);
     p[i].y=yR*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
Звезда – выпуклый многоугольник, поэтому неудивительно, что я смог решить тогда когда этим занимался

Надеюсь этот материал поможет вам в ваших начинаниях. Этот код позволяет строить хоть трех конечную, хоть сто конечную правильную звезду.
==========

C++ для начинающих Виртуальные функции Продолжение знакомства Динамический полиморфизм

В продолжении темы начала знакомства с виртуальными функциями пишу этот материал. Несмотря на то, что я описал первопричину использования виртуальных функций, этого явно мало. В литературе для лучшего понимания виртуальных функций используют пример, в котором создается массив объектов. Это не обычный массив объектов, а каждый элемент массива имеет объект различного типа. Вспомним что такое класс. Класс – это пользовательский тип данных. Значит каждый новый класс это новый тип.

Давайте вспомним одно свойство массива. Массив – это набор однотипных данных. Может показаться, что использование наследования и виртуальных функций немного нарушает такое определение. Если размышлять, то можно прийти к мыслям: “Если массив содержит данные только одного типа, то значит в один массив объекты разных типов мне поместить не удастся”. Это совершенно верные мысли. Несмотря на это любой самый обычный массив можно построить таким образом, что элементы, обрабатываемые с помощью него будут иметь различные типы. Может показаться, что я противоречу сам себе, но вовсе нет. Для того, чтобы построить такой массив с элементами различных типов используют массив указателей. Каждый элемент такого массива – это указатель. 

Перехожу от слов к делу.
Задача: Создать массив объектов класса млекопитающие

Предположим у нас есть 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();
}

================
При запуске этой программы будет предложено ввести число. В зависимости от введенного числа программа определит объект какого типа пользователь стремиться создать. Для объекта выбранного типа выделится необходимая память и указатель, который указывает на адрес созданного объекта будет записан в массив как элемент массива. Благодаря использованию виртуальных функций нам нет нужды задумываться о том, как правильно вызвать метод, который будет описывать звук издаваемый млекопитающим. Другими словами для каждого элемента массива будет вызван именно тот метод, который описан или переопределен в объекте, на который указатель, являющийся элементом массива указывает

Посмотрев и поняв работу примера вы лучше поймете описание полиморфизма: Один класс, множество методов

Основная идея использования полиморфизма: “Независимо от возрастания объемов кода и сложности программы, ко всем объектам, выведенным из базового класса, можно использовать один единственный общий способ доступа для каждого объекта, независимо от того что поведения этих объектов различны”

C++ для начинающих Виртуальные функции Поверхностное знакомство

Знакомство с виртуальными функциями у начинающих программистов может быть слегка проблематичным. Возникает много простых вопросов, на которые сходу трудно дать полноценные ответы.
Первый такой вопрос: “Зачем использовать виртуальные функции”
 Если, например, объявлено много объектов-наследников, то у каждого из них может быть переопределена одна и та же функция, благодаря которой определяется уникальное поведение для каждого существующего объекта.

С переопределением функции вы скорее всего уже знакомы
==============
/*Родитель*/
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() использовать

Немного нужных сведений:

  • Если в базовом классе есть функция с ключевым словом virtual, то такой класс называется полиморфным, а все функции внутри потомков (которые предполагается переопределить) называются виртуальными.
  • То, какая виртуальная функция будет вызвана определяется во время выполнения программы.
  • Виртуальная функция должна быть членом класса для которого определяется, а не его “другом”, но в то же время виртуальная функция может быть другом иного класса
  • Функциям деструкторов разрешается быть виртуальными, а функциям конструкторов нет

C++ для начинающих Конструктор копирования Причины использования

Продолжая начатую тему Конструктор копирования, я приведу пример случая, в котором необходимо создании конструктора копирования. Пример почти такой же, как в википедии куда я ранее ссылался, но чуть проще и немного понятнее для новичков в программировании.

Код С++ Зачем нужен конструктор копирования
=========
#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 текущего.

Этот материал рассмотрен очень поверхностно, но может быть кому-то это поможет
Читать продолжение: Конструктор копирования. Причины использования

C++ для начинающих Шаблоны классов Первое знакомство

Начинающие программисты постоянно могут наблюдать использование шаблонов в различных исходных кодах. Иногда они просят помочь с какой-то работой более опытных программистов и часть из таких опытных программистов, не задумываясь, выдает им код с использованием шаблонов. Самое интересное, что начинающий может хорошо понимать, что такое шаблон и зачем он нужен, но не понимать, как прочитать код с использованием этих самых шаблонов.
 Я уже описывал Шаблоны функций в С++ для начинающих, но кроме этого, в С++ часто применяются шаблоны классов. В некоторых книгах для обучения используется пример создания класса Стек или Список и создание шаблона для этого класса. Но я бы сказал, вначале пути использование динамических структур данных слегка затруднительно, и я склонен считать, что те кто учит, пусть сначала научать понимать шаблон классов, а потом человеку будет проще понять другие материалы, которые требуют отдельного изучения.

 Для того чтобы человек мог понять, что такое шаблон класса и как шаблон класса использовать в своем коде я решил использовать один из самых простых вариантов на мой взгляд. Выбор пал на создание класса массив. Думаю, вы знакомы с понятием массива и поэтому знаете, как добавить и отобразить массив на экране. У массива должен быть тип, его тип соответствует типу данных, которые он хранит в себе. Внутри массива определенное число данных, поэтому должен быть счетчик элементов. Ничто не мешает создать простой класс Массив, который будет выполнять простые действия: Добавление и Отображение элементов

Код С++ Шаблоны классов Создание класса без шаблонов

============
#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 (тоже нельзя)

 Нужно знать:

  • Чтобы перегрузить оператор, нужно определить класс, к которому оператор будет назначен
  • Когда перегружается оператор, перегрузка действует только для класса в котором он определяется. Если оператор использует неклассовые переменные, то используется стандартное определение оператора
  • Для перегрузки оператора используется ключевое слово С++ operator. Это слово определяет метод класса, который С++ вызывает каждый раз, когда переменная класса вызывает оператор
  • С++ позволяет перегружать все операторы кроме вышеуказанных

Кроме уже описанных ограничений на перегрузку операторов имеют место другие ограничения.

  •  Старшинство операций не может быть изменено перегрузкой
  •  Ассоциативность операций не может быть изменена перегрузкой
  •  Изменить количество операндов, которое берет операция невозможно: Перегруженные унарные операции остаются унарными, перегруженные бинарные остаются бинарными
  •  Каждая из операций & * + – может иметь и унарный и бинарный варианты. Эти унарные и бинарные варианты могут перегружаться отдельно
  •  Создавать новые операции невозможно. Возможно только использовать существующие.
  •  Нельзя изменить операцию для объекта встроенного типа (уже упоминалось чуть другими словами)
  •  Неявной перегрузки не существует (например object1=object1+object2 не равно object1+=object2 если явно перегрузки не прописано)

Нужно помнить, что перегрузка операторов нужна для облегчения читаемости кода и злоупотребление этим вместо пользы может приносить вред.