Так, например, на языке Си написаны первые реализации языков Java, Python, Perl и PHP. При этом во многих программах наиболее требовательные к ресурсам части принято писать на языке Си. Ядро программы Mathematica[86] написано на Си, а MATLAB, изначально написанный на Фортране, был переписан на Си в 1984 году[87]. Некоторые компиляторы идут в комплекте с компиляторами других языков программирования (включая C++) или являются составной частью среды разработки программного обеспечения. Практика возвращения маркера https://deveducation.com/blog/luchshie-ide-dlya-razrabotki-na-c/ ошибки, вместо кода ошибки, хоть и экономит количество передаваемых в функции аргументов, но в ряде случаев приводит к ошибкам в результате человеческого фактора.
Сравнение с альтернативными языками
Встраиваемые предметно-специфичные языки, реализуемые таким образом, всё равно требуют знания самого C++, что не обеспечивает полноценного разделения труда. Таким образом, возможности C++ по расширению возможностей самого C++ весьма ограничены[41][42]. В примере реализована функция чтения файла на языке Си, однако она требует соответствия функций fopen() и fread() стандарту POSIX, иначе они могут не выставлять переменную errno, что сильно усложняет как отладку, так и написание универсального и безопасного кода. На платформах, не соответствующих POSIX, поведение данной программы будет неопределённым в случае ошибки[⇨].
Избыточные и опасные возможности
- C++ оказал огромное влияние на другие языки программирования, в первую очередь на Java и C#.
- Невозможно статически проконтролировать даже все вызовы функции printf(), поскольку строка формата может создаваться в программе динамически.
- Существует много разных кодировок, в которых отдельный символ может быть запрограммирован разным количеством байт.
- Минимальное и максимальное значения каждого типа указывается в файле limits.h в виде макроопределений.
- Одна из групп, разрабатывающих язык C++ и направляющих комитету по стандартизации C++ предложения по его улучшению — это Boost, которая занимается, в том числе, совершенствованием возможностей языка путём добавления в него особенностей метапрограммирования.
- В примере реализована функция чтения файла на языке Си, однако она требует соответствия функций fopen() и fread() стандарту POSIX, иначе они могут не выставлять переменную errno, что сильно усложняет как отладку, так и написание универсального и безопасного кода.
Глобальные переменные не позволяют писать реентерабельные алгоритмы, а автоматическое выделение памяти не позволяет возвращать произвольную область памяти из вызова функции. Автоматическое выделение также не подходит для выделения больших объёмов памяти, поскольку может привести к порче стека или кучи[46]. Динамическая память лишена этих недостатков, но имеет большие накладные расходы при её использовании и более сложна в использовании. Макроопределения часто используются для обеспечения совместимости с разными версиями библиотек, у которых изменился API, включая те или иные участки кода в зависимости от версии библиотеки. Для этих целей библиотеки часто предоставляют макроопределения с описанием своей версии[38], а иногда и макросы с параметрами для сравнения текущей версии с заданной в рамках препроцессора[39]. Также макроопределения применяются для условной компиляции отдельных кусков программы, например для включения поддержки какого-либо дополнительного функционала.
Операторы работы с указателями и членами класса
Изначально эта библиотека была отдельным продуктом и её аббревиатура расшифровывалась иначе, но потом она вошла в стандартную библиотеку C++ в качестве неотъемлемого элемента. В названии отражено то, что для реализации средств общего вида (контейнеров, строк, алгоритмов) использованы механизмы обобщённого программирования (шаблоны C++ — template). В работах Страуструпа подробно описываются причины, по которым был сделан именно такой выбор.
C++ и функциональные и скриптовые языки
Хотя эти результаты не могут быть прямо перенесены на C++, но всё же представляют интерес с учётом того, что многие недостатки C++ унаследованы от Си. Несмотря на то, что большая часть кода C будет справедлива и для C++, C++ не является надмножеством C и не включает его в себя. Это отличает его от Objective C, ещё одного усовершенствования C для ООП, как раз являющегося надмножеством C.
Развитие и стандартизация языка
Авторы признают, что полученные ими данные не составляют репрезентативной выборки и воздерживаются от категоричных выводов. Этот пример демонстрирует, кроме всего прочего, использование контейнеров стандартной библиотеки шаблонов (STL). Здесь класс Figure является абстрактным (и, даже, интерфейсным), так как метод Draw не определён. Объекты данного класса нельзя создать, зато можно использовать ссылки или указатели с типом Figure. Выбор реализации метода Draw будет производиться во время выполнения исходя из реального типа объекта.
Это даёт возможность получить указатель на любой объект, включая исполняемый код, и обратиться по этому указателю, если только механизм защиты памяти системы этому не воспрепятствует. Язык весьма сложен и наполнен опасными элементами, которые очень легко использовать неправильно. Своей структурой и правилами он никак не поддерживает программирование, нацеленное на создание надёжного и удобного в сопровождении программного кода, напротив, рождённый в эпоху прямого программирования под различные процессоры, язык способствует написанию небезопасного и запутанного кода[96]. Многие профессиональные программисты склонны считать, что язык Си — мощный инструмент для создания элегантных программ, но в то же время с его помощью можно создавать крайне некачественные решения[97][98]. Несмотря на то, что стандартная библиотека входит в стандарт языка, её реализации идут отдельно от компиляторов.
Хотя есть и успешные попытки применения ФП в задачах реального времени без интеграции со средствами Си[52][53][54], всё же на данный момент (2013 г.) в низкоуровневой разработке применение в той или иной мере средств Си имеет лучшее соотношение трудоёмкости с результативностью. Много усилий было приложено разработчиками Python и Lua для обеспечения использования этих языков программистами на C++, так что из всех языков, достаточно тесно связанных с ФП, именно они чаще всего отмечаются в совместном использовании с C++ в одном проекте. Язык содержит средства, позволяющие программисту нарушать заданную в конкретном случае дисциплину программирования. Например, модификатор const задаёт для объекта свойство неизменности состояния, но модификатор mutable предназначен именно для принудительного разрешения изменения состояния внутри константного объекта, то есть для нарушения ограничения константности.
Освобождение ресурсов по ошибкам находится за основным алгоритмом для повышения читабельности, а переход осуществляется с помощью goto[66]. Предотвращение двух из трёх типов ошибок сводится к проверкам входных данных на область допустимых значений. Поэтому стандартом языка предусмотрена возможность анализа математических функций на ошибки. Начиная со стандарта C99 такой анализ возможен двумя способами, в зависимости от значения, хранимого в макросе math_errhandling. Для исполняемой программы стандартной точкой входа является функция с именем main, которая не может быть статической и должна быть единственной в программе. Исполнение программы начинается с первого оператора функции main() и продолжается до выхода из неё, после чего программа завершается и возвращает операционной системе абстрактный целочисленный код результата своей работы.
В стандартной библиотеке коды errno обозначаются через макроопределения и могут иметь одинаковые значения, что не даёт возможности анализировать коды ошибок через оператор switch. В языке нет специального типа данных для флагов и кодов ошибок, они передаются как значения типа int. Отдельный тип errno_t для хранения кода ошибки появился лишь в расширении K стандарта C11 и может не поддерживаться компиляторами[64].
Следует иметь в виду, что функция c16rtomb() для преобразования из 16-битной строки в многобайтовую работает не так, как задумывалось, и в стандарте C11 оказалась неспособной переводить из UTF-16 в UTF-8[29]. Исправление работы данной функции может зависеть от конкретной реализации компилятора. Альтернативным вариантом хранения размера строки с низким потреблением памяти может оказаться подход добавления в начало строки её размера в формате размера переменной длины[англ.]. Подобный подход применяется в протокольных буферах, однако только на этапе передачи данных, но не их хранения.
На данный момент C++ является одним из наиболее распространённых языков программирования в мире и позиционируется как язык общего назначения с уклоном в системное программирование[94]. Как правило возникновение ошибки требует завершения работы функции с возвращением индикатора ошибки. Если в функции ошибка может возникнуть в разных её частях, требуется освобождать ресурсы, выделенные в ходе её работы, чтобы предотвратить утечки. Хорошей практикой освобождения ресурсов считается их чистка в обратном порядке перед возвратом из функции, а в случае ошибок — освобождение в обратном порядке после основного return. В отдельные части такого освобождения можно сделать переход с помощью оператора goto[66]. Подобный подход позволяет вынести не связанные с реализуемым алгоритмом участки кода за пределы самого алгоритма, повышая читабельность кода, и схож с работой оператора defer из языка программирования Go.
Стандарт языка также допускает выполнение операций сравнения над нулевым указателем и над адресами в рамках массивов, структур и выделенных областей памяти. Также допускается работа с адресом элемента массива, следующим за последним, что сделано для облегчения написания алгоритмов. Однако сравнение указателей адресов, полученных для разных переменных (или областей памяти) не должно осуществляться, так как результат будет зависеть от реализации конкретного компилятора[49]. Для автоматически выделяемых переменных с помощью модификатора register можно давать подсказку компилятору о необходимости быстрого доступа к ним. Из-за ограниченного количества регистров и возможных оптимизаций компилятора переменные могут оказаться в обычной памяти, но тем не менее из программы на них невозможно будет получить указатель[47]. Модификатор register является единственным, который можно указывать в аргументах функций[48].
Первое время язык развивался вне формальных рамок, спонтанно, по мере встававших перед ним задач. Эти номера версий кросс-компилятора распространялись и на сам язык, но применительно к настоящему времени речь о версиях языка C++ не ведут. Одна из групп, разрабатывающих язык C++ и направляющих комитету по стандартизации C++ предложения по его улучшению — это Boost, которая занимается, в том числе, совершенствованием возможностей языка путём добавления в него особенностей метапрограммирования. Некоторые конкретные реализации и сторонние библиотеки предоставляют такие средства, как подсчёт ссылок и слабые ссылки[104], умные указатели[105], а также ограниченные формы сборки мусора[106], но все эти средства не являются стандартными, что, естественно, ограничивает их применение.
Язык также не был назван D, поскольку «является расширением C и не пытается устранять проблемы путём удаления элементов C»[7]. Язык возник в начале 1980-х годов, когда сотрудник фирмы Bell Labs Бьёрн Страуструп придумал ряд усовершенствований к языку Си под собственные нужды[7]. Так, язык Симула имеет такие возможности, которые были бы очень полезны для разработки большого программного обеспечения, но работает слишком медленно, а язык BCPL достаточно быстр, но слишком близок к языкам низкого уровня и не подходит для разработки большого программного обеспечения. Источником опасных ситуаций служит совместимость указателей с числовыми типами и возможность использования адресной арифметики без строгого контроля на этапах компиляции и исполнения.
Попыткой совмещения безопасности и скорости разработки, характерных для Java и C#, с возможностями C++ явился диалект Managed C++ (впоследствии — C++/CLI). Он разработан Microsoft в основном для переноса существующих проектов на C++ под платформу Microsoft.NET. Программы выполняются под управлением CLR и могут использовать весь массив библиотек .NET, но при этом накладывается ряд ограничений на использование возможностей C++, что фактически сводит C++ к C#.
IT курсы онлайн от лучших специалистов в своей отросли https://deveducation.com/ here.
Leave a Reply