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