Чим C відрізняється від C ++?


21

Багато людей говорили, що C ++ - це зовсім інша мова, ніж C, але сам Б'ярн сказав, що C ++ - це мова, яка поширюється з C, отже, звідки ++походить. То чому всі продовжують говорити, що C і C ++ - це абсолютно різні мови? Чим C відрізняється від C ++, крім розширених функцій C ++?


3
Завдяки тому, як вони використовуються. Ви, звичайно, можете написати C на C ++ ... але не варто.
Ed S.

Відповіді:


18

Протягом 1980-х років, коли розробка C ++ тільки починалася, C ++ був майже належним набором C. Саме з цього все і почалося.
Однак з часом і C, і C ++ еволюціонували та відходили одна від одної, хоча сумісність між мовами завжди вважалася важливою.

Крім того, технічні відмінності між C і C ++ зробили типові ідіоми в цих мовах, і те, що вважається "хорошою практикою", розходиться ще більше.

Це є рушійним фактором, який говорить про такі речі, як "немає такої мови, як C / C ++" або "C і C ++ - це дві різні мови". Хоча можна писати програми, прийнятні як для компілятора C, так і для C ++, цей код, як правило, не є ні прикладом хорошого коду С, ні прикладом хорошого коду C ++.


Я не думаю, що перший абзац є правильним. Я вважаю, що C завжди мав неявний кастинг, void *але C ++ ніколи цього не робив. (Не подав заявку)
альтернатива

5
@mathepic: Визначте "завжди". З C ++ було взято тип пустоти * У K&R C, malloc повертався із чару *
Неманья Трифунович

19

Сам Stroustrup відповідає на це у своїх FAQ :

C ++ - це прямий нащадок C, який зберігає майже всю C як підмножину. C ++ забезпечує більш сильну перевірку типу, ніж C, і безпосередньо підтримує ширший діапазон стилів програмування, ніж C. ефективності). У тому ж сенсі ANSI C є кращим C, ніж K&R C. Крім того, C ++ підтримує абстрагування даних, об'єктно-орієнтоване програмування та загальне програмування.

Це підтримка об'єктно-орієнтованого програмування та загального програмування, які роблять C ++ "абсолютно іншим" для C. Ви можете майже записати чистий C, а потім компілювати його за допомогою компілятора C ++ (поки ви подбаєте про більш сувору перевірку типу). Але тоді ви все ще пишете C - ви не пишете C ++.

Якщо ви пишете C ++, ви використовуєте його об'єктно-орієнтовані та шаблонні функції, і це не що інше, як ви бачили в C.


"це нічого подібного до того, що ви побачили б у C." Ця фраза звучить смішно! "див. в С". С в С! Це своєрідна поезія.
Галактика

13

Простіше кажучи, те, що в C вважається ідіоматичним, напевно не є ідіоматичним в C ++.

C і C ++ на практиці дуже різні мови, через те, як люди ними користуються. C спрямований на мінімалізм, де C ++ є дуже складною мовою, що має багато кількістю функцій.

Є також деякі практичні відмінності: C можна легко викликати з майже будь-якої мови і часто визначає ABI платформи, тоді як C ++ досить важко використовувати з інших бібліотек. Більшість мов мають FFI або інтерфейс на C, навіть мови, реалізовані на C ++ (наприклад, Java).


4
Справа не лише в тому, що код C-стилю не ідіоматичний в C ++. Кодування у стилі C насправді проблематично в C ++ через відсутність безпеки винятків.
dan04

4

Крім очевидного факту, що C ++ підтримує об’єктно-орієнтоване програмування, я думаю, ви маєте тут свою відповідь: http://en.wikipedia.org/wiki/Compatibility_of_C_and_C++

Ця стаття містить приклади коду, що показують, що це нормально в C, але не в C ++. Наприклад:

int *j = malloc(sizeof(int) * 5); /* Implicit conversion from void* to int* */

Перенос програми C на C ++ часто є простим і складається здебільшого з виправлення помилок компіляції (додавання кастів, нових ключових слів тощо).


4
Перенесення такої програми не дає програмі C ++. Це дає вам C, який можна скласти на компіляторі C ++. Це не робить це C ++ (я б все-таки називав отриманий код C (навіть не C з класами)).
Мартін Йорк

@MartinYork Назвіть це погано C, зауважте, що семантика може бути різною, і я погоджуюсь. Результат очевидно, хоча C.
Дедуплікатор

