Загрузка...
 
Печать
ИГРОКОДИНГ  »  ИГРОКОДИНГ: Учебный курс  »  Программируем 3D-шутер от первого лица (FPS) (Win32, Cpp, DirectX9)  »  Часть 1. Создание движка  »  Основы языка Cpp

Основы языка С++


Язык C++(external link) - компилируемый, объектно-ориентированный, статически типизированный язык программирования общего назначения. До недавнего времени подавляющее большинство коммерческих игр для ПК и консолей создавалось с его помощью. Даже приход платформы .NET и языка программирования C# (читается "Си шарп") не смог кардинально изменить ситуацию. По C++ написана не одна сотня книг (многие из которых на русском языке). А так как данный курс не претендует на полноту изложения, настоятельно рекомендуем прочесть хотя бы пару из них. Не факт, что всё усвоишь, но основные моменты всё равно запомнишь.

Объектно-ориентированное программирование

Независимо от языка программирования объектно-ориентированный подход имеет ряд общих принципов:

  • Возможность создавать абстрактные типы данных, позволяющая наряду с предопределёнными типами данных (integer, bool, double и др.) вводить свои собственные типы данных (классы) и объявлять совего рода "переменные" таких типов данных (объекты). В этом случае программист оперирует не машинными терминами (переменная, функция), а объектами реального мира, поднимаясь тем самым на новый абстрактный уровень. Напрмер яблоки и людей нельзя умножать друг на друга. Низкоуровневый код запросто может совершить такую логическую ошибку, тогда как при использовании абстрактных типов данных такая операция становится невозможной.
  • ИНКАПСУЛЯЦИЯ допускает взаимодействие пользователя с абстрактными типами данных только через их интерфейс. Реализация объекта остаётся закрытой от посторонних глаз с целью предотвратить модифицирование (изменение) этих ообъектов. Использование инкапсуляции позволяет разработать автономный объект и использовать его, прибегая лишь к небольшому числу интерфейсных методов и не заботясь о его внутренней реализации.
  • НАСЛЕДОВАНИЕ позволяет на основе существующего класса создавать другие классы, которые автоматически получают его возможности. Зачастую многие классы бывают достаточно сложны, поэтому прибегают к их последовательной разработке, выстраивая иерархию классов от общего к частному.
  • ПОЛИМОРФИЗМ - возможность классов содержать методы с одинаковым названием, но с разным набором аргументов. Очень полезно, например, при обработке массивов данных разного типа.

Абстрактные типы данных (в нашем случае это классы) позволяют программисту вводить в программу переменные с желаемыми свойствами, когда возможностей существующих в языке типов данных не хватает. Связи между объектами реального мира зачастую настолько сложны, что для их эффективного моделирования необходим отдельный язык программирования. Разрабатывать специализированный язык программирования для каждой прикладной задачи - очень дорогое удовольствие. Поэтому во многие языки программирования (C++, PHP, Java, Python и др.) вводится объектно-ориентированный подход, который позволяет создать свой мини-язык путём введения классов и объектов. Переменными здесь уже являются объекты, в качестве типа данных для которого выступает класс. Класс описывает состав объекта - переменные и функции, которые тем самым определяют поведение объекта.

Использование заголовочных файлов (header files)

Функционал некоторого исходного файла (с расширением .cpp) может быть представлен его заголовочным фалом (с расширением .h). К заголовочным файлам часто обращаются как к интерфейсам. Например, ты можешь создать класс с названием Animal (от англ. Animal - животное), который воспроизводит функционал некоего "базового" животного в отдельно взятой игре. Класс будет объявлен в заголовочном файле (в C++, перед использованием чего угодно, это "что угодно" необходимо сначала объявить) (например animal.h) и затем уже будет реализован (имплементирован) в исходном файле, ассоциированном с ним (Animal.cpp). Таким образом, заголовочный файл предоставляет интерфейс для класса Animal, предназначенный для того, чтобы класс и функции этого класса были доступны для других файлов в пределах данной гипотетической игры.

Следующий пример показывает объявление нашего класса Animal, которое размещается в заголовочном файле animal.h. Это объявление затем становится интерфейсом класса Animal. Данный код (вообще, он считается "псевдокодом") не нужно отправлять на компиляцию в MS Visual C++. Его надо просто прочитать и попытаться в нём разобраться.

animal.h
class Animal
{
public:
Animal();		//Объявление конструктора
virtual ~Animal();	//Объявление деструктора

private:
	bool m_alive;
};

Наш простой класс имеет конструктор(external link), деструктор(external link) и одну переменную типа bool. Тип Boolean подразумевает всего два значения - true (истина) или false (ложь). Перед деструктором стоит служебное слово virtual, о котором мы расскажем позднее. Пока лишь отметим, что здесь оно также необходимо.

