﻿# Как различить C и C\+\+ разработчиков по их коду

Так уж случилось, что я пишу код для разных IoT\-железок, связанных с электричеством, типа зарядных станций автомобилей\. Поскольку аппаратных ресурсов, как правило, вполне достаточно, то основным фокусом является не экономия каждого байта и такта процессора, а понятный и надежный код\. Поэтому в проекте разрабатывают под Embedded Linux и в качестве основного языка используют C\+\+ в его современном варианте \- C\+\+17, активно поглядывая на фичи из стандарта 20\-го года и новее \(подождите, кто сказал Rust?\)\.

![0946_C_Cpp_developers_difference_ru/image1.png](https://import.viva64.com/docx/blog/0946_C_Cpp_developers_difference_ru/image1.png)


> Мы опубликовали и перевели эту статью с разрешения правообладателя\\\. Автор статьи – Кирилл Овчинников \\\(\[kirill\\\.ovchinn@gmail\\\.com\]\(mailto:kirill\.ovchinn@gmail\.com\)\\\)\\\. Оригинал опубликован на сайте \[Habr\]\(https://habr\.com\)\\\.



![0946_C_Cpp_developers_difference_ru/image2.png](https://import.viva64.com/docx/blog/0946_C_Cpp_developers_difference_ru/image2.png)

Иногда запускаются новые проекты на той же платформе, с теми же процессами и с переиспользованием многих уже существующих компонентов, и тогда в эти проекты мы ищем программистов, с учетом вышесказанного \- программистов на C\+\+\. В embedded, тем не менее, чистый C все еще очень популярен, и нередко собеседоваться на вакансию C\+\+ Developer'а приходят именно сишники\. Логика у человека простая: языки, на первый взгляд, довольно близкие и почти обратно\-совместимые, базовый синтаксис одинаков, про ООП кандидат что\-то слышал, и значит, основная база уже есть, и он сможет [легко освоить C\+\+ за 21 день](https://pikabu.ru/story/kak_vyiuchit_c_za_21_den_891929) в процессе работы, поэтому можно наплести про "с C\+\+ тоже работал", начать писать на "Си с классами" и все получится\. В то время как в новой команде таких "бывших сишников" уже и так набралось несколько, и такой кандидат нам уже не подойдет, на оставшиеся позиции нужен именно опытный плюсовик\-затейник, который будет активно внедрять best practices и наставлять на code review на путь истинный менее опытных коллег\.

![0946_C_Cpp_developers_difference_ru/image3.png](https://import.viva64.com/docx/blog/0946_C_Cpp_developers_difference_ru/image3.png)

К счастью, несмотря на кажущуюся схожесть языков, чем лучше ты знаешь каждый из них, тем больше ты понимаешь, что они очень разные\. И в итоге разработчика на C от разработчика на C\+\+ можно легко отличить на интервью или ревью, и команда даже на основе своего опыта и своих предпочтений набросала список звоночков, которые выдают сишников при собеседовании на C\+\+\-позицию и вызывают необходимость уже более детального разговора на тему "а почему ты сделал так"\. Итак, признаки того, что разработчик программирует не на C\+\+, а на "C с классами":

1\. Использует _<stdint\.h\>_, _<string\.h\>_, _<stdio\.h\>_ вместо _<cstdint\>_, _<cstring\>_, _<cstdio\>_;

2\. Использует _malloc\(\)_ и _free\(\)_ кроме явно предназначенных для этого мест \(типа кастомных аллокаторов\);

3\. Использует ручное управление памятью с _new_ и _delete_, вместо RAII и умных указателей;

4\. Использует _char\*_\-строки и функции _<string\.h\>_ вместо _std::string_ и _std::string\_view_; \(единственное исключение \- строковые константы через _constexpr_\)\. Использует функции из _<time\.h\>_ вместо _std::chrono_\. Использует _atoi\(\)_ вместо _stoi\(\)_\. Использует функции из _<stdio\.h\>_ вместо _std::filesystem_ и потоков ввода\-вывода\. Использует _<pthread\.h\>_ вместо _std::thread_;

5\. Когда нужно имплементировать алгоритм или контейнер независимый от типа данных, которыми он оперирует, использует _\#define_\-макросы или _void\*_\-указатели вместо темплейтов;

6\. Для объявления констант использует _\#define_ вместо _const_ и _constexpr_;

7\. Использует C\-style массивы вместо _std::array_;

8\. Использует _NULL_ вместо _nullptr_;

9\. Пишет _\(type\)something_ вместо _static\_cast<type\>\(something\)_;

10\. Использует простые указатели на функции вместо _std::function_;

11\. Использует _enum_ вместо _enum class_ даже для простых перечислений;

12\. Для функций, не изменяющих состояние объектов, не использует _const_ при объявлении; Для конструкторов забывает _explicit_\. Для деструкторов забывает _virtual_ :\)

13\. При разработке в ООП\-стиле, объявляет все члены класса как _public_;

14\. Если вам нужно вернуть из функции несколько разных значений \(например, результат работы и/или код ошибки\), то одно из них возвращает через _return_, а другое \- по указателю или по неконстантной ссылке, вместо использования _std::optional_, _std::pair/std::tuple_ \(особенно хорошо в паре со structured binding\) или просто возврата _struct_;

15\. Объявляя новую переменную с типом\-структурой, везде пишет _struct_ в имени типа, или наоборот, при объявлении новой структуры пишет _typedef struct_ вместо просто _struct_;

16\. Не использует неймспейсы при структурировании кода;

17\. Использует _union_ вместо _std::variant_ \(кстати, для каламбура типизации использовать _union_ тоже нельзя, он нарушает active member rule\);

18\. Пишет реализации общеиспользуемых алгоритмов \(_foreach_, _transform_, _find\_if_, _sort_, _lower\_bound_, и т\.д\.\) вручную даже если они есть в _<algorithm\>_;

19\. При простой итерации по элементам контейнера пишет многословные конструкции вместо _range\-based for_\. Не использует _auto_ и _using_ в многословных конструкциях типов;

Плюс немного дополнений из комментариев:

20\. Использует битовые поля вместо _std::bitset_;

21\. Использует си\-шные библиотеки на прямую без уровня абстракции над ней;

22\. В заголовочных файлах куча инклудов, которые можно было в принципе там и не писать \(incomplete class\)\.

Если вы матерый плюсовик, и при чтении этого списка у вас полыхает несогласие с некоторыми из этих пунктов и бурлит желание поспорить — это отлично, значит вы действительно матерый плюсовик\. А для остальных, пожалуй, добавлю очевидную оговорку, **что для многих описанных практик есть исключения и все зависит от конкретной ситуации\.** Например:

* у вас может быть очень много соприкосновений с чисто сишными библиотеками;
* проект может использовать древний тулчейн, умеющий только C\+\+98 \(правда, при работе в таких проектах нужно требовать тройную оплату и дополнительную страховку за вредность, а лучше вообще избегать подобного\);
* вы используете Qt, где своя модель владения, и _new_ там используется на каждом шагу;
* _std::string_ не подойдет когда вы не можете работать с динамической памятью \(хотя, и тут можно придумать что\-нибудь интересное с кастомными аллокаторами\);
* абстракции рано или поздно [протекают](https://habr.com/ru/company/selectel/blog/512796/): вы не сможете создать _std::fstream_ из существующего и открытого posix file descriptor \(хотя некоторые реализации stdlib такое умеют\), а средствами _<thread\>_ вы не сможете задать приоритет потоку, и еще много чего;

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