Uncategorized

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

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

Требуются знания

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

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

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

Для начала нужно создать класс и определить в нем поведение объекта. Наш объект — это треугольник.
Под поведением объекта понимается то, что этот объект должен уметь делать. В нашем случае Объект треугольник должен уметь рисовать себя, значит нужно описать метод, который научит объект Треугольник себя рисовать.
Что такое треугольник? Треугольник является подмножеством многоугольников и у него три угла. Какие данные нужны, чтобы треугольник нарисовать по заданным координатам? Нужно три точки

Итог: Нужны три детали (3 точки) и одно описание поведения (научить треугольник рисовать себя)
После описанного анализа, можно описать класс треугольник следующим образом:

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

pointtype — это тип данных точка. Чтобы получить доступ к элементам точки, нужно эти элементы прописать через точку.

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

Если все, что написано понятно, то
Шаг второй, описание поведения объекта (описание методов класса)
В классе объявлено два метода (конструктор и метод Show). Конструктор я использую для сообщения координат объекту
graphics.h Рисование треугольника по заданным координатам

Так при помощи конструктора, принимаемые в объект параметры будут передаваться в детали объекта. Теперь осталось описать поведение объекта и научить треугольник рисовать себя. Я упоминал, что покажу два способа. Первый способ — рисование линиями:
graphics.h Нарисовать треугольник по заданным координатам (Способ1)

graphics.h Нарисовать треугольник по заданным координатам (Способ2)

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

Последний шаг — Объявление объекта треугольник, передача ему параметров и Рисование треугольника по указанным координатам
graphics.h Нарисовать треугольник по заданным координатам ( продолжаю Способ2)

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

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

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

Программирование графики является одним из вопросов начинающих программистов. Моя речь будет о программировании графики в DOS, средствами языка С++. Компилятор, который я использую, является динозавром эпохи компьютеров, но он не мешает изучать С++. Я знаю, что раньше учили, сейчас учат и еще сто лет будут учить программировать, начиная с Borland c++3.1 или Turbo C. На вопросы о графике в этих компиляторах часто слышно что-то типа: «Перейди на нормальный и рисуй». С другой стороны, существуют разные преподаватели и бывают разные требования к преподаванию. Если в одном месте к студенту проявят лояльность и позволят использовать любые средства разработки, то в другом месте могут жестко ограничить, поэтому я не жалею, что использую своего динозавра в качестве обучающей базы.

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

Своё знакомство с графикой я начал с геометрических примеров, в которых используются алгоритмы на базе знаний геометрии. Очень часто требуется нарисовать простой рисунок типа: дом, дерево, солнце. Часть программистов начинает понимать графику именно с такого рода примитивных рисунков. Меня кто-то тут в комментариях попросил рассмотреть graphics.h, поэтому я решил выполнить просьбу и начал периодически писать о создании графики в C++ под Dos.

Перед рисованием рисунка, нужно хотя бы представлять его в общих чертах. Можно нарисовать сразу, можно срисовать с другого места. Честно говоря, по-жизни я рисовать не умею, но в Paint примитив нарисовать смогу, поэтому, первым делом я нарисовал рисунок в PaintNet

=======
Пейзаж
=======

Это то, что у меня получилось. Тут видно дом, солнце, линию горизонта, три дерева и озеро. Дом состоит из таких частей как окна, крыша, двери. Деревья имеют ветви, листву и ствол. Другими словами: «Мы имеем объекты – Дом, дерево, Солнце, озеро». Для того, чтобы с объектами было проще работать, можно использовать объектно-ориентированный подход к рисованию. При рисовании рисунка, я буду использовать классы. Никаких усложнений постараюсь не делать.

Тот, кто имеет познания рисования в Paint, тот знает, что сначала нужно рисовать фон. Здесь точно также. Чтобы фоновая картинка не затирала остальную, нужно создать фон. На моем рисунке фон — это небо, солнце, трава. Ничего не раскрашено, но обозначена линия горизонта, которая разделяет небо и землю.

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

Код С++ graphics.h Создание фона

После запуска получил такую картинку.
===========
Фон рисунка
===========

Кто-то может не поймет, почему я, только объявив картинку, смог ее нарисовать. В классе описан только один метод и метод этот — это конструктор. Конструктор выполняется раньше чем выполняется функция main, поэтому так и выходит.

После выполнения метода рисования фона, нужно приступать к основным объектам картинки (метод рисования фона в моем случае является конструктором класса). Я решил начать с рисования дерева. Честно признаюсь: «Рисование дерева отняло у меня некоторое время». Подобный рисунок я рисовал впервые и, поэтому, при рисовании дерева подгонял точку за точкой.

  • moveto — перемещает перо на указанные координаты. Позиция пера – это начало рисования.
  • lineto — рисует линию из начала рисования на указанные координаты.

