Чем С отличается от С ++?

21

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

Джошуа Партоги
источник
3
Из-за того, как они используются. Вы, конечно, можете написать C на C ++ ... но вы не должны.
Эд С.

Ответы:

18

В 1980-х годах, когда разработка C ++ только начиналась, C ++ был почти правильным надмножеством C. Именно так все и началось.
Однако со временем и C, и C ++ развивались и отличались друг от друга, хотя совместимость между языками всегда считалась важной.

Кроме того, технические различия между C и C ++ привели к тому, что типичные идиомы в этих языках стали расходиться еще больше.

Это является движущим фактором для людей, говорящих такие вещи, как «нет такого языка, как C / C ++» или «C и C ++ - это два разных языка». Хотя можно писать программы, приемлемые как для компилятора C, так и для C ++, обычно считается, что код не является ни примером хорошего кода C, ни примером хорошего кода C ++.

Барт ван Инген Шенау
источник
Я не думаю, что первый абзац правильный. Я полагаю, что C всегда имел неявное приведение из, void *но C ++ никогда не делал. (Не понизило)
альтернатива
5
@mathepic: определить «всегда». C взял тип void * из C ++. В K & R C Маллок возвращал чар *
Неманья Трифунович
19

Сам Страуструп отвечает на это в своем FAQ :

C ++ является прямым потомком C, который сохраняет почти все C как подмножество. C ++ обеспечивает более строгую проверку типов, чем C, и напрямую поддерживает более широкий диапазон стилей программирования, чем C. C ++ является «лучшим C» в том смысле, что он поддерживает стили программирования, выполненные с использованием C, с лучшей проверкой типов и большей поддержкой нотации (без потерь эффективности). В том же смысле ANSI C лучше, чем K & R C. Кроме того, C ++ поддерживает абстракцию данных, объектно-ориентированное программирование и общее программирование.

Это поддержка объектно-ориентированного программирования и универсального программирования, которые делают 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 ++ из-за отсутствия безопасности исключений.
Ден04
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 ++, они добавили, как, variadic макросы или что-то в этом роде, и все. «Расширенные возможности» в C ++ являются полным пересмотром языка и полностью вытесняют лучшие практики C, потому что новые функции C ++ намного лучше, чем оригинальные функции C, что оригинальные функции C полностью и полностью избыточны в подавляющем большинстве случаев. , Предположение, что C ++ просто расширяет C, предполагает, что современный боевой танк расширяет нож для целей ведения войны.

DeadMG
источник
Можете ли вы перечислить пример одной из более полезных функций, которые есть в C ++, которых нет в C?
Темный тамплиер
2
@DarkTemplar: как насчет простого управления ресурсами с помощью RAII? Или хорошие общие структуры данных с использованием шаблонов? Просто для начала.
DeadMG
1

Разница в том, что в 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 и бесплатно. Операторы new () и delete () не только выделяют и освобождают память, но и позволяют коду выполняться как часть destru-er, вызываемого в цепочке. Если вы используете C ++ - на самом деле ПЛОХО использовать malloc и бесплатно.

  5. Типы ввода-вывода и перегрузка операторов Перегрузка операторов делает код читаемым или интуитивно понятным, если все сделано хорошо. То же самое для операторов << и >> io. C способ сделать это будет использовать указатели на функции - но это грязно и только для опытных программистов.

  6. Используя «строку». Char * из C работает везде. Так что C и C ++ почти одинаковы. Однако, если вы находитесь в C ++ - всегда намного лучше (и безопаснее) использовать классы String, которые избавляют вас от опасностей массивов при работе, которые есть почти во всех вещах.

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

  1. Умные указатели - да, они очень умные! И, как и большинство умных вещей, они начинаются хорошо, а потом становятся грязными! Я не совсем люблю использовать

Вещи, которые мне нравятся в C и не хватает в C ++

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

  2. Простые темы. При создании потоков были pthreads - это довольно просто и управляемо. Это происходит, когда вам нужно создать потоки, которые должны быть «приватными» для классов (чтобы они имели доступ к закрытым членам). Существует расширенный тип фреймворков, но ничего в базовом C ++ нет.

Dipan.

Дипан Мехта
источник
0

Определенные языковые функции, даже если они являются дополнениями, могут полностью изменить способ использования языка. В качестве одного примера рассмотрим этот случай:

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

Энергия Дракона
источник
2
memcpy(&f2, f1, sizeof f2);это также «хаос, правящий адским огнем» в C, если Fooесть какие-либо собственные указатели, или еще хуже, так как вам также не хватает инструментов, чтобы справиться с этим. Таким образом, люди, пишущие на С, не делают таких вещей
Калет
@Caleth Кое-что из того, что упрощает то, что даже при наличии указателей, это явное освобождение указателей-обладателей, поэтому в таких случаях оно фактически превращается в мелкую копию, например, только в одном месте по-прежнему освобождается исходная память (в некоторых случаях, в зависимости от дизайн). Принимая во внимание, что с участием деструкторов, оба Fooэкземпляра теперь могут захотеть уничтожить динамический массив, или вектор, или что-то с этой целью. Я часто нахожу в некоторых специфических случаях, что полезно иметь возможность писать определенные структуры данных, опираясь на тот факт, что уничтожение явное, а не неявное.
Энергия Дракона,
@Caleth По общему признанию, это лень с моей стороны, так как, если у Fooнего есть какие-либо собственные указатели, мы могли бы дать ему правильный ctor, dtor, семантику использования значений и т. Д., И иметь гораздо более богатый и безопасный код. Но иногда я обнаруживаю, что достигаю C в тех случаях, когда я хочу обобщить структуру данных или распределитель на однородном уровне битов и байтов, с некоторыми допущениями, которые я могу сделать для типов, которые, как правило, в их узких случаях упрощаются немного из-за таких предположений я чувствую себя более уверенно делать в C.
Dragon Energy
@Caleth В моем случае, однако, иногда есть краеугольный камень архитектуры, где я не вижу смысла рассматривать вещи как нечто большее, чем биты и байты в памяти, чтобы упорядочивать и получать к ним доступ (и, как правило, в этих случаях нет ничего, кроме POD). Это те немногие особые случаи, где я все еще предпочитаю C. Если вы представляете себе распределитель памяти, на самом деле не с чем работать, кроме битов и байтов, пустых указателей и тому подобного, с акцентом на выравнивание и объединение и раздавать биты и байты, и в этих очень специфических случаях я считаю, что C предлагает меньше препятствий, чем C ++.
Энергия Дракона,
Другой случай, когда я иногда использую C, - это случаи, когда код может стать более обобщенным и многократно используемым, будучи менее абстрактным и сфокусированным на примитивах, как API void filter_image(byte* pixels, int w, int h);в простом примере, а не как API void filter_image(ImageInterface& img);(который связывает наш код с таким интерфейсом изображений, сужение его применимости). В таких случаях я иногда просто реализую такие функции в C, поскольку в C ++ мало что можно получить, и это снижает вероятность того, что такой код потребует будущих изменений.
Энергия Дракона