Но мы двигаемся дальше, и сейчас посмотрим на реализацию этого класса, размещённую в исходном файле Animal.cpp.

Animal.cpp
#include "animal.h"

Animal::Animal()	//Реализация конструктора
{
	m_alive = true;
}

Animal::~Animal()	//Реализация деструктора
{
	m_alive = false;
}

Для того, чтобы компилятор(external link) связал объявление класса с исходным фалом Animal.cpp, где содержится реализация класса, в начале Animal.cpp необходимо указать ссылку на заголовочный файл (animal.h). Для этого в начале Animal.cpp размещают специальное выражение #include (от англ. "вложить"). Оно просто "говорит" компилятору взять всё содержимое указанного файла и разместить его на этом месте. В нашем случае в начале исходного файла мы ставим команду #include "animal.h".

Но зачем вообще нужен заголовочный файл? Нельзя ли обойтись без него, разместив весь исходный код (объявление класса + его реализация) в одном исходном файле (.cpp)? Ответ - да, можно. Более того, ты можешь разместить функции (в нашем случае это конструктор и деструктор) прямо внутри объявления класса:

Animal.cpp
class Animal
{
	public:
		Animal()
		{
			m_alive = true;
		};

	virtual ~Animal()
		{
			m_alive = false;
		};

	private:
		bool m_alive;
}

Но такой подход имеет свои недостатки:

  1. Если ты размещаешь объявление класса в начале исходного файла. то теряешь возможность иметь отдельно вынесенный интерфейс для своего класса. На деле это усложнит код, что, в свою очередь, снизит его пригодность к обслуживанию и внесению измемнений (Maintainability) и, как следствие, усложнит его использование (usability). Подробнее об этом читаем здесь. Это не проблема, когда приложение содержит всего один класс. Но любая игра насчитывает десятки, а то и сотни классов, размещать которые в одном исходном файле будет крайне необдуманно.
  2. Если ты размещаешь реализацию класса внутри его объявления, у тебя нет защищённого интерфейса. Вся суть интерфейсов состоит в том, чтобы позволить разработать класс, который может быть позднее использован в других проектах БЕЗ отображения его реализации. Таким образом, для того чтобы выяснить, что делает данный класс, любой желающий может просмотреть его интерфейс (обычно это заголовочный файл с расширением .h). Но никто не может увидеть исходный код с реализацией этого класса, скрытый, как правило, в недрах динамически подключаемой библиотеки (.dll). Экспонирование функций динамически подключаемой библиотеки (.dll) в заголовочных файлах (.h) - обычная практика в программировании. И хотя, вместо DLL-библиотеки, у нас вполне открытый и доступный для прочтения исходный файл (.cpp), принцип остаётся тот же.


В нашей гипотетической игре, если мы захотим создать ещё один инстанс(external link) (от англ. instance - экземпляр) нашего класса Animal (уже в другом исходном файле), всё, что нам нужно будет сделать, это вставить выражение #include с указанием на интерфейс этого класса где-то в начале исходного файла, который будет создавать этот экземпляр. Само собой, выражение #include необходимо размещать ДО того места в коде, где ты пытаешься использовать данный класс. В следующем примере мы вставляем выражение #include "animal.h" в "какой-то другой исходный файл" и создаём после этого экземпляр класса Animal (изначально определённый в animal.h), назвав его dog:

SomeOtherSourceFile.cpp
#include "animal.h"

int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmdLine, int cmdShow)
{
	Animal dog;

	return true;
}

Этот принцип мы возьмём на вооружение и будем повсеместно применять при создании движка и игры. Едва ли не каждый класс будет создаваться со своим внешним интерфейсом (в виде заголовочного файла .h). При программировании движка эти интерфейсы будут отлинкованы (скомпонованы через выражения #include) в один "центральный" заголовочный файл Engine.h, что полностью удовлетворяет нашему требованию о "единой точке контакта". Весь функционал нашего будущего движка будет представлен в одном этом файле. Более того, для использования движка (например при создании игры) достаточно включить в проект его главный заголовочный файл, указав в начале исходного файла (.cpp) всего одно выражение #include "Engine.h". Чуть позднее мы ещё раз обсудим концепцию "единой точки контакта".

Служебное слово virtual и "виртуальный" деструктор

В нашем предыдущем примере с классом Animal, ты заметил ключевое слово virtual, стоящее перед деструктором класса. Оно служит для информирования компилятора о том, что данная функция может быть переопределена (overriden) дочерним классом.


ИГРОКОДИНГ  »  ИГРОКОДИНГ: Учебный курс  »  Программируем 3D-шутер от первого лица (FPS) (Win32, Cpp, DirectX9)  »  Часть 1. Создание движка  »  Основы языка Cpp

Contributors to this page: slymentat .
Последнее изменение страницы Среда 18 / Май, 2016 10:22:16 MSK автор slymentat.

Последние комментарии

No records to display

Хостинг