`

СПЕЦІАЛЬНІ
ПАРТНЕРИ
ПРОЕКТУ

Чи використовує ваша компанія ChatGPT в роботі?

BEST CIO

Определение наиболее профессиональных ИТ-управленцев, лидеров и экспертов в своих отраслях

Человек года

Кто внес наибольший вклад в развитие украинского ИТ-рынка.

Продукт года

Награды «Продукт года» еженедельника «Компьютерное обозрение» за наиболее выдающиеся ИТ-товары

 

C++ – 20 лет победного несовершенства

+11
голос

Сегодня трудно даже поверить в то, что юбиляру – языку программирования C++, гибель предрекали мало того что неоднократно, так еще и очень убедительно. Последний раз юбиляра уверенно отправляли «на покой» 10 лет назад, когда многообещающий преемник – Java – как будто устранил все неисчислимые недостатки C++ и вроде как без потерь в производительности.

В краткой преамбуле к статье есть лежащий на поверхности один из верных ответов на вопрос о причинах живучести и неснижающейся популярности обладающих массой известных недостатков языков C и C++. Да, C++ и очень близкий к нему C критикуют много и заслуженно. Но все альтернативы, увы, до сих предлагали «как будто» устранение всех их недостатков «вроде как» без потерь в производительности созданных программ. Можно до хрипоты спорить об этих «как будто» и «вроде как», можно бросаться цифрами, проводить тестирования – и все это будет впустую. Потому как C++ исполнилось 20 лет, и вместе со своим предшественником C эти языки образуют инструментальную базу для громадной индустрии программного обеспечения: по некоторым оценкам, до 38% сегодняшних программных проектов реализуются на языке C++, а уж C буквально царит в мире ПО для неисчислимого количества встраиваемых компьютеров.

C 7 по 10 ноября в Лас-Вегасе, городе, который никогда не спит, прошли празднования 20-й годовщины языка С++. Бьярн Страуструп, Стенли Липпман, Андрей Александреску, Скотт Мейерс и многие другие гуру С++ произнесли спичи в честь юбиляра.

И хоть о юбилярах положено говорить много хорошего, мы откажемся от этого правила и для начала покажем самую малую долю того «ужасного обаяния C++», за которое этот язык (равно как и его предшественника C) одновременно и любят, и уважают, и ненавидят. Если вы не знаете C или C++, не отчаивайтесь – пример будет крохотным, и мы его разберем по косточкам, так, чтобы самое ценное в нем – натуральный ужас, стало понятным даже тем, кто не знает, чего надо бояться. Для начала маленькое отступление. В языках программирования высокого уровня обычно используют блочную структуру программы. Блоки позволяют, например, локализовать переменные, перекладывая заботы об управлении памятью на компилятор. Так, память, выделенная под переменные, объявленные в начале блока, обычно автоматически освобождается перед завершением блока. Естественно, в основе такого принципа лежит простой механизм стека – в начале блока ссылка на каждую объявленную в нем переменную записывается на вершину специального стека (обычно невидимого для прикладного программиста), а в конце блока ссылки автоматически снимаются со стека, и адресуемые ими участки памяти помечаются как свободные. Естественно, для того чтобы такая блочная структура могла вообще работать, необходимо строгое соблюдение требования вложенности блоков. Только в этом случае после выхода из вложенного блока на стеке управления памятью автоматических переменных останутся адреса наружного (по отношению ко вложенному) блока, и все будет работать в соответствии с описанной логикой.

Практически во всех языках программирования высокого уровня это правило свято соблюдается. Для многих языковых конструкций есть правила, требующие строго определенной их иерархической блочной структуры. И в C++, и в C в том числе. В этих языках для обозначения начала и конца блока используются символы "" и "" соответственно. А теперь – обещанный ужас:

switch (len % 8) 
  case 0: 
    do  HAL_IO_PORT = *pSource++;
      case 7:  HAL_IO_PORT = *pSource++; 
      ...
      case 1:  HAL_IO_PORT = *pSource++; 
     while (--n > 0);
  

Для понимания, что это – натуральный ужас, вам вовсе не надо знать C++ или С. Просто следите за соблюдением правила вложенности блоков. Видите? Блоки, которые, по логике вещей, должны быть на одном уровне иерархии (они начинаются со слова case), почему-то на одном уровне не находятся. И между тем это совершенно законный работающий код на С++. Более того, в C++ законность работоспособности данного кода унаследована от C. Именно на C этот великолепный кошмар был изобретен 9 ноября 1983 г. (кстати, еще один юбилей!) программистом Томом Даффом. Для подтверждения законности этого кода в свое время потребовалось личное участие Ларри Рослера, председателя подкомитета ANSI по стандартизации языка C. А в оригинальном электронном письме Тома Даффа более чем двадцатилетней давности, где он сообщает об изобретении данного кода, можно найти слова, которые дают еще один ответ на поставленный в начале статьи вопрос: «Я поражен – после 10 лет программирования на C в этом языке еще остаются неразведанные мною уголки».

C++ : вчера и сегодня

Созданный там же, где и предшественник, язык C, – в Исследовательском центре компьютерных наук Bell Labs, С++ в полной мере повторил и приумножил успех С. Причем сразу следует заметить, что успехи и C, и C++, основывались исключительно на свойствах этих языков как инструментов. Ведь по сути никакой коммерческой «раскруткой» языков программирования компания AT&T, в состав которой входила Bell Labs, не занималась. C распространялся как бы «в нагрузку» к ОС UNIX, C++ вообще фактически не имел «товарного» характера. Более того, AT&T своевременно поддержала политику стандартизации открытых спецификаций этих языков. Благодаря этой модели распространения разработок в области языков программирования, принятой AT&T, сегодня доступны не только высококлассные коммерческие компиляторы С и С++, но и великолепный GCC, ставший главным инструментом в мире Open Source.

Впрочем, достоинства модели распространения ПО – это вещь весьма абстрактная. А у языка C++ все-таки есть вполне конкретный автор, которого замечательная во всем модель свободного распространения открытых стандартных спецификаций в какой-то мере обделила. Американец датского происхождения, получивший образование в Дании и Англии, Бьярн Страуструп никаких отчислений за спецификации C++ не получает. Благо специфика любого языка программирования – сложность (даже самый «простой» язык в действительности очень сложен, что отлично демонстрируется хоррор-программой Тома Даффа). Ну а там, где люди вынужденно сталкиваются со сложностью, всегда есть потребность в единственном универсальном средстве борьбы с нею – в документации. Изданные во множестве стран мира суммарным легальным тиражом свыше миллиона экземпляров сложные книги Бьярна Страуструпа о сложном языке C++ – лучшее тому подтверждение.

Основатель Netscape Марк Андрессен на примере Java охарактеризовал результаты попыток создать сверхоснащенный язык программирования на все случаи жизни: «Java была намного более дружелюбным по отношению к программисту языком, чем С или С++ была несколько лет назад, до тех пор, пока ее не сделали такой сложной. Скоро, вероятно, ее будет еще труднее изучить, чем С++».

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

Самым интересным в C++, пожалуй, является замысел Страуструпа создать одновременно высокоуровневый язык, несоизмеримо более высокоуровневый по сравнению с C, и в то же время как максимально приближенный по низкоуровневым возможностям к C, так и в разумной степени совместимый с C. Возможно, именно благодаря такой затее современный C++ – вовсе не «C с классами» и даже не «объектно-ориентированный язык». C++ – это так называемый «полипарадигменный язык». Сам Бьярн Страуструп слово «парадигма» в отношении своего детища употреблять не любит и предпочитает говорить о различных стилях программирования, допустимых при использовании С++. Структурное программирование, программирование с применением абстрактных типов данных, обобщенное и функциональное программирование – в принципе, в умелых руках C++ позволяет использовать как каждый из стилей (или парадигм), так и комбинировать их в одном программном проекте. Естественно, такая гибкость дается немалой ценой. Особенно если учитывать, что основные затраты на язык программирования – это вовсе не стоимость приобретения лицензионного компилятора, а время на изучение собственно языка. А вот сколько времени нужно на то, чтобы виртуозно овладеть С++, сказать очень трудно. Если несоизмеримо более простой C в свое время поразил Тома Даффа, C-программиста с десятилетним стажем, обилием «неразведанных уголков», то что тогда можно говорить о С++, отдельным техникам которого посвящены такие непростые книги, как 550-страничное руководство по шаблонам «C++ Templates: The Complete Guide»? И между тем, даже при этом уровне сложности на юбилейной конференции, посвященной 20-летию С++, Бьярн Страуструп отметил, что С++ уже начал утрачивать, как бы это лучше сказать, «крутизну»: «C++ стал мощным, отработанным инструментом, но использовать его уже не круто (no cool)». Возможно, лишение С++ ореолов «крутизны» и «ковбойства» пойдет только на пользу. И на пользу не столько языку, сколько использующим его программистам и организациям. По крайней мере, период, когда С++ выбирался языком реализации исключительно потому, что это было «круто», безвозвратно прошел. А ведь такой период в истории языка был, и именно благодаря ему столь убедительно звучали голоса предрекающих скорый уход C++ с арены популярных инструментальных средств.

С++ завтра, или 0х уж этот С++0x

Сегодняшний С++ многим кажется языком, пик развития которого уже в прошлом. И, по большому счету, это мнение верно. 20 лет – срок, достаточный для достижения зрелости даже такой сложной разработкой. И все-таки говорить о том, что развитие языка после стандартизации ISO в 1998 г. полностью прекратилось, нельзя. В будущем более чем трехмиллионная армия С++-программистов получит обновленный инструмент, отвечающий формирующемуся стандарту C++0x. Говорить о точной дате появления нового стандарта и, тем более, о времени выхода компиляторов, понимающих C++0x C++, пока очень трудно. Дело в том, что C++ используется в самых разнообразных областях применения, и, соответственно, от разнообразия желанных улучшений просто рябит в глазах. Однако разработчики стандартных спецификаций строго следуют важнейшему правилу, заложенному в идеологию C++ его создателем Бьярном Страуструпом: «C++ – это универсальный язык, пригодный к использованию во многих прикладных областях благодаря удачному компактному набору общеупотребимых средств». Если вспомнить судьбу языков, в которые включались специализированные средства (например, языковая поддержка потокового программирования), можно уверенно говорить о безошибочности выбора Страуструпа.

Вторая сложность в формировании С++0x кроется в специфике типизации, принятой в нынешнем С++. С одной стороны, требование к повышению надежности разрабатываемых программ вынуждает создателей спецификаций языка как можно строже относиться к системе типов и к контролю за ними. В идеале, конечно, язык со строгой типизацией был бы просто замечательным. Но это в идеале. В реальной жизни программистам необходимы одновременно и возможность создания абстрактных типов, и возможность, например, доступа к ячейкам памяти или регистрам, расположенным в адресном пространстве по известным адресам. В реальной жизни программисты стараются использовать как можно большую часть ранее написанного кода повторно, и строгая типизация в С++0x привела бы к тому, что все исходные тексты, где используются массивы, указатели, объединения, явные и неявные приведения типов, пришлось бы отправить в мусорную корзину.

И наконец, к несовершенству реальной жизни следует отнести и жесткие требования к высокой производительности полученного с помощью компиляции кода. Неважно, что сейчас эпоха «дешевых гигагерцев» – далеко не во всех областях применения эти гигагерцы нужны, далеко не везде компьютерам разрешена работа «обогревателем по совместительству». Иными словами, требование к минимальности среды времени исполнения C++0x остаются в силе. С++0x сохраняет минимальной дистанцию между программистом и «железом», отображая один в один целевую машину языковыми средствами. Машинные слова, байты, регистры – все это в C++0x остается.

Наряду с принципами, по которым формируется C++0x, уже известно и о некоторых конкретных нововведениях. Так, в стандартной библиотеке появится поддержка кортежей – tuples. Кортежи – это коллекции определенного количества (до десяти) объектов разных типов. Обычно они поддерживаются в интерпретируемых языках и позволяют эффективно возвращать множества значений из функций, проводить множественные сравнения или присваивания и прочие групповые операции над гетерогенными наборами данных. Перечень базовых операций с кортежами включает механизмы их создания, определения числа элементов и типа каждого элемента, а также доступа как к кортежу в целом, так и к каждому его элементу. Реализацию кортежей в C++0x можно считать очень удачной, так как она фактически не потребовала модификаций ядра языка.

К полезным модификациям также следует отнести «облатку» над ссылками С++. В текущих спецификациях языка объекты типа «ссылка» не являются равноправными с прочими объектами базовых типов. Так, например, ссылки нельзя использовать в качестве параметров в шаблонных конструкциях. В некоторых случаях такое неравноправие ссылок, например с указателями, вынуждало программистов заниматься различными ухищрениями. В С++0x от несправедливости решили избавляться малой кровью и ввели специальный класс-«облатку» (reference_wrapper), позволяющий скрыть в объекте класса ссылку так, чтобы одновременно сохранить ее свойства и устранить ограничения. Реализация «облатки» такова, что из ссылок на объекты разных типов в С++0x можно, к примеру, формировать кортежи.

Еще одно нововведение С++0x – возвращение в язык старого ключевого слова auto, но в совершенно новом качестве. Идея нового auto очень проста и заключается в ответе на вопрос: какова степень избыточности следующего выражения:

const double Salary = 2563.92 ;

Если немного подумать, то почти вся левая часть, касающаяся объявления переменной, избыточна. Потому как информация о типе совершенно явно указывается в правой части, в значении, используемом для инициализации переменной. Соответственно, в С++0x можно будет полениться и доверить компилятору дописать очевидное, заменив его словом auto:

const auto Salary = 2563.92 ;

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

В общем, возвращаясь к оценкам перспектив развития языка, можно уверенно говорить, что революции с появлением C++0x не произойдет. Что-то будет удобнее, что-то – лучше, все сделанное ранее останется работоспособным. И это не может не радовать.

Ready, set, buy! Посібник для початківців - як придбати Copilot для Microsoft 365

+11
голос

Напечатать Отправить другу

Читайте также

 

Ukraine

 

  •  Home  •  Ринок  •  IТ-директор  •  CloudComputing  •  Hard  •  Soft  •  Мережі  •  Безпека  •  Наука  •  IoT