2

C ++ додає C. Не тільки нові функції, але нові поняття та нові ідіоми. Навіть незважаючи на те, що C ++ та C тісно пов’язані між собою, залишається фактом, що для того, щоб ефективно писати мовою, потрібно думати у стилі цієї мови. Навіть найкращий код C не може скористатися різними сильними сторонами та ідіомами C ++, і це швидше, ніж насправді, досить поганий код C ++.


2

"Розширені функції", ви звучите так, як у C ++ вони додавали, як, різноманітні макроси чи щось таке, і все. "Розширені функції" в C ++ - це повний капітальний ремонт мови і повністю замінює найкращі практики C, оскільки нові функції C ++ настільки кращі, ніж оригінальні функції C, що оригінальні функції C повністю і абсолютно зайві у переважній більшості випадків . Припускаючи, що C ++ просто поширюється на C, це говорить про те, що сучасний бойовий танк розширює живіт для цілей ведення війни.


Чи можете ви перерахувати приклад однієї з більш корисних функцій, у яких C ++ має цю C?
Темний тамплієр

2
@DarkTemplar: Як щодо простого управління ресурсами з RAII? Або приємні загальні структури даних за допомогою шаблонів? Для початку.
DeadMG

1

Різниця полягає в тому, що в C ви мислите процедурно, а в C ++ ви думаєте об'єктно орієнтованим чином. Мови досить схожі, але підхід дуже різний.


