<MyRusakov.ru />

Программирование на C++ в Unreal Engine 5

Программирование на C++ в Unreal Engine 5

Данный курс научит Вас созданию игр на C++ в Unreal Engine 5. Курс состоит из 12 разделов, в которых Вас ждёт теория и практика. Причём, в качестве практики будет создан весьма крупный проект объёмом свыше 5000 строк качественного кода, который уже на практике познакомит Вас с принципами создания игр на C++ в Unreal Engine 5.

Параллельно с курсом Вы также будете получать домашние задания, результатом которых станет, в том числе, полноценная серьёзная работа для портфолио.

Помимо самого курса Вас ждёт ещё и очень ценный Бонус: «Тестирование Unreal-проектов на Python», в рамках которого Вы научитесь писать очень полезные тесты для тестирования самых разных аспектов разработки игр.

Подробнее
Подписка

Подпишитесь на мой канал на YouTube, где я регулярно публикую новые видео.

YouTube Подписаться

Подписавшись по E-mail, Вы будете получать уведомления о новых статьях.

Подписка Подписаться

Добавляйтесь ко мне в друзья ВКонтакте! Отзывы о сайте и обо мне оставляйте в моей группе.

Мой аккаунт Мой аккаунт Моя группа
Опрос

Какая тема Вас интересует больше?

Заголовочные файлы в C++: как правильно разделять код на .h и .cpp (практическое руководство)

Заголовочные файлы в C++: как правильно разделять код на .h и .cpp (практическое руководство)

Правильная работа с заголовочными файлами в C++ экономит часы сборки и спасает от «multiple definition» и «undefined reference». Это руководство объясняет, что класть в .h и .cpp, как использовать include guards и #pragma once, когда нужны forward declaration, как разорвать циклические зависимости и какие ошибки встречаются чаще всего.

Что такое заголовочный файл в C++

Заголовочный файл (.h/.hpp) — место, где объявляются интерфейсы: функции, классы, константы, типы. Исходный файл (.cpp) — место, где находятся определения (реализации). Такой подход ускоряет сборку, улучшает модульность и позволяет переиспользовать код.

Что хранить в .h и в .cpp

  • В .h: объявления функций, классов, enum, константные интерфейсы, инлайновые и шаблонные функции, документация к API.
  • В .cpp: определения функций и методов, приватные детали реализации, статические объекты с внутренним связыванием.

Include guards и #pragma once

Чтобы заголовок не подключился дважды, используйте include guards или #pragma once.

// math.hpp
#ifndef MATH_HPP
#define MATH_HPP

namespace math {
  double avg(int a, int b); // объявление
}

#endif // MATH_HPP

Альтернатива — #pragma once (поддерживается большинством компиляторов):

// util.hpp
#pragma once
int add(int a, int b);

Минимальный пример разделения на .h и .cpp

// math.hpp
#ifndef MATH_HPP
#define MATH_HPP
namespace math { double avg(int a, int b); }
#endif

// math.cpp
#include "math.hpp"
namespace math {
  double avg(int a, int b) { return (a + b) / 2.0; }
}

// main.cpp
#include <iostream>
#include "math.hpp"
int main() {
  std::cout << math::avg(3, 5) << "\n";
}

// Компиляция (GCC/Clang):
g++ -std=c++20 -Wall -Wextra -O2 main.cpp math.cpp -o app

Инлайн и constexpr в заголовках

Определения неинлайновых функций в .h вызывают множественные определения при линковке. Если маленькая функция нужна прямо в заголовке — объявляйте её inline или constexpr (когда это уместно).

// math_inline.hpp
#pragma once
inline int square(int x) { return x * x; }
constexpr int add1(int x) { return x + 1; }

Шаблоны и заголовки

Шаблонные функции и классы обычно полностью размещаются в .h, потому что компилятору нужны их определения при инстанцировании. Выносить реализацию шаблонов в .cpp нельзя (за редкими продвинутыми приёмами).