Всмотритесь в дерево на первом, бесцветном рисунке. Оно у меня нарисовано линиями и овалами с кругами. Вот, первым делом, я рисовал дерево без листвы. Чтобы мне было немного проще подгонять точку к точке, я сначала перемещал перо в приблизительное место нужной точки и рисовал линию оттуда и на координаты (0,0). (тянул отрезок из текущей точки в левый верхний угол экрана). Ни разу не попал сразу, поэтому приходилось подгонять вверх, вниз, вправо и влево. Когда точка попадала в нужное место ствола или ветки, я начинал подгонять те координаты, которые (0,0).
Проще и понятнее: «Сначала брался за первую точку отрезка и пытался поставить ее в нужное место, потом за вторую. Таким образом, я смог получить дерево без листвы очень похожее на дерево с моего рисунка.
Вы можете нарисовать свое дерево на бумаге (или в компе). Для начала одни ветки без листьев и попытаться срисовать его. Так вы лучше поймете, о чем я толковал.
 Следующий этап — дорисовывание к дереву листвы. С листвой тоже немного нужно повозиться, подгоняя её в нужные места. В отличии от отрезка, для листика дерева можно использовать окружность или овал, а их местоположение определяет одна точка. Подогнать листья немного проще.

Сам код рисования дерева на моем фоне получился таким

Код С++ graphics.h Дерево на фоне

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

===========
Рисование дома, дерева
===========
Если сравнивать с моим изначально подготовленным рисунком, то заметно небольшое сходство.

Благодаря существованию объектно-ориентированного подхода к программированию, мне не нужно мучаться с координатами других деревьев. Вы, наверняка, увидели, что в моем коде использованы обозначения координат (x,y) вместо обычных цифр. Эти координаты объявлены внутри класса и по идее недоступны из функции main. Конструктор принимает параметры в свои большие (X,Y), которые потом передаются в маленькие (x,y) метода класса, поэтому не прямой, но доступ к ним есть. При объявлении объекта типа дерево, мы указываем два параметра для такой передачи. (У точки на плоскости две координаты).
Теперь можно рисовать много деревьев в разных участках экрана.
Кодом я показывать пока не буду, кто-то может сам поймет, просто нужно рисовать следующий объект – Дом.

Дом состоит из целого набора деталей (окна, двери, крыша). Каждую деталь нужно нарисовать. Чтобы получить чуть более чем простой рисунок, нужно рисовать относительно точек, а не просто по цифрам. Такой подход у меня тоже занял некоторое время и объяснить все, наверное, не получится, но основную идею я изложу.

Сначала я пробовал рисовать простые примитивы типа rectangle, контур линиями, но при заливке возникли проблемы, поэтому использованы bar3d, drawpoly.

  • Bar3d рисует прямоугольник
  • а drawpoly многоугольник.

Если всмотреться в дом на картинке, то можно увидеть такие многоугольники как параллелограмм и треугольник. Чтобы мне было немного проще находить координаты, чем было тогда, когда я находил координаты с деревом, я решил выводить на экран сначала textxy(x,y,”TEXT”). Аналогично дереву мне нужно плясать от какой-то точки, поэтому я подгонял эти точки в приблизительные места и только потом рисовал многоугольники. Такой момент оставлен в комментариях кода. Подгонял я, разумеется, с многократным запуском программы и изменениями координат вывода текста. В таком подгоне заключается основная идея моего подхода.

Код С++ graphics.h Дом, дерево, солнце

После запуска получилась картинка.
Рисование Дом, дерево, солнце
Несмотря на то, что этот подход к рисованию труднее понять, чем простая подстановка координат цифрами, он все-таки лучше. Теперь можно создавать дом в любом месте экрана, как и дерево. Если нужен пример, то пожалуйста.

Изменяем функцию main, остальное все остается так, как написано, и получаем что-то типа

Код C++ graphics.h Дом, дерево, Солнце

Получили рисунок
Рисование Дом, Дерево, Солнце, объекты

Вот такое вот нелегкое начало рисования приводит к достаточно простому созданию копий объектов, каждый из объектов может быть отдельным рисунком.
При всем при этом, такие начинания являются первым шагом к пониманию анимации изображений.span style=»color: #800000;»

Рисование правильной n конечной звезды

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

