Нужно ли воспринимать возможность использовать Си в программах на С++, как приятное
дополнение или относится только как к обратной совместимости?
Есть те, кто считает, что такое использование вредит пониманию, так как другой человек
может быть не знаком с некоторыми частями Си или вовсе не знать ничего, кроме С++.
Также некоторые считают это плохим стилем, потому что его использование делает код
не красивым. Или же с точки зрения убеждений, что код должен содержать конструкции
языка, на котором пишешь, а не смешивание new с malloc т.п.
Но а как же быстродействие некоторых его частей или использование удобных функций
форматирования?
Где та грань, которой нужно придерживаться?
Я описал только малую часть, так как на большее не хватает знаний. По этому было
бы прекрасно, если бы вы ответили не точно следуя этим вопросам, а опираясь на свой
опыт и мнение и этим возможно тема раскроется ещё глубже, что в дальнейшем поможет
и мне и возможно вам или кому-то другому.
UPD:
@avp, на материал во второй ссылке буду постепенно поглядывать, но скорей для справки,
чем искать повод для переезда на другой язык. Потому как в принципе я согласен со многим,
даже при том, что у меня очень мало опыта. Мне кажется, что если начинать с С++, то
это скорей будет изучение самого С++, а потом уже возможно через пару лет и программирования,
что нельзя сказать про Си, так как он действительно прост в понимании, особенно после
С++. И концентрация идет не на разбирание граблей языка или умения работать с stl/boost,
а на освоение новых алгоритмов при написании своих костылей. Такие костыли будут в
начале плохими и некрасивыми и на них будет уходить много времени в сравнении с использованием
готовых решений на C++, но их написание даст понимание как это работает или как это
выгодно модифицировать для своих нужд, а не просто использовать STL и даже может разбираться
частично как работают его внутренности, но все ровно не быть способным написать что-то
подобное на том же языке или другом.
Насчет первой ссылки, то встречал подобное и от самого автора(если не фейк) тут и
должен сказать на том этапе знакомства с С++, меня это довольно сильно расстроило,
так как неважно с какой целью началось изучения языка, но если видишь такое от его
создателя, то это не может не задеть.
Наверное я бы выбрал Си, если бы вернулся в прошлое, но сейчас буду и дальше продолжать
кодить с упором на С++ и поглядывать на С, то есть бежать наверх в низ по ступенькам.
Потому что во мне преобладает непостоянность и я просто обязан закончить с этой книгой).
Вот такие пироги.
P.S. Спасибо за ответы и приношу извинения, если развел здесь оффтоп.
Ответы
Ответ 1
На самом деле, единой точки зрения на это нету.
С одной стороны, С — очень мощное подмножество языка, и используя его, можно легко
«прострелить себе ногу», то есть, наделать глупых ошибок по незнанию.
Например, при помощи макросов можно сделать как много полезного, так и много вредного
наподобие #define strcpy(a,b) memmove(a,b,strlen(b)+2), поэтому во многих проектах
макросов стараются избегать — тем более, что при помощи гораздо более безопасных темплейтов
можно сделать многое из того, что умеют макросы (и кроме того многое другое).
Другими частыми источниками ошибок являются смешивание malloc/free и new/delete/delete[]
(по стандарту, free(new int) есть undefined behaviour), ручное управление памятью вместо
использования смарт-указателей и RAII (источник 90% проблем новичков на ХК), использование
массивов (и более продвинутое велосипедостроительство) вместо подходящих стандартных
контейнеров std::vector, std::map, std::stack (источник остальных 90% проблем новичков).
Особенно сложно даётся новичкам работа со строками (которая, нужно признать, в C организовано
не блестяще): немногие осилят правильное чтение строки не известного заранее размера
из файла!
С другой стороны, многие очень приятные лаконичные конструкции C не имеют прямого
«безопасного» C++-варианта, например, обсуждавшийся недавно sscanf.
Не стоит забывать, что C — другой язык, с другой идеологией, методологией и эстетикой,
поэтому смесь кода на двух языках выглядит ненатурально и является источником ошибок.
Например, в C приветствуются лаконичные конструкции:
void copy_str(char *p, char *q)
{
while (*q++ = *p++)
;
}
— в то время как в рамках C++ этот код слишком плотный, чересчур завязан на конкретные
типы, слишком прямо работает с памятью и и слишком надеется на непроверяемые предусловия
(априори считается, что q указывает на выделенный кусок памяти размером не менее strlen(p)
+ 1).
Для себя я установил правило: если пишется код на C++, стараться использовать идиоматичные
конструкции, чтобы тем, кто читает мой код, было легче понимать его, и чтобы уменьшить
вероятность ошибки: если выбран язык C++, а не C, надёжность более важна, чем экономия
нескольких тактов процессора. Если же в каком-то месте я вижу необходимость использовать
конструкцию из C, я пишу комментарий, обосновывающий эту необходимость.
В любом случае, присоединяясь к существующему проекту на C++, узнайте, какие там
стандарты на использование конструкций чистого C, и следуйте им.
Update (спасибо @alexlz за комментарий!)
Заметьте, что в случае ограниченности ресурсов (программирование для контроллеров,
например), использование «полноценного» C++ может быть неоправданно затратным. Это
усугубляется тем, что компиляторы для таких платформ зачастую выдают плохо оптимизированный код.
В таких случаях распространённой практикой является отказ от всего множества языковых
фич C++ и ограничение определённым подмножеством. Например, могут быть выброшены лямбды,
шаблоны, стандартные контейнеры, исключения, стандартные алгоритмы, введено ручное
управление памятью, то есть, итоговый язык будет ближе к чистому C. Но это — не от
хорошей жизни (высокоуровневые конструкции позволяют забыть о надоедливых мелочах),
и определяется опытным в разработке на данной платформе архитектором проекта.
Ответ 2
@strol, программирование само по себе достаточно сложная штука.
Язык должен помогать как можно проще и естественней выражать свои мысли. На самом
деле С++ язык сложный (а ведь создавался с целью упростить программирование на Си).
Поэтому старайтесь писать проще (однако, не проще, чем необходимо) и не слишком переживайте
по поводу смешения стилей. Конечно, некоторые вещи, которые на первый взгляд эквивалентны,
могут на самом деле оказаться несовместимыми (например, new/delete и malloc/free).
Просто о них надо знать, т.е. понимать как реализуются разные конструкции. В самом
С++ полно взаимозависимых вещей и добавление чего-то из чистого Си ситуацию не упрощает
(но и далеко не всегда усложняет).
Примерно на эту тему мне понравилось высказывание Кернигана (кстати, они работали
с Страуструпом в Bell labs) в интервью из книги "Пионеры программирования"
...Си занимает прочную позицию среди языков программирования. Он чрезвычайно выразителен,
но в то же время не слишком сложен или велик, а кроме того, написанные на нем программы
эффективны. ... С этим языком удобно работать, потому что если нужно что-то выразить,
он представляет для этого не так много разных способов. Я посмотрю на ваш код и скажу:
да, мне понятно, что он делает. Едва ли то же самое можно сказать о таких языках, как
Perl или С++. Я посмотрю на ваш код и останусь в недоумении, потому что тут есть много
способов написать одно и то же.
C++ сложен и огромен, и выразить что-либо можно многими способами. Если мы с вами
будем писать на C++, то можем прийти к весьма разным способам описания какой-нибудь
большой задачи. В Си такого не бывает. Си сохранился потому, что у него оказалось хорошее
соотношение выразительности и эффективности, и для важных приложений он остается лучшим
инструментом.
.... Бьерн голову себе сломал, пытаясь добиться максимальной совместимости с Си.
Одной из причин успеха C++ в сравнении с другими языками была хорошая совместимость
на уровне как исходного, так и объектного кода, а это означало отсутствие необходимости
полностью перестраивать работу, чтобы использовать C++ в среде Си.
.... Одним из крупнейших прегрешений считают чрезмерную близость к Си....
... Возможно, но чем дальше он отошел бы от Си, тем меньше были бы его шансы на успех.
Здесь трудно соблюсти правильную меру, и я думаю, что он очень хорошо справился со
своей задачей.
(приношу извинения за столь длинную цитату)
А некоторые люди вообще высказываются о программировании на С++ довольно кратко,
но значительно резче.
Здесь весьма обстоятельная критика.
Так что, изучайте все получше и делайте выводы сами для себя.
Ответ 3
ИМХО, основное достоинство С - возможность написания быстрых программ. А если не
быстрых, то совместимых/работающих с быстрыми. С++ - это С с немного более человеческим
лицом. Но основная идея - по-прежнему скорость. Допустим, в std::list вы не найдёте
метода sort. Хотя, конечно, есть дополна простых, но неэффективных методов типа std::find
или insert в vector... Ну что поделаешь, какую-то цену за удобство надо платить.
Это всё я к тому, что я считаю возможность смешивания С++ и С стилей одним из главных
достоинств языка. Ну лень тебе сегодня - завёл vector. А завтра оказалось,
что это всё тормозит - заменил vector на vector, память выделил один раз на
все строки, раздал и всё! Я даже память для массивов частенько выделяю через
std::string m_memory; ...
m_memory.resize(data_size);
return m_memory.c_str()
Как ни странно, ни разу сильно не огребал из-за подобных шалостей. Гораздо тяжелей
было научиться работать со строками без access violation'ов в С, когда только начинал :)
Upd: @VladD, ну естественно не на стеке! Имеется в виду член класса, который живёт
дольше, чем используется память, которую он держит. Я потому и написал m_:
class C {
private:
std::string m_memory; ...
char *GetWorkMemory(int data_zize) {
m_memory.resize(data_size);
return m_memory.c_str();
} ...