// clamp.hpp
#pragma once

template<typename T>
T clamp(T value, T lo, T hi) {
  if (value < lo) return lo;
  if (value > hi) return hi;
  return value;
}

Forward declaration и разрыв циклических зависимостей

Если в заголовке нужен только указатель или ссылка на тип, достаточно предварительного объявления (forward declaration), а полный заголовок подключите в .cpp. Так вы уменьшаете зависимости и избегаете циклов.

// a.hpp
#ifndef A_HPP
#define A_HPP
class B;               // forward declaration
class A {
public:
  void set(B* b);
private:
  B* b_{nullptr};      // указатель: полного типа пока не нужно
};
#endif

// a.cpp
#include "a.hpp"
#include "b.hpp"       // здесь уже нужен полный тип B
void A::set(B* b) { b_ = b; }

// b.hpp
#ifndef B_HPP
#define B_HPP
class A;               // forward declaration
class B {
public:
  void ping(A* a);
};
#endif

// b.cpp
#include "b.hpp"
#include "a.hpp"
void B::ping(A* /*a*/) {}

Избегайте взаимных #include в заголовках без необходимости — это главный источник циклических зависимостей и роста времени сборки.

Организация include'ов и стиль

  • В .cpp файлах первым подключайте «собственный» заголовок: это быстро выявляет пропущенные зависимости в нём самом.
  • #include "..." — для ваших файлов, #include <...> — для системных и библиотечных.
  • Подключайте только то, что реально используете (подход IWYU).
  • Стабильный порядок include'ов улучшает повторяемость сборки.
// file.cpp
#include "file.hpp"     // свой заголовок — первым
#include <iostream>
#include <vector>

Сокрытие реализации через PIMPL (по желанию)

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

// widget.hpp
#ifndef WIDGET_HPP
#define WIDGET_HPP
#include <memory>
class Widget {
public:
  Widget();
  ~Widget();
  void draw();
private:
  struct Impl;
  std::unique_ptr<Impl> p_;
};
#endif

// widget.cpp
#include "widget.hpp"
#include <iostream>
struct Widget::Impl {
  void drawImpl() { std::cout << "draw\n"; }
};
Widget::Widget() : p_(std::make_unique<Impl>()) {}
Widget::~Widget() = default;
void Widget::draw() { p_->drawImpl(); }

Типичные ошибки: ODR и multiple definition

  • Определение переменной в заголовке: приведёт к множественным определениям при линковке.
// utils.hpp — так НЕЛЬЗЯ
int g_counter = 0; // multiple definition

// Правильно через extern:
// utils.hpp
#ifndef UTILS_HPP
#define UTILS_HPP
extern int g_counter;
#endif

// utils.cpp
#include "utils.hpp"
int g_counter = 0;
  • Inline-переменные (C++17+) в заголовке — допустимый способ иметь одну «общую» сущность без multiple definition.
// config.hpp (C++17+)
#pragma once
inline int g_limit = 100; // OK: одно ODR-определение на программу

Чек‑лист по заголовочным файлам

  • Каждый .h защищён include guard или #pragma once.
  • В .h — только объявления, реализация в .cpp (кроме inline/constexpr/шаблонов).
  • Минимизируйте зависимости: используйте forward declaration там, где можно.
  • В .cpp подключайте свой .h первым.
  • Не определяйте глобальные не-inline переменные в заголовках — используйте extern или inline переменные (C++17+).
  • Следите за циклическими зависимостями и размером заголовков: по возможности применяйте PIMPL.

Куда двигаться дальше

Научиться грамотно организовывать проект, управлять зависимостями и сборкой помогает практика. Хотите системно прокачать навыки современного C++? Посмотрите программу и задания курса — Прокачать C++ от нуля до гуру на практическом курсе.

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (https://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: https://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: https://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:

    Она выглядит вот так: Как создать свой сайт

  2. Текстовая ссылка:

    Она выглядит вот так: Как создать свой сайт

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):

Комментарии (0):

Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.