Определенные языковые функции, даже если они являются дополнениями, могут полностью изменить способ использования языка. В качестве одного примера рассмотрим этот случай:
lock_mutex(&mutex);
// call some functions
...
unlock_mutex(&mutex);
Если в приведенном выше коде задействованы функции вызова, реализованные в C ++, мы можем столкнуться с целым миром проблем, поскольку любой из этих вызовов функций может выдать, и мы никогда не разблокируем мьютекс в этих исключительных путях.
Деструкторы больше не находятся в сфере удобства, чтобы помочь программистам не забыть освободить / освободить ресурсы в этот момент. RAII становится практическим требованием, потому что по-человечески невозможно предвидеть каждую отдельную строку кода, которая может привести к нетривиальным примерам (не говоря уже о том, что эти строки могут не генерироваться сейчас, но могут позже с изменениями). Возьмите другой пример:
void f(const Foo* f1)
{
Foo f2;
memcpy(&f2, f1, sizeof f2);
...
}
Такой код, хотя в целом безвреден в C, подобен адскому огню, царящему хаосу в C ++, потому что memcpy
бульдозеры разбивают биты и байты этих объектов и обходят такие вещи, как конструкторы копирования. Такие функции , такие как memset
, realloc
, memcpy
и т.д., в то время как ежедневные инструменты среди разработчиков C привыкли смотреть на вещи в довольно однородным образом битов и байтов в памяти, не гармонируют с более сложной и более богатой системы типов C ++. C ++ поощряет гораздо более абстрактное представление пользовательских типов.
Таким образом, эти типы вещей больше не позволяют C ++ любому, кто хочет правильно его использовать, смотреть на него как на «надмножество» языка C. Эти языки требуют совершенно другого мышления, дисциплины и способа мышления для наиболее эффективного использования. ,
Я не нахожусь в лагере, который считает C ++ лучше во всех отношениях, и на самом деле большинство моих любимых сторонних библиотек по какой-то причине являются библиотеками C. Я не знаю, почему именно, но C-библиотеки имеют тенденцию быть более минималистичными по своей природе (возможно, потому, что отсутствие такой богатой системы типов делает разработчиков более сосредоточенными на предоставлении минимальной требуемой функциональности без создания большого и многоуровневого набора абстракций), хотя я часто заканчиваю тем, что просто помещаю обертки C ++ вокруг них, чтобы упростить и адаптировать их использование для моих целей, но этот минималистский характер предпочтительнее для меня даже при этом. Я действительно люблю минимализм как привлекательную особенность библиотеки для тех, кто тратит дополнительное время на поиск таких качеств, и, возможно, С склонен поощрять это,
Я предпочитаю C ++ гораздо чаще, чем нет, но на самом деле мне приходится довольно часто использовать API-интерфейсы C для самой широкой двоичной совместимости (и для FFI), хотя я часто реализую их в C ++, несмотря на использование C для заголовков. Но иногда, когда вы переходите на действительно низкоуровневый уровень, например, к распределителю памяти или очень низкоуровневой структуре данных (и я уверен, что среди тех, кто занимается встроенным программированием, есть и другие примеры), иногда бывает полезно Можно предположить, что типы и данные, с которыми вы работаете, не имеют определенных функций, таких как vtables, costructors и destructors, так что мы можем рассматривать их как биты и байты для перемешивания, копирования, освобождения, перераспределения. Для особо низкоуровневых задач иногда бывает полезно работать с гораздо более простой системой типов, которую предоставляет C,
Разъяснение
Один интересный комментарий, на который я хотел бы ответить более подробно (я считаю, что комментарии здесь настолько строги по ограничению символов):
memcpy(&f2, f1, sizeof f2);
также является «хаосом правящего адского огня» в C, если у Foo есть какие-либо собственные указатели, или еще хуже, так как вам также не хватает инструментов, чтобы справиться с этим.
Это справедливо, но все, на чем я сконцентрировался, в основном сосредоточено на системе типов C ++, а также на RAII. Одна из причин, по которой такое рентгеновское байт-копирование memcpy
или qsort
типы функций представляют меньшую практическую опасность в C, заключается в том, что уничтожение f1
и f2
выше является явным (если они даже требуют нетривиального уничтожения), тогда как когда деструкторы перемещаются в картину они становятся неявными и автоматизированными (часто с большой ценностью для разработчиков). Это даже не говоря о скрытом состоянии, таком как vptrs и т. Д., Которые такие функции могли бы разрушить прямо сейчас. Если f1
владеет указателями иf2
поверхностное копирование их в некотором временном контексте, тогда это не представляет проблемы, если мы не попытаемся явно освободить те, у кого есть указатели, во второй раз. С C ++ это то, что компилятор автоматически захочет сделать.
И это становится еще более значительным, если обычно в C: « Если у Foo есть собственные указатели», потому что ясность, требуемая при управлении ресурсами, часто делает это чем-то более сложным, чтобы пропустить, тогда как в C ++ мы можем сделать UDT более не тривиальным конструируемый / разрушаемый, просто заставляя его хранить любую переменную-член, которая не является тривиально конструируемой / разрушаемой (опять же, как правило, очень полезно, опять же, но не в том случае, если у нас возникает искушение использовать такие функции как memcpy
или realloc
)
Моя главная мысль не в том, чтобы пытаться спорить о какой-либо пользе от этой явности (я бы сказал, что если таковые имеются, они почти всегда отягощены минусами повышенной вероятности человеческой ошибки, которая сопровождает их), а просто сказать, что функции как memcpy
и memmove
и qsort
и memset
иrealloc
и т. д. нет места в языке с такими UDT, такими же богатыми по своим возможностям и возможностям, как C ++. Несмотря на то, что они существуют независимо, я думаю, что было бы не слишком оспаривать, чтобы сказать, что подавляющее, огромное большинство разработчиков C ++ было бы разумно избегать таких функций, как чума, тогда как в C это очень повседневные функции, и я Я утверждаю, что они создают меньше проблем в C по той простой причине, что его система типов намного более проста и, возможно, «тупее». Рентгенография типов C и обработка их как битов и байтов подвержена ошибкам. Делать это в C ++ возможно просто ошибочно, потому что такие функции борются с очень фундаментальными особенностями языка и тем, что он поощряет в системе типов.
Это на самом деле самое привлекательное для меня C, тем не менее, особенно с учетом того, как оно связано с совместимостью языков. Было бы намного сложнее заставить что-то вроде FFI в C # понимать полноценную систему типов и языковые особенности C ++ вплоть до конструкторов, деструкторов, исключений, виртуальных функций, перегрузки функций / методов, перегрузки операторов, всех различных типов наследование и т. д. С C это относительно тупой язык, который стал довольно стандартным для API, так что многие различные языки могут импортировать напрямую через FFI или косвенно через некоторые функции экспорта API C в желаемой форме (например, Java Native Interface ). И именно здесь у меня больше не остается выбора, кроме как использовать C, так как эта функциональная совместимость языка является практическим требованием в нашем случае (хотя часто я
Но вы знаете, я прагматик (или, по крайней мере, я стараюсь быть). Если бы C был этим самым отвратительным и отвратительным, склонным к ошибкам языком, то некоторые из моих коллег-энтузиастов C ++ утверждали, что это так (и я бы считал себя энтузиастом C ++, за исключением того, что каким-то образом это не привело к ненависти к C с моей стороны). напротив, это оказало на меня противоположное влияние, заставив меня ценить оба языка лучше в их собственных отношениях и различиях), тогда я ожидал бы, что это проявится в реальном мире в форме некоторых из самых плохих и утечек и ненадежные продукты и библиотеки пишутся на C. И я не нахожу этого. Мне нравится Linux, мне нравится Apache, Lua, zlib, я нахожу OpenGL приемлемым для своего давнего наследия против таких меняющихся требований к оборудованию, Gimp, libpng, Cairo и т. Д. По крайней мере, все, что мешает изложить язык, похоже, не создает тупиковых ситуаций, если я пишу классные библиотеки и продукты в компетентных руках, и это действительно все, что меня интересует. Поэтому я никогда не был настолько заинтересован в самых страстных. языковые войны, если не считать прагматического обращения и сказать: «Эй, есть классные вещи! Давайте узнаем, как они это сделали, и, возможно, есть классные уроки, не настолько специфичные для идиоматической природы языка, которые мы можем вернуть на любой язык, который мы используем. " :-D