Чи не має C також конструкцій? Хіба файли C не є окремими модулями (в основному, об'єктами)? Не впевнений, у чому полягає точна різниця між процедурною та об’єктно-орієнтованою ...
Темний тамплієр

1
Темно, ти проілюстрував мій погляд. Це не обмеження мови, яке дозволяє писати процедурно або об'єктно-орієнтованим способом, це етос чи спосіб мислення.
Мураш

0

Хоча C ++ може бути супер набором C у синтаксичному плані - тобто будь-яка конструкція програми C може бути складена компілятором C ++.

Однак ви майже ніколи не пишете програми C ++ так, як це зробили б з програмою C. Список може бути нескінченним, а може бути, хтось повинен просто зробити більше досліджень, щоб поставити його як вичерпні звіти. Однак я вкладаю кілька покажчиків, які роблять ключові відмінності.

Суть поточної публікації полягає в тому, що C ++ має такі функції, які хороші програмісти C ++ повинні використовувати як найкращі практики програмування, навіть незважаючи на те, що C еквівалент можна компілювати.

Як це робити в C ++ над C

  1. Заняття та спадкування. Це найважливіші відмінності, що дозволяє систематичну орієнтацію об'єкта, що робить вираз програмування дуже потужним. Я здогадуюсь - цей пункт не потребує кращого пояснення. Якщо ви перебуваєте на C ++ - майже завжди вам краще скористатися класами.

  2. Приватизація - класи, і навіть структури мають те, що є приватними членами. Це робить можливим інкапсуляцію класу. Еквівалент у C полягає у введенні об'єкта як void * до програми, щоб програма не мала доступу до внутрішніх змінних. Однак у C ++ ви можете мати елементи як із загальнодоступними, так і з приватними класами.

  3. Перейти за посиланням. C ++ дозволяє модифікувати на основі посилань, на які потрібні проходження покажчиків. За допомогою посилання-код посилається код дуже чистим та безпечнішим проти небезпек. Ви також передаєте вказівник у стилі C, і це працює - але якщо ви перебуваєте в C ++, ви краще, поки

  4. новий & видалити проти malloc та безкоштовно. Нові оператори () та delete () не тільки виділяють і не виділяють пам'ять, але й дозволяють виконувати код як частину destruct-er для виклику в ланцюзі. Якщо ви використовуєте C ++ - насправді BAD використовувати malloc та free.

  5. Види IO та перевантаження оператора Перевантаження оператора робить код читабельним чи інтуїтивнішим, якщо це зроблено добре. Те саме для операторів << і >>. Способом C це зробити було б використовувати покажчики функцій - але це безладно і лише для попередніх програмістів.

  6. Використовуючи "рядок". Char * від C працює скрізь. Так що C і C ++ майже однакові. Однак якщо ви перебуваєте на C ++ - завжди набагато краще (і безпечніше) використовувати класи String, що позбавить вас від небезпеки масивів над бігом, які є майже у всіх речах.

Особливості, які я все ще не захоплюю в C ++ 1. Шаблони - Хоча я не використовую важкі шаблони у багатьох кодах - це може виявитися дуже потужним для бібліотек. У С. майже немає його еквівалента. Але в звичайний день - особливо якщо ви математично не вистачаєте.

  1. Розумні покажчики - Так, вони дуже розумні! І як і більшість розумних речей - вони починають добре і згодом стають безладними! Я не дуже люблю використовувати

Те, що мені подобається в С і пропускаю в С ++

  1. Поліморфні алгоритми з використанням функціональних покажчиків. У C, коли ви запускаєте складні алгоритми, ви можете використовувати декілька функціональних покажчиків. Це робить справжній поліморфізм потужним чином. Коли ви перебуваєте на C ++, ви МОЖЕТЕ використовувати функціональні вказівники - але це погано. Вам слід використовувати лише методи - інакше будьте готові до того, що ви станете безладним. Єдиною формою поліморфізму в класах C ++ є перевантаження функцій та операторів, але це досить обмежує.

  2. Прості нитки. При створенні ниток були pthreads - це досить просто і керовано. Це стає тоді, коли вам потрібно створити теми, які повинні бути "приватними" для класів (щоб вони мали доступ до приватних членів). Існує прискорений тип фреймворків, але нічого в базовому C ++.

Діпан.


0

Певні мовні особливості, навіть якщо вони є доповненнями, можуть змінити весь спосіб, яким мова практично потрібна. Як один із прикладів розглянемо цей випадок:

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


2
memcpy(&f2, f1, sizeof f2);це також "пекельний вогонь, що панує в С", якщо Fooє якісь власні покажчики, або ще гірше, оскільки вам також не вистачає інструментів для вирішення цього питання. Тож люди, що пишуть C, не займаються подібними справами
Калет

@Caleth Дещо з того, що спрощує, що навіть при володінні покажчиками є явне звільнення власних покажчиків, тому воно ефективно перетворюється на дрібну копію в таких випадках, коли лише одне місце все ще звільняє оригінальну пам'ять, наприклад (в деяких випадках, залежно від дизайн). Тоді як із залученням деструкторів обидва Fooекземпляри тепер можуть захотіти знищити динамічний масив чи вектор, або щось для цього. Я часто знаходжу для деяких своєрідних випадків, що корисно вміти писати певні структури даних, спираючись на те, що знищення явне, а не неявне.
Енергія Дракона

@Caleth Це, мабуть, лінь з мого боку, оскільки, якщо Fooє якісь власні покажчики, ми можемо дати їй належну копію / переміщення ctor, dtor, використовувати значення семантики тощо, і мати набагато багатший і безпечніший код. Але час від часу я виявляю, що я досягаю C у тих випадках, коли я хочу узагальнити структуру даних або розподільник на однорідному рівні біт і байтів, з певними припущеннями, які я можу зробити для типів, які, як правило, у своїх вузьких випадках використання спрощуються трохи від таких припущень, я відчуваю себе трохи впевненіше висловити C.
Енергія Дракона

@Caleth У моєму випадку, іноді, є якийсь наріжний камінь архітектури, де я не можу розглядати речі як бити і байти в пам'яті для впорядкування та доступу (і зазвичай ці випадки не передбачають нічого, крім POD). Це декілька своєрідних випадків, коли я все ще віддаю перевагу C. Якщо ви уявляєте розподільник пам'яті, з цим насправді немає нічого більше, ніж біти та байти, недійсні вказівники, речі подібного роду, з усією увагою на вирівнювання та об'єднання. і роздавати біти і байти, і в тих дуже своєрідних випадках я вважаю, що C пропонує менше перешкод, ніж C ++.
Драконова енергія

Інший випадок, коли я іноді досягаю C, - це випадки, коли код може насправді стати більш узагальненим і багаторазовим для використання, будучи менш абстрактним та зосередженим на примітивах, як API void filter_image(byte* pixels, int w, int h);простий приклад на відміну від «подобається» API void filter_image(ImageInterface& img);(який би зв'язав наш код із таким інтерфейсом зображення, звуження його застосовності). У таких випадках я іноді просто реалізую такі функції в C, оскільки в C ++ мало що можна отримати, і це зменшує ймовірність того, що такий код потребуватиме майбутніх змін.
енергія Дракона
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.