Я давно написал и думал, что выложил этот исходник рисования правильной звезды с указанным количеством концов, но сегодня заметил, что этой статьи нет. Правильную n-конечную звезду я смог нарисовать своими собственными силами в то время, когда разбирался с правильным n-угольником. За пройденное время у меня появились статьи других разделов и я уже немного подзабыл что тут и как. Но я считаю нужным поделится моим примером с моими читателями.

Программирование Правильная n-конечная звезда

Сначала вызывается функция ввода данных, пользователь вводит параметры правильной n конечной звезды и из функции ввода происходит вызов функции рисования этой правильной n конечной звезды. Помню,что у меня проблема была при рисовании правильного n-угольника связанная с типами данных (там об этом упомянуто), поэтому призываю быть внимательными при объявлении переменных и назначений им типов. В компьютерной графике хорошо заметно, что что-то не так. Можете поиграть с типами данных. Я не долго думая всё заменил на float, но это только чтобы самому себе сэкономить время.

  • M_PI — это число ПИ = 3,14 и определено в math.h
  • Звезда — выпуклый многоугольник, поэтому неудивительно, что я смог решить тогда когда этим занимался

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

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

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

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

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

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

Создадим базовый класс — млекопитающие, от которого унаследуем виды млекопитающих и определим для них уникальное поведение (как нам уже известно каждое млекопитающее должно издавать свой звук)

Код C++ Виртуальные функции Динамический полиморфизм

Применены приемы наследования. Создано три потомка из основного класса. Каждый потомок — это известное нам млекопитающее

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

Код С++ Виртульные функции Динамический полиморфизм

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

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

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

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

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

С переопределением функции вы скорее всего уже знакомы

Переопределение очень схоже с перегрузкой, но это принципиально разные понятия

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

Код С++ Указатели Классы и переопределение функции

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

Так срабатывает потому что мы не указали, что класс наследник должен переопределять виртуальную функцию, а указали переопределение обычной. В такой ситуации компилятор считает, что нам нужна та функция, которая встречается первая сверху (имеется ввиду иерархия классов). Про вопрос по переопределению функций. Для начала вспомним, что потомков у класса может быть гораздо больше чем один и у каждого могут быть функции, которые необходимо переопределить. Если использовать вариант, который описан выше, то сколько бы у нас объектов-потомков класса А не было, для всех них будет происходить вызов метода из базового класса, что нас явно не устраивает. На помощь приходит использование виртуальных функций.

Код С++ Указатели Классы и переопределение функции

Теперь при изменении указателя, компилятор выбирает метод именно того потомка, на который указатель указывает. Сколько бы потомков у класса А не было, для каждого из них будет происходить именно тот вызов метода, который нужен. При каждом новом вызове метода Show() С++ каждый раз определяет тип вызываемого объекта, адресуемого указателем и исходя из этого определяет какую версию Show() использовать

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

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

Читать дальше: Виртуальные функции Продолжение знакомства Динамический полиморфизм

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

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

Код С++ Зачем нужен конструктор копирования

Если вы плохо знакомы с указателями, то выполнение этого кода может произойти слегка не так, как вы ожидали. Несмотря на то, что и в первом и во втором случае, я вывожу на экран только данные второго созданного мною объекта, не изменяя его напрямую, данные в нем изменились после изменения данных в первом объекте. Такое поведение работы программы объясняется тем, что срабатывает конструктор копирования по умолчанию. Этот конструктор копирования копирует указатель как указатель и получается, что в обоих объектах оба указателя указывают на один и тот же адрес. То есть если изменить данные, расположенные на том адресе, то и первый и второй объект будут получать те измененные данные. Думаю, суть вы должны уловить. Вот в таких случаях и возникает вопрос безопасного копирования данных из одного объекта в другой. Безопасное копирование в этом случае обозначает, что изменения одного из объектов не должны влиять на другой объект. Для решения этого вопроса как раз и используют конструктор копирования.

Код С++ Зачем нужен конструктор копирования

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

После выполнения таких не хитрых операций легко можно работать с обоими объектами, не беспокоясь за то, что изменяя один объект мы навредим внутри другого. Это и есть ответ на вопрос: «Зачем нужен конструктор копирования»

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

С++ для начинающих Конструктор копирования Поверхностное знакомство

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

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

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

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

Код С++ Конструктор копирования

Если вы немного понимаете классы и попробуете прочитать код не глядя на его выполнение, можно предположить, что произойдет присвоение в a2 данных из объекта a1, но при выполнении кода окажется, что это не так. Весь фокус в том, что когда вы прописываете конструктор копирования явно, то компилятор не создает неявных конструкторов копирования. Несмотря на то, что у нас в коде пустой блок, созданием своего такого конструктора мы проинструктировали компилятор, что мы отбираем у него полномочия на копирование данных из объекта в объект и будем крутить и копировать то, что нам надо своими руками. Так как блок пустой, то ничего и не происходит.

