Недружелюбность C++ к новичкам: взгляд Unity-разработчика

Привет, меня зовут Максим. Я программист-самоучка, свою первую строчку кода написал еще в 1994 году и на текущий момент принял участие где-то в 10 игровых проектах.

За это время мне пришлось писать на множестве различных языков:

  • с 8 по 11 класс самостоятельно изучал BASIC и Turbo Pascal в компьютерном классе школы;
  • писал игры для калькулятора МК-61 дома (в средине 90-х компьютер был роскошью);
  • Delphi на первой работе;
  • Lua и немного C++/CLI на второй, с которой я вошел в GameDev 14 лет назад;
  • Python и C++ под Bigworld на третьей;
  • C# под Unity на текущей;
  • в свободное время делаю свои проекты;
  • та же некоторое время посвятил изучению Rust и D в попытке найти альтернативу C++.

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

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

Эта статья — попытка осмыслить, почему у C++ такой высокий порог вхождения, чем он уступает другим языкам. А также почему я считаю его плохим языком для программистов-новичков.

Целевая аудитория:

  • те, кто считает, что начинать нужно со сложного и максимально эффективного;
  • те, кто много писал на C#|Java и подобных и хочет покорить новые вершины;
  • те, кто имеет некоторый опыт C++, но, как и я, понимает, что нет предела совершенству;
  • практикующие системные программисты на языках типа Rust и D, которые смогут рассказать, почему выбрали их альтернативой C++;
  • гуру C++, которые в комментариях просветят неопытных, чем и на сколько оправданы те или иные подходы языка, затронутые в статье.

Иллюстрация Ульяны Патоки

Очень надеюсь, что подобранная мною информация окажется полезной и вызовет только конструктивное обсуждение.

Компиляция и линковка

Первое, с чем приходится столкнуться, — архаичное правило, что «единицей компиляции является файл», которое перекочевало в С++ из языка С. Это означает, что если файл ссылается на что-то из других файлов, то нужно каким-то образом сообщить компилятору тот минимум информации, что позволит ему выполнить работу. Такой информацией являются преимущественно объявления используемых этим файлом функций — копии заголовка функций без тела.

Чтобы компилятор получил эту информацию, сначала отрабатывает препроцессор. Он не менее архаичным способом переносит все содержимое каждого включаемого файла в тот, который компилируется. Если в каком-то из этих файлов были включения, они тоже добавляются.

Чтобы не включать избыточую для компилятора информацию (ему код включаемых функций не нужен), договорились все заголовки функций выносить в отдельные файлы, которые и назвали заголовочными. Таким образом возникло разделение, что заголовочные файлы имеют расширение .h (иногда пишут .hpp, чтобы явно указать, что это написано на С++), а файлы с кодом — в файлах с расширением .cpp.

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

Приведу пример.

Сейчас я работаю над проектом на C# под Unity, который состоит из 4300 файлов с кодом. Это 25 мегабайт исходников! Время полной компиляции проекта на моем компьютере занимает 10 секунд. Берем пустой проект на Unreal, добавляем единственный объект с С++ классом. Вносим туда малейшее изменение — время компиляций и линковки до запуска, минимум 7 секунд на том же i7-8700.

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

1. Так как заголовочные файлы могут включать другие заголовочные файлы, то возможна ситуация, что одно и то же объявление функции будет подставлено более одного раза.

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

// unit.h
#pragma once
 
void f1(); // пример описания заголовка (сигнатуры) функции f1()
bool f2(int x); //пример описания заголовка (сигнатуры) функции f2()

Но вы можете увидеть и такой вариант решения проблемы, которым пользовались до появления поддержки #pragma once:

// unit.h
#ifndef __UNIT_H__
#define __UNIT_H__
 
void f1(); // пример описания заголовка (сигнатуры) функции f1()
bool f2(int x); //пример описания заголовка (сигнатуры) функции f2()
 
#endif

Здесь для каждого файла программист придумывал уникальное имя константы препроцессора. Тогда код между #define и #endif включался только при первой попытке подключить этот заголовочный файл к текущему компилируемому файлу.

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

Похожие статьи:
Я Product Owner / Product Manager в американском стартапе Atiim Inc. Два года назад я пришла в эту компанию в качестве QA Engineer. Всего через полгода работы...
[Об авторе: Сергей Королев — управляющий директор в Railsware с более чем 15-летним опытом работы в ИТ: от стартапов до корпораций. Инженер,...
✔ Для трудоустройства необходим хороший практический опыт?✔ Мечтаете зарабатывать больше 500 дол. за заказ?✔ Желаете профессионально...
Оператор мобильной связи «Билайн» объявил об открытии сезона акционных предложений по супервыгодным ценам, приуроченного к...
В предпраздничном ажиотаже главное — успеть вовремя остановиться и подумать о светлом будущем. Мы подумали :) 6 февраля...
Яндекс.Метрика