Что нужно делать, чтобы конструктор копирования, созданный нами работал и копировал данные одного объекта в другой? Нужно прописать код для такого копирования на месте пустого блока. Попробуйте немного подумать сами, что и чему нужно присвоить. Если придумать не можете, то только тогда смотрите

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

примечание
b=a.b; — В метод класса принимается ссылка на объект тип которого совпадает с типом класса текущего объекта, следовательно к элементам принимаемого объекта можно обратиться через точку. Нам нужно скопировать b из принимаемого объекта в b текущего.

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

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

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

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

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

Это пример создания обычного класса. У программистов иногда возникает необходимость создания такого же класса, но в котором отличается только тип данных. Например, может потребоваться создание класса, в котором требуется создание массива, который будет хранить в себе и обрабатывать не целочисленные переменные, а строковые. Как вариант, можно дописать кучу классов для каждого из типов переменных, но это не дело. Уйдет некоторое время и код получится большим, громоздким. Чем больше кода, тем проще в нем ошибаться и тем сложнее искать ошибки (это должно быть понятно тем, у кого голова дружит с логикой). Вот тут и приходят на помощь шаблоны классов.

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

  • Когда объявляете метод вне класса, то сначала прописывается шаблон с необходимыми параметрами (их может быть и не один), потом класс с параметрами из шаблона. (Параметры в угловых скобках). Сам метод описывается обычным образом с использованием оператора глобального разрешения ::, но при необходимости, внутри принимаемых методом параметров тип принимаемой переменной подменяется на тип, принимаемый шаблоном класса.
  • Кроме уже сказанного, само объявление переменной, типа класса с использованием шаблона слегка отличается от обычного объявления переменной типа простого собственного класса. В угловых скобках указываются параметры, которые должен принимать шаблон. Вот и все премудрости. Возможно, я мог кого-то запутать этими словами, но все это читается по коду.

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

    =======

    C++ для начинающих Динамические структуры данных СТЕК

    В этот раз я опишу создание стека.

    • Стек — это такой линейный список, в котором и добавление новых и удаление существующих элементов возможно только с головного элемента.

    Часто стек сравнивают с ящиком, в который например складывают кирпичи, часто со стопкой тарелок. Кроме проведения аналогий стек часто называют структурой LIFO, что обозначает последним пришел, первым ушел.

    Наверное это может быть непонятно, поэтому поясню на цифрах.

    • Вводим: 1,2,3,4,5
    • На выходе 5,4,3,2,1

    При этом голова у нас наша единица.
    Стек

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

    Для стека определены следующие операции:

    • Проверка стека на пустоту
    • Добавление элемента в стек
    • Считывание головного элемента
    • Удаление головного элемента
    • Очистка стека

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

    Я не буду разбивать код на этапы создания. Для создания стека (или динамического списка LIFO) будем использовать 4 функции (main, Добавить в список, Показать список, Удалить список из памяти)

    Код C++ Создать Стек (или список LIFO)

    Я смотрю на свой первоначальный код (LIFO Первое знакомство), там с классами и меня посещают мысли: «Неужели так сложно дать то, что так просто?» Я ведь много страниц тогда перерыл и не нашел этого.

    Чтобы привести этот код к классическому пониманию стека, достаточно заменить мои temp -переменные на (*MyList) и тогда даже при простом просмотре просмотренные данные будут сразу теряться. Удаляться не будут, но связь потеряется. И если так сделаете, то будут паразитировать эти призраки стека и пожирать вашу память отныне и вовеки веков. При работе с динамическими данными надо быть очень внимательным к памяти и не нужно этого забывать.

    Стек можно просмотреть с противоположной стороны голове, но это требует очень много с точки зрения затрат памяти, потому что каждый раз придется проходить от головы до последнего (который последний текущий) ровно столько раз сколько в стеке элементов. Это чтобы два элемента считать, надо два раза по стеку пройтись.

    Для стека можно выполнить и другие нетипичные для него операции, но кроме как для лучшего понимания материала это вряд ли стоит делать

    В моем примере стек был построен на основе линейного связного списка, но стеки могут реализовываться на различных структурах данных: на одномерных статических и динамических массивах, на линейных списках, с помощью файлов… В общем стек можно реализовать на любой структуре, в которой есть возможность хранить несколько элементов и если есть то, куда можно сохранять данные

    ==============================================================
    Еще один пример стека

    C++ для начинающих Динамические деревья Обходы

    Дерево, дерево, дерево… Что за деревья такие, как же понять. Я на такое вообще способен? Такие мысли могут появляться у начинающих программистов. Хорошо когда знаешь английский, а когда не знаешь, то программистам труднее. Я вот не знаю и мне приходится крупицами искать материалы. Но эти крупицы есть! Так с миру по нитке получаются статьи этого блога.

    Первый мой шаг по изучению динамических деревьев попал на бинарное дерево. Но бинарное дерево — это такое дерево по определению, в котором все элементы упорядочены. А ведь не обязательно динамическое дерево заполняется с условием, что слева должны быть меньшие, справа большие элементы. Иногда можно видеть разные картинки, на которых нарисованы графы. При этом описание может быть одно и то же, а сама логика на картинках разная. Например при описании обхода дерева меня сначала запутало то что при моем выводе на экран, значения не совпадали с теми, которые на картинке.

    Оказалось, что мое дерево бинарное, а на картинке динамическое дерево и просто заполнено по другому (такими же числами как и у меня, просто другой порядок записи данных в звенья)

    Вот например так: С++ Дерево

    Сейчас я чуть более подробно опишу понятие дерево. Дерево может строится по разному. Например, можно искать первое пустое место и в это самое пустое место записывать элемент. Чтобы зря не городить огород, я опишу только функцию добавления элемента в дерево. На протяжении всей статьи я буду говорить об одном и том же коде, который я приведу целиком чуть позже

    Код C++ Создание динамического дерева

    В таком случае дерево будет разрастаться равномерно. На первом уровне один узел, на втором два, на третьем четыре, четвертом восемь и так далее. При этом информация не будет иметь каких-то признаков обработки. Мы инструктировали компилятор, чтобы он писал так, как мы пишем в тетрадках слева направо по чистым местам листов. Это проиллюстрировано рисунком выше.

    Вот другой вариант — это создание бинарного дерева, там элементы записываются в совершенно другом порядке. Создание функции добавления я описал в статье Бинарное дерево – проще чем кажется
    Код С++ Бинарное дерево

    В бинарном дереве элементы по определению упорядочены, причем слева идут меньшие, справа большие. Конечно это легко поменять, но в таком случае дерево перестанет быть бинарным.

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

    С добавлением информации в динамические деревья более менее разобрались. Кроме ввода данных, существуют разные алгоритмы обработки деревьев. Среди таких алгоритмов часто встречаются понятия обхода деревьев. Это первое, что должно вас интересовать, потому что на первых порах вам будет нужно выводить данные дерева на экран и это то, оно самое. Алгоритмы обхода деревьев используют по разным причинам и в разных целях, но независимо от этого вы должны понимать как обходы работают.

    Для того чтоб описать функции обхода деревьев, я покажу их кодом. Предполагается, что обход дерева — это функция отображения дерева, поэтому опишу внутри функции.

    Нетрудно увидеть и понять разницу.

    Есть еще обход в ширину, в котором узлы посещаются уровень за уровнем(N-й уровень дерева — множество узлов с высотой N). Каждый уровень обходится слева направо.
    Для реализации используется структура queue — очередь с методами (поставить в очередь, взять из очереди, проверка очереди на пустоту)

    Иногда могут потребовать описать восемь методов обхода деревьев. Знаете почему? Потому что кроме использования рекурсии, для поиска пустого узла и записи в него элемента, можно использовать обычные циклы. Их написать чуть сложнее чем написать код с рекурсивным вызовом, но вы должны знать чем отличается использование рекурсии от использования обычных циклов. При хорошем понимании построения деревьев, их обходов использовать обычные циклы должно стать несложной задачей. Но я все-таки сконцентрирую внимание на том что понять попроще

    Код С++ Создание бинарного дерева Обход в прямом порядке

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

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

    Код исправлен и рабочий. Если видите, что ошибки у меня, наверное лучше меня в мои ошибки и тыкать носом. Я за всеми своими огрехами уследить физически не могу.

    Поиск

     
         
    
    
    Яндекс.Метрика

    НАГРАДИ АВТОРА САЙТА
    WEBMONEY
    R375024497470
    U251140483387
    Z301246203264
    E149319127674
    
    
    Демотиватор программирование на языке ада

    Выражаю свою признательность

    • Максиму очень признателен за указание на мои ошибки и неточности.
    • Sergio ===> за оказание помощи в исправлении моих ошибок
    • Gen ===> за правильное стремление помочь другим новичкам и выявления